From 5dcd370435ddada3ac5509c5bc1190893e8f249e Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Mon, 24 Nov 2025 15:45:27 -0500 Subject: [PATCH 01/18] Consolidate test mocks and remove unused ones Added shared config mock to reduce duplication across tests. The mock provides a flexible default config that tests can override using setMockConfig(). Removed unused mocks: - font.js and style.js: Vite handles CSS and font imports natively - src/app/__mocks__/index.js: All tests use inline mocks with test-specific data - WorkflowRunSuccess.vue: Test uses inline mock instead --- client/src/app/__mocks__/index.js | 38 ------------------ .../Run/__mocks__/WorkflowRunSuccess.vue | 3 -- client/src/composables/__mocks__/config.ts | 40 +++++++++++++++++++ client/tests/vitest/__mocks__/font.js | 1 - client/tests/vitest/__mocks__/style.js | 1 - 5 files changed, 40 insertions(+), 43 deletions(-) delete mode 100644 client/src/app/__mocks__/index.js delete mode 100644 client/src/components/Workflow/Run/__mocks__/WorkflowRunSuccess.vue create mode 100644 client/src/composables/__mocks__/config.ts delete mode 100644 client/tests/vitest/__mocks__/font.js delete mode 100644 client/tests/vitest/__mocks__/style.js diff --git a/client/src/app/__mocks__/index.js b/client/src/app/__mocks__/index.js deleted file mode 100644 index 43ce32827dd..00000000000 --- a/client/src/app/__mocks__/index.js +++ /dev/null @@ -1,38 +0,0 @@ -export function getGalaxyInstance() { - return { - config: { - allow_user_deletion: false, - allow_local_account_creation: true, - wiki_url: "https://galaxyproject.org/", - ftp_upload_site: null, - support_url: "https://galaxyproject.org/support/", - allow_user_dataset_purge: false, - allow_library_path_paste: false, - user_library_import_dir: null, - terms_url: null, - library_import_dir: null, - logo_url: null, - enable_unique_workflow_defaults: false, - }, - user: { - username: "test", - quota_percent: null, - total_disk_usage: 61815527, - nice_total_disk_usage: "59.0 MB", - email: "test@test.test", - model_class: "User", - id: "f2db41e1fa331b3e", - attributes: {}, - isAdmin: function () { - return false; - }, - }, - root: "/", - emit: { - debug: function () {}, - error: function (v) { - window.console.error(v); - }, - }, - }; -} diff --git a/client/src/components/Workflow/Run/__mocks__/WorkflowRunSuccess.vue b/client/src/components/Workflow/Run/__mocks__/WorkflowRunSuccess.vue deleted file mode 100644 index 44f9f66f018..00000000000 --- a/client/src/components/Workflow/Run/__mocks__/WorkflowRunSuccess.vue +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/client/src/composables/__mocks__/config.ts b/client/src/composables/__mocks__/config.ts new file mode 100644 index 00000000000..69c8b61a2d9 --- /dev/null +++ b/client/src/composables/__mocks__/config.ts @@ -0,0 +1,40 @@ +import { ref } from "vue"; +import { vi } from "vitest"; + +// Default mock config values that tests can override +const mockConfig = ref({ + allow_local_account_creation: true, + enable_oidc: false, + mailing_join_addr: "", + prefer_custos_login: false, + registration_warning_message: "", + server_mail_configured: false, + show_welcome_with_login: false, + terms_url: "", + welcome_url: "", +}); + +export const useConfig = vi.fn(() => ({ + config: mockConfig, + isConfigLoaded: true, +})); + +// Helper function for tests to override config values +export function setMockConfig(config: Record) { + mockConfig.value = { ...mockConfig.value, ...config }; +} + +// Helper function to reset config to defaults +export function resetMockConfig() { + mockConfig.value = { + allow_local_account_creation: true, + enable_oidc: false, + mailing_join_addr: "", + prefer_custos_login: false, + registration_warning_message: "", + server_mail_configured: false, + show_welcome_with_login: false, + terms_url: "", + welcome_url: "", + }; +} diff --git a/client/tests/vitest/__mocks__/font.js b/client/tests/vitest/__mocks__/font.js deleted file mode 100644 index ff8b4c56321..00000000000 --- a/client/tests/vitest/__mocks__/font.js +++ /dev/null @@ -1 +0,0 @@ -export default {}; diff --git a/client/tests/vitest/__mocks__/style.js b/client/tests/vitest/__mocks__/style.js deleted file mode 100644 index f053ebf7976..00000000000 --- a/client/tests/vitest/__mocks__/style.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = {}; From 9646660dc82693e116c034a2e0c833704efd198e Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Mon, 24 Nov 2025 16:00:00 -0500 Subject: [PATCH 02/18] Update tests to use shared config mock Converted Login, Register, and CitationsList tests to use the shared config mock instead of inline duplicates. Tests can now call setMockConfig() to customize config values while sharing the mock implementation. --- .../components/Citation/CitationsList.test.ts | 18 ++++----- client/src/composables/__mocks__/config.ts | 2 + .../src/entry/analysis/modules/Login.test.ts | 36 +++++++++--------- .../entry/analysis/modules/Register.test.ts | 37 +++++++++---------- 4 files changed, 45 insertions(+), 48 deletions(-) diff --git a/client/src/components/Citation/CitationsList.test.ts b/client/src/components/Citation/CitationsList.test.ts index 80823d59eff..566f5cdf83d 100644 --- a/client/src/components/Citation/CitationsList.test.ts +++ b/client/src/components/Citation/CitationsList.test.ts @@ -6,22 +6,20 @@ import flushPromises from "flush-promises"; import { beforeEach, describe, expect, it, vi } from "vitest"; import VueRouter from "vue-router"; +import { setMockConfig } from "@/composables/__mocks__/config"; + import CitationItem from "./CitationItem.vue"; import MountTarget from "./CitationsList.vue"; const localVue = getLocalVue(true); localVue.use(VueRouter); -vi.mock("@/composables/config", () => ({ - useConfig: vi.fn(() => ({ - config: { - value: { - citation_bibtex: - "@article{Galaxy2024, title={The Galaxy platform for accessible, reproducible, and collaborative data analyses: 2024 update}, author={{The Galaxy Community}}, journal={Nucleic Acids Research}, year={2024}, doi={10.1093/nar/gkae410}, url={https://doi.org/10.1093/nar/gkae410}}", - }, - }, - })), -})); +vi.mock("@/composables/config"); + +setMockConfig({ + citation_bibtex: + "@article{Galaxy2024, title={The Galaxy platform for accessible, reproducible, and collaborative data analyses: 2024 update}, author={{The Galaxy Community}}, journal={Nucleic Acids Research}, year={2024}, doi={10.1093/nar/gkae410}, url={https://doi.org/10.1093/nar/gkae410}}", +}); vi.mock("@/components/Citation/services", () => ({ getCitations: vi.fn(() => diff --git a/client/src/composables/__mocks__/config.ts b/client/src/composables/__mocks__/config.ts index 69c8b61a2d9..029ec47a5ad 100644 --- a/client/src/composables/__mocks__/config.ts +++ b/client/src/composables/__mocks__/config.ts @@ -12,6 +12,7 @@ const mockConfig = ref({ show_welcome_with_login: false, terms_url: "", welcome_url: "", + citation_bibtex: "", }); export const useConfig = vi.fn(() => ({ @@ -36,5 +37,6 @@ export function resetMockConfig() { show_welcome_with_login: false, terms_url: "", welcome_url: "", + citation_bibtex: "", }; } diff --git a/client/src/entry/analysis/modules/Login.test.ts b/client/src/entry/analysis/modules/Login.test.ts index 256dc65093b..64bfc49eb32 100644 --- a/client/src/entry/analysis/modules/Login.test.ts +++ b/client/src/entry/analysis/modules/Login.test.ts @@ -2,35 +2,33 @@ import { createTestingPinia } from "@pinia/testing"; import { getLocalVue } from "@tests/vitest/helpers"; import { shallowMount } from "@vue/test-utils"; import { setActivePinia } from "pinia"; -import { describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import { setMockConfig } from "@/composables/__mocks__/config"; import Login from "./Login.vue"; const localVue = getLocalVue(true); -const configMock = { - allow_local_account_creation: true, - enable_oidc: true, - mailing_join_addr: "mailing_join_addr", - prefer_custos_login: true, - registration_warning_message: "registration_warning_message", - server_mail_configured: true, - show_welcome_with_login: true, - terms_url: "terms_url", - welcome_url: "welcome_url", -}; - vi.mock("@/app", () => ({ getGalaxyInstance: vi.fn(() => ({ session_csrf_token: "session_csrf_token" })), })); -vi.mock("@/composables/config", () => ({ - useConfig: vi.fn(() => ({ - config: configMock, +vi.mock("@/composables/config"); - isConfigLoaded: true, - })), -})); +beforeEach(() => { + setMockConfig({ + allow_local_account_creation: true, + enable_oidc: true, + mailing_join_addr: "mailing_join_addr", + prefer_custos_login: true, + registration_warning_message: "registration_warning_message", + server_mail_configured: true, + show_welcome_with_login: true, + terms_url: "terms_url", + welcome_url: "welcome_url", + }); +}); const mockRouter = (query: object) => ({ currentRoute: { diff --git a/client/src/entry/analysis/modules/Register.test.ts b/client/src/entry/analysis/modules/Register.test.ts index b2e487f47a0..351e6ef9d3d 100644 --- a/client/src/entry/analysis/modules/Register.test.ts +++ b/client/src/entry/analysis/modules/Register.test.ts @@ -1,35 +1,34 @@ import { createTestingPinia } from "@pinia/testing"; import { getLocalVue } from "@tests/vitest/helpers"; import { shallowMount } from "@vue/test-utils"; -import { describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import { setMockConfig } from "@/composables/__mocks__/config"; import Register from "./Register.vue"; import RegisterForm from "@/components/Register/RegisterForm.vue"; const localVue = getLocalVue(true); -const configMock = { - allow_local_account_creation: true, - enable_oidc: true, - mailing_join_addr: "mailing_join_addr", - prefer_custos_login: true, - registration_warning_message: "registration_warning_message", - server_mail_configured: true, - show_welcome_with_login: true, - terms_url: "terms_url", - welcome_url: "welcome_url", -}; - vi.mock("@/app/singleton", () => ({ getGalaxyInstance: vi.fn(() => ({ session_csrf_token: "session_csrf_token" })), })); -vi.mock("@/composables/config", () => ({ - useConfig: vi.fn(() => ({ - config: configMock, - isConfigLoaded: true, - })), -})); +vi.mock("@/composables/config"); + +beforeEach(() => { + setMockConfig({ + allow_local_account_creation: true, + enable_oidc: true, + mailing_join_addr: "mailing_join_addr", + prefer_custos_login: true, + registration_warning_message: "registration_warning_message", + server_mail_configured: true, + show_welcome_with_login: true, + terms_url: "terms_url", + welcome_url: "welcome_url", + }); +}); const mockRouter = (query: object) => ({ currentRoute: { From b762b342a4f2ca73994abc8648caa080e2e56b1f Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Mon, 24 Nov 2025 17:42:55 -0500 Subject: [PATCH 03/18] Consolidate 6 more tests to use shared config mock Four tests were using empty config {} so they just use the shared mock now. Two tests both used toolbox_auto_sort so they now share that config too. --- .../DatasetInformation/DatasetError.test.ts | 7 +------ .../components/Panels/Common/ToolSection.test.js | 15 +++++++-------- client/src/components/Panels/ToolBox.test.js | 14 ++++++-------- client/src/components/Panels/ToolPanel.test.ts | 7 +------ client/src/components/Upload/UploadModal.test.js | 7 +------ client/src/composables/__mocks__/config.ts | 2 ++ 6 files changed, 18 insertions(+), 34 deletions(-) diff --git a/client/src/components/DatasetInformation/DatasetError.test.ts b/client/src/components/DatasetInformation/DatasetError.test.ts index 81d182c67f5..17ecc3fdfb4 100644 --- a/client/src/components/DatasetInformation/DatasetError.test.ts +++ b/client/src/components/DatasetInformation/DatasetError.test.ts @@ -15,12 +15,7 @@ const localVue = getLocalVue(); const DATASET_ID = "dataset_id"; -vi.mock("@/composables/config", () => ({ - useConfig: vi.fn(() => ({ - config: {}, - isConfigLoaded: true, - })), -})); +vi.mock("@/composables/config"); const { server, http } = useServerMock(); diff --git a/client/src/components/Panels/Common/ToolSection.test.js b/client/src/components/Panels/Common/ToolSection.test.js index 5cf40d1637e..c7b3431d011 100644 --- a/client/src/components/Panels/Common/ToolSection.test.js +++ b/client/src/components/Panels/Common/ToolSection.test.js @@ -3,16 +3,15 @@ import { mount } from "@vue/test-utils"; import { createPinia } from "pinia"; import { describe, expect, test, vi } from "vitest"; +import { setMockConfig } from "@/composables/__mocks__/config"; + import ToolSection from "./ToolSection.vue"; -vi.mock("@/composables/config", () => ({ - useConfig: vi.fn(() => ({ - config: { - toolbox_auto_sort: true, - }, - isConfigLoaded: true, - })), -})); +vi.mock("@/composables/config"); + +setMockConfig({ + toolbox_auto_sort: true, +}); const localVue = getLocalVue(); const pinia = createPinia(); diff --git a/client/src/components/Panels/ToolBox.test.js b/client/src/components/Panels/ToolBox.test.js index 2561c0ccc79..6b22c7c16d4 100644 --- a/client/src/components/Panels/ToolBox.test.js +++ b/client/src/components/Panels/ToolBox.test.js @@ -2,19 +2,17 @@ import axios from "axios"; import MockAdapter from "axios-mock-adapter"; import { beforeEach, describe, expect, it, vi } from "vitest"; +import { setMockConfig } from "@/composables/__mocks__/config"; import toolsList from "@/components/ToolsView/testData/toolsList"; import toolsListInPanel from "@/components/ToolsView/testData/toolsListInPanel"; import { createSortedResultPanel, filterTools } from "./utilities"; -vi.mock("@/composables/config", () => ({ - useConfig: vi.fn(() => ({ - config: { - toolbox_auto_sort: true, - }, - isConfigLoaded: true, - })), -})); +vi.mock("@/composables/config"); + +setMockConfig({ + toolbox_auto_sort: true, +}); describe("ToolBox", () => { const toolsMock = toolsList.reduce((acc, item) => { diff --git a/client/src/components/Panels/ToolPanel.test.ts b/client/src/components/Panels/ToolPanel.test.ts index 1b06dacf5ab..f118968ba00 100644 --- a/client/src/components/Panels/ToolPanel.test.ts +++ b/client/src/components/Panels/ToolPanel.test.ts @@ -35,12 +35,7 @@ const TEST_PANELS_URI = "/api/tool_panels"; const DEFAULT_VIEW_ID = "default"; const PANEL_VIEW_ERR_MSG = "Error loading panel view"; -vi.mock("@/composables/config", () => ({ - useConfig: vi.fn(() => ({ - config: {}, - isConfigLoaded: true, - })), -})); +vi.mock("@/composables/config"); vi.mock("@/composables/userLocalStorage", () => ({ useUserLocalStorage: vi.fn(() => ref(DEFAULT_VIEW_ID)), diff --git a/client/src/components/Upload/UploadModal.test.js b/client/src/components/Upload/UploadModal.test.js index dd9b0d40191..f098834d8aa 100644 --- a/client/src/components/Upload/UploadModal.test.js +++ b/client/src/components/Upload/UploadModal.test.js @@ -12,12 +12,7 @@ import { useUserStore } from "@/stores/userStore"; import UploadContainer from "./UploadContainer.vue"; import UploadModal from "./UploadModal.vue"; -vi.mock("@/composables/config", () => ({ - useConfig: vi.fn(() => ({ - config: {}, - isConfigLoaded: true, - })), -})); +vi.mock("@/composables/config"); const { server, http } = useServerMock(); diff --git a/client/src/composables/__mocks__/config.ts b/client/src/composables/__mocks__/config.ts index 029ec47a5ad..40b71684e83 100644 --- a/client/src/composables/__mocks__/config.ts +++ b/client/src/composables/__mocks__/config.ts @@ -13,6 +13,7 @@ const mockConfig = ref({ terms_url: "", welcome_url: "", citation_bibtex: "", + toolbox_auto_sort: false, }); export const useConfig = vi.fn(() => ({ @@ -38,5 +39,6 @@ export function resetMockConfig() { terms_url: "", welcome_url: "", citation_bibtex: "", + toolbox_auto_sort: false, }; } From 6af97f40b06fc38cb52fe871f5b876ca4808c7c3 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Mon, 24 Nov 2025 19:49:33 -0500 Subject: [PATCH 04/18] Switch from jsdom to happy-dom for test environment happy-dom is faster and lighter with better standards compliance. Should make tests run quicker overall. --- client/package.json | 1 + client/tests/vitest/setup.ts | 2 +- client/vitest.config.mts | 4 ++-- client/yarn.lock | 31 +++++++++++++++++++++++++++++++ 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/client/package.json b/client/package.json index 207662d93ce..70f6900b4ca 100644 --- a/client/package.json +++ b/client/package.json @@ -196,6 +196,7 @@ "fork-ts-checker-webpack-plugin": "^9.0.2", "fs-extra": "^11.2.0", "gulp": "^5.0.0", + "happy-dom": "^20.0.10", "ignore-loader": "^0.1.2", "imports-loader": "^5.0.0", "jsdom": "^27.2.0", diff --git a/client/tests/vitest/setup.ts b/client/tests/vitest/setup.ts index 0c3f457d92a..b2dc7114bcc 100644 --- a/client/tests/vitest/setup.ts +++ b/client/tests/vitest/setup.ts @@ -31,7 +31,7 @@ vi.doMock("vue", () => ({ ...Vue, })); -// Mock window.scrollIntoView (not available in jsdom) +// Mock window.scrollIntoView (not available in test environment) Object.defineProperty(global, "scrollIntoView", { writable: true, configurable: true, diff --git a/client/vitest.config.mts b/client/vitest.config.mts index 82e956d1d89..3d5945eda9a 100644 --- a/client/vitest.config.mts +++ b/client/vitest.config.mts @@ -25,10 +25,10 @@ export default defineConfig({ plugins: [vue(), i18nPlugin(), yamlPlugin()], test: { globals: false, - environment: "jsdom", + environment: "happy-dom", setupFiles: ["./tests/vitest/setup.ts"], environmentOptions: { - jsdom: { + happyDOM: { url: "http://localhost/", }, }, diff --git a/client/yarn.lock b/client/yarn.lock index e4ce259bbb4..59d2aee7d78 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -2824,6 +2824,13 @@ resolved "https://registry.npmjs.org/@types/node/-/node-18.6.5.tgz" integrity sha512-Xjt5ZGUa5WusGZJ4WJPbOT8QOqp6nDynVFRKcUt32bOgvXEoc6o085WNkYTMO7ifAj2isEfQQ2cseE+wT6jsRw== +"@types/node@^20.0.0": + version "20.19.25" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.19.25.tgz#467da94a2fd966b57cc39c357247d68047611190" + integrity sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ== + dependencies: + undici-types "~6.21.0" + "@types/node@^22.0.0": version "22.0.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-22.0.0.tgz#04862a2a71e62264426083abe1e27e87cac05a30" @@ -2940,6 +2947,11 @@ resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.18.tgz#74bd1c8fd3a2058cb6fc76b188fcded50a83d866" integrity sha512-v/ZHEj9xh82usl8LMR3GarzFY1IrbXJw5L4QfQhokjRV91q+SelFqxQWSep1ucXEZ22+dSTwLFkXeur25sPIbw== +"@types/whatwg-mimetype@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz#e5e06dcd3e92d4e622ef0129637707d66c28d6a4" + integrity sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA== + "@types/wrap-ansi@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz#18b97a972f94f60a679fd5c796d96421b9abb9fd" @@ -6716,6 +6728,15 @@ handsontable@^4.0.0: numbro "^2.0.6" pikaday "1.5.1" +happy-dom@^20.0.10: + version "20.0.10" + resolved "https://registry.yarnpkg.com/happy-dom/-/happy-dom-20.0.10.tgz#01fb5f09426420994e47e966598e272102678dd2" + integrity sha512-6umCCHcjQrhP5oXhrHQQvLB0bwb1UzHAHdsXy+FjtKoYjUhmNZsQL8NivwM1vDvNEChJabVrUYxUnp/ZdYmy2g== + dependencies: + "@types/node" "^20.0.0" + "@types/whatwg-mimetype" "^3.0.2" + whatwg-mimetype "^3.0.0" + has-bigints@^1.0.1, has-bigints@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" @@ -10930,6 +10951,11 @@ undici-types@~6.11.1: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.11.1.tgz#432ea6e8efd54a48569705a699e62d8f4981b197" integrity sha512-mIDEX2ek50x0OlRgxryxsenE5XaQD4on5U2inY7RApK3SOJpofyw7uW2AyfMKkhAxXIceo2DeWGVGwyvng1GNQ== +undici-types@~6.21.0: + version "6.21.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" + integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== + unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz" @@ -11924,6 +11950,11 @@ whatwg-encoding@^3.1.1: dependencies: iconv-lite "0.6.3" +whatwg-mimetype@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" + integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== + whatwg-mimetype@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" From 944eeaee39ef99659f569ad3a77e602f55bfa60d Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Mon, 24 Nov 2025 22:19:59 -0500 Subject: [PATCH 05/18] Fix malformed CSS selectors in tests Missing closing bracket in attribute selectors that jsdom was lenient about. --- client/src/components/ActivityBar/ActivitySettings.test.js | 2 +- client/src/components/Form/Elements/FormData/FormData.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/components/ActivityBar/ActivitySettings.test.js b/client/src/components/ActivityBar/ActivitySettings.test.js index 2aa498da26e..728d31efe4f 100644 --- a/client/src/components/ActivityBar/ActivitySettings.test.js +++ b/client/src/components/ActivityBar/ActivitySettings.test.js @@ -78,7 +78,7 @@ describe("ActivitySettings", () => { expect(items.length).toBe(1); const checkbox = items.at(0).find("[data-title='Hide in Activity Bar']"); expect(checkbox.exists()).toBeTruthy(); - const icon = wrapper.find("[icon='activity-test-icon'"); + const icon = wrapper.find("[icon='activity-test-icon']"); expect(icon.exists()).toBeTruthy(); expect(activityStore.getAll()[0].visible).toBeTruthy(); checkbox.trigger("click"); diff --git a/client/src/components/Form/Elements/FormData/FormData.test.ts b/client/src/components/Form/Elements/FormData/FormData.test.ts index 4b3b228803b..c786b17e819 100644 --- a/client/src/components/Form/Elements/FormData/FormData.test.ts +++ b/client/src/components/Form/Elements/FormData/FormData.test.ts @@ -442,7 +442,7 @@ describe("FormData", () => { }); const noCheckLinked = wrapper.find("input[type='checkbox']"); expect(noCheckLinked.exists()).toBeFalsy(); - await wrapper.find("[title='Multiple datasets'").trigger("click"); + await wrapper.find("[title='Multiple datasets']").trigger("click"); expect(wrapper.emitted()!.input![1]![0]).toEqual(null); const elements_0 = wrapper.findAll(SELECT_OPTIONS); expect(elements_0.length).toEqual(6); @@ -508,7 +508,7 @@ describe("FormData", () => { const selectedValues = wrapper.findAll(SELECTED_VALUE); expect(selectedValues.length).toBe(1); expect(selectedValues.at(0).text()).toBe("5: hdcaName5"); - await wrapper.find("[title='Multiple datasets'").trigger("click"); + await wrapper.find("[title='Multiple datasets']").trigger("click"); expect(options.at(0).classes()).toContain("active"); expect(wrapper.emitted()!.input![2]![0]).toEqual(null); }); From 82f0d37185a1b30c23fb653c9ce812b6fed799d8 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Mon, 24 Nov 2025 22:24:55 -0500 Subject: [PATCH 06/18] Add ResizeObserver mock for happy-dom compatibility --- client/tests/vitest/setup.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/client/tests/vitest/setup.ts b/client/tests/vitest/setup.ts index b2dc7114bcc..6d757c6b7c2 100644 --- a/client/tests/vitest/setup.ts +++ b/client/tests/vitest/setup.ts @@ -67,6 +67,19 @@ Object.defineProperty(window, "IntersectionObserver", { value: MockIntersectionObserver, }); +// Mock ResizeObserver (not fully implemented in happy-dom) +class MockResizeObserver { + observe = vi.fn(); + disconnect = vi.fn(); + unobserve = vi.fn(); +} + +Object.defineProperty(window, "ResizeObserver", { + writable: true, + configurable: true, + value: MockResizeObserver, +}); + // Mock HTMLDialogElement methods if not available if (typeof HTMLDialogElement !== "undefined") { HTMLDialogElement.prototype.showModal = HTMLDialogElement.prototype.showModal || vi.fn(); From 85cbbef6570ac403d993e20a539138d959ca9907 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Tue, 25 Nov 2025 06:24:53 -0500 Subject: [PATCH 07/18] Spoof jsdom in user agent for BootstrapVue compatibility --- client/tests/vitest/setup.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/client/tests/vitest/setup.ts b/client/tests/vitest/setup.ts index 6d757c6b7c2..44feb712f0c 100644 --- a/client/tests/vitest/setup.ts +++ b/client/tests/vitest/setup.ts @@ -38,6 +38,15 @@ Object.defineProperty(global, "scrollIntoView", { value: vi.fn(), }); +// Spoof user agent to include "jsdom" so BootstrapVue skips its +// "Multiple instances of Vue" warning check (it only checks in non-jsdom envs) +if (typeof window !== "undefined" && window.navigator) { + Object.defineProperty(window.navigator, "userAgent", { + value: window.navigator.userAgent + " jsdom", + configurable: true, + }); +} + // Replace setImmediate with setTimeout to fix certain tests if (!global.setImmediate) { Object.defineProperty(global, "setImmediate", { From 48990e7b40afebe4f3360d62f3c0d317b1296958 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Tue, 25 Nov 2025 06:27:24 -0500 Subject: [PATCH 08/18] Fix test assertions for happy-dom compatibility --- client/src/components/Form/Elements/FormText.test.js | 5 +++-- .../src/components/Markdown/Sections/MarkdownGalaxy.test.js | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/client/src/components/Form/Elements/FormText.test.js b/client/src/components/Form/Elements/FormText.test.js index 603c9a2892f..d39f493cf11 100644 --- a/client/src/components/Form/Elements/FormText.test.js +++ b/client/src/components/Form/Elements/FormText.test.js @@ -47,9 +47,10 @@ describe("FormText", () => { it("should be able to render border and text color from props", async () => { const wrapper = await mountFormText({}); const el = wrapper.find("input"); - expect(el.element.style).toMatchObject({}); + expect(el.element.style.color).toBeFalsy(); await wrapper.setProps({ color: "green" }); - expect(el.element.style).toMatchObject({ color: "green", "border-color": "green" }); + expect(el.element.style.color).toBe("green"); + expect(el.element.style.borderColor).toBe("green"); await wrapper.setProps({ cls: "my-custom-class" }); expect(el.element.classList).toContain("my-custom-class"); }); diff --git a/client/src/components/Markdown/Sections/MarkdownGalaxy.test.js b/client/src/components/Markdown/Sections/MarkdownGalaxy.test.js index 00ca92ce59f..a0ed7472dc3 100644 --- a/client/src/components/Markdown/Sections/MarkdownGalaxy.test.js +++ b/client/src/components/Markdown/Sections/MarkdownGalaxy.test.js @@ -91,7 +91,8 @@ describe("MarkdownContainer", () => { const container = wrapper.find(".collapse"); expect(container.attributes("style")).toBe("display: none;"); await link.trigger("click"); - expect(container.attributes("style")).toBe(""); + // After click, style attribute is removed (undefined in happy-dom, "" in jsdom) + expect(container.attributes("style")).toBeFalsy(); }); it("Renders time stamp", async () => { From b371c3193b254add6aae758e425b8d39e2376286 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Tue, 25 Nov 2025 06:28:10 -0500 Subject: [PATCH 09/18] Fix import ordering --- client/src/components/Panels/ToolBox.test.js | 2 +- client/src/composables/__mocks__/config.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/components/Panels/ToolBox.test.js b/client/src/components/Panels/ToolBox.test.js index 6b22c7c16d4..f88e1b37727 100644 --- a/client/src/components/Panels/ToolBox.test.js +++ b/client/src/components/Panels/ToolBox.test.js @@ -2,9 +2,9 @@ import axios from "axios"; import MockAdapter from "axios-mock-adapter"; import { beforeEach, describe, expect, it, vi } from "vitest"; -import { setMockConfig } from "@/composables/__mocks__/config"; import toolsList from "@/components/ToolsView/testData/toolsList"; import toolsListInPanel from "@/components/ToolsView/testData/toolsListInPanel"; +import { setMockConfig } from "@/composables/__mocks__/config"; import { createSortedResultPanel, filterTools } from "./utilities"; diff --git a/client/src/composables/__mocks__/config.ts b/client/src/composables/__mocks__/config.ts index 40b71684e83..5d0bdce982b 100644 --- a/client/src/composables/__mocks__/config.ts +++ b/client/src/composables/__mocks__/config.ts @@ -1,5 +1,5 @@ -import { ref } from "vue"; import { vi } from "vitest"; +import { ref } from "vue"; // Default mock config values that tests can override const mockConfig = ref({ From aca9f4907c8d34470bb8b9175576484d9c81a4ee Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Tue, 25 Nov 2025 06:43:57 -0500 Subject: [PATCH 10/18] Mock workflow counts API in WorkflowList test --- client/src/components/Workflow/List/WorkflowList.test.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/client/src/components/Workflow/List/WorkflowList.test.ts b/client/src/components/Workflow/List/WorkflowList.test.ts index 6b5dace706a..50d55c851ce 100644 --- a/client/src/components/Workflow/List/WorkflowList.test.ts +++ b/client/src/components/Workflow/List/WorkflowList.test.ts @@ -7,6 +7,7 @@ import { setActivePinia } from "pinia"; import { beforeEach, describe, expect, it, vi } from "vitest"; import VueRouter from "vue-router"; +import { useServerMock } from "@/api/client/__mocks__"; import { loadWorkflows } from "@/api/workflows"; import { useUserStore } from "@/stores/userStore"; @@ -14,6 +15,8 @@ import { generateRandomWorkflowList } from "../testUtils"; import WorkflowList from "./WorkflowList.vue"; +const { server, http } = useServerMock(); + vi.mock("@/api/workflows", () => ({ loadWorkflows: vi.fn(), })); @@ -55,6 +58,12 @@ describe("WorkflowList", () => { beforeEach(() => { suppressBootstrapVueWarnings(); vi.clearAllMocks(); + // Mock the workflow counts endpoint used by workflow card badges + server.use( + http.get("/api/workflows/{workflow_id}/counts", ({ response }) => { + return response(200).json({}); + }), + ); }); it("render empty workflow list", async () => { From 04167177efe1233b5764f96eee6169d8cbbb093e Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Tue, 25 Nov 2025 06:59:22 -0500 Subject: [PATCH 11/18] Suppress KaTeX quirks mode warning and add doctype --- client/tests/vitest/setup.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/client/tests/vitest/setup.ts b/client/tests/vitest/setup.ts index 44feb712f0c..7987d637915 100644 --- a/client/tests/vitest/setup.ts +++ b/client/tests/vitest/setup.ts @@ -47,6 +47,16 @@ if (typeof window !== "undefined" && window.navigator) { }); } +// Ensure document has proper doctype to avoid quirks mode (needed for KaTeX) +if (typeof document !== "undefined" && !document.doctype) { + const doctype = document.implementation.createDocumentType("html", "", ""); + if (document.childNodes.length > 0) { + document.insertBefore(doctype, document.childNodes[0]); + } else { + document.appendChild(doctype); + } +} + // Replace setImmediate with setTimeout to fix certain tests if (!global.setImmediate) { Object.defineProperty(global, "setImmediate", { @@ -126,6 +136,13 @@ const failOnConsole = (await import("vitest-fail-on-console")).default; failOnConsole({ shouldFailOnError: true, shouldFailOnWarn: true, + silenceMessage: (message: string) => { + // KaTeX warns about quirks mode at module load time before we can set doctype + if (message.includes("KaTeX doesn't work in quirks mode")) { + return true; + } + return false; + }, }); // Import and setup MSW if needed From 82fe10901d27eed85f0dde77e90595974b06f13e Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Tue, 25 Nov 2025 08:08:13 -0500 Subject: [PATCH 12/18] Remove jsdom packages, now using happy-dom --- client/package.json | 2 - client/yarn.lock | 281 +------------------------------------------- 2 files changed, 4 insertions(+), 279 deletions(-) diff --git a/client/package.json b/client/package.json index 70f6900b4ca..5ece4f33964 100644 --- a/client/package.json +++ b/client/package.json @@ -199,8 +199,6 @@ "happy-dom": "^20.0.10", "ignore-loader": "^0.1.2", "imports-loader": "^5.0.0", - "jsdom": "^27.2.0", - "jsdom-worker": "^0.3.0", "json-loader": "^0.5.7", "mini-css-extract-plugin": "^2.9.2", "msw": "^2.3.4", diff --git a/client/yarn.lock b/client/yarn.lock index 59d2aee7d78..bbfb6692c6a 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -7,11 +7,6 @@ resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== -"@acemir/cssom@^0.9.23": - version "0.9.23" - resolved "https://registry.yarnpkg.com/@acemir/cssom/-/cssom-0.9.23.tgz#9930458ccace533c597e1cd90c200edc336ed80a" - integrity sha512-2kJ1HxBKzPLbmhZpxBiTZggjtgCwKg1ma5RHShxvd6zgqhDEdEkzpiwe7jLkI2p2BrZvFCXIihdoMkl1H39VnA== - "@adobe/css-tools@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.4.0.tgz#728c484f4e10df03d5a3acd0d8adcbbebff8ad63" @@ -25,33 +20,6 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@asamuzakjp/css-color@^4.0.3": - version "4.1.0" - resolved "https://registry.yarnpkg.com/@asamuzakjp/css-color/-/css-color-4.1.0.tgz#4c8c6f48ed2e5c1ad9cc1aa23c80d665e56dd458" - integrity sha512-9xiBAtLn4aNsa4mDnpovJvBn72tNEIACyvlqaNJ+ADemR+yeMJWnBudOi2qGDviJa7SwcDOU/TRh5dnET7qk0w== - dependencies: - "@csstools/css-calc" "^2.1.4" - "@csstools/css-color-parser" "^3.1.0" - "@csstools/css-parser-algorithms" "^3.0.5" - "@csstools/css-tokenizer" "^3.0.4" - lru-cache "^11.2.2" - -"@asamuzakjp/dom-selector@^6.7.4": - version "6.7.4" - resolved "https://registry.yarnpkg.com/@asamuzakjp/dom-selector/-/dom-selector-6.7.4.tgz#1b7cafe7793e399f9291de2689fdd2efc01838dd" - integrity sha512-buQDjkm+wDPXd6c13534URWZqbz0RP5PAhXZ+LIoa5LgwInT9HVJvGIJivg75vi8I13CxDGdTnz+aY5YUJlIAA== - dependencies: - "@asamuzakjp/nwsapi" "^2.3.9" - bidi-js "^1.0.3" - css-tree "^3.1.0" - is-potential-custom-element-name "^1.0.1" - lru-cache "^11.2.2" - -"@asamuzakjp/nwsapi@^2.3.9": - version "2.3.9" - resolved "https://registry.yarnpkg.com/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz#ad5549322dfe9d153d4b4dd6f7ff2ae234b06e24" - integrity sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q== - "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.18.6": version "7.18.6" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz" @@ -1296,39 +1264,6 @@ "@citation-js/date" "^0.5.0" citeproc "^2.4.6" -"@csstools/color-helpers@^5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@csstools/color-helpers/-/color-helpers-5.1.0.tgz#106c54c808cabfd1ab4c602d8505ee584c2996ef" - integrity sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA== - -"@csstools/css-calc@^2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@csstools/css-calc/-/css-calc-2.1.4.tgz#8473f63e2fcd6e459838dd412401d5948f224c65" - integrity sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ== - -"@csstools/css-color-parser@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz#4e386af3a99dd36c46fef013cfe4c1c341eed6f0" - integrity sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA== - dependencies: - "@csstools/color-helpers" "^5.1.0" - "@csstools/css-calc" "^2.1.4" - -"@csstools/css-parser-algorithms@^3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz#5755370a9a29abaec5515b43c8b3f2cf9c2e3076" - integrity sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ== - -"@csstools/css-syntax-patches-for-csstree@^1.0.14": - version "1.0.17" - resolved "https://registry.yarnpkg.com/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.17.tgz#307685b5204cdf9fc029a5850a94edd3a8dc100f" - integrity sha512-LCC++2h8pLUSPY+EsZmrrJ1EOUu+5iClpEiDhhdw3zRJpPbABML/N5lmRuBHjxtKm9VnRcsUzioyD0sekFMF0A== - -"@csstools/css-tokenizer@^3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz#333fedabc3fd1a8e5d0100013731cf19e6a8c5d3" - integrity sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw== - "@discoveryjs/json-ext@^0.6.1": version "0.6.3" resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz#f13c7c205915eb91ae54c557f5e92bddd8be0e83" @@ -3624,7 +3559,7 @@ ag-grid-vue@^30: resolved "https://registry.yarnpkg.com/ag-grid-vue/-/ag-grid-vue-30.2.1.tgz#56395cf053ca2df70107123cf589618ddc3f84b3" integrity sha512-dnyltXrVUPk0ALQ1PfwnjBtYk/GDOjRjyOMy8LVAiWxVQA6Tmnb/dTnS1yjym1uggu+dDKof2zgPxVKayIHtWg== -agent-base@^7.1.0, agent-base@^7.1.2: +agent-base@^7.1.2: version "7.1.4" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.4.tgz#e3cd76d4c548ee895d3c3fd8dc1f6c5b9032e7a8" integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ== @@ -4048,13 +3983,6 @@ bent@~7.3.6: caseless "~0.12.0" is-stream "^2.0.0" -bidi-js@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/bidi-js/-/bidi-js-1.0.3.tgz#6f8bcf3c877c4d9220ddf49b9bb6930c88f877d2" - integrity sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw== - dependencies: - require-from-string "^2.0.2" - big.js@^5.2.2: version "5.2.2" resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz" @@ -4757,14 +4685,6 @@ css-tree@^2.2.1: mdn-data "2.0.30" source-map-js "^1.0.1" -css-tree@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-3.1.0.tgz#7aabc035f4e66b5c86f54570d55e05b1346eb0fd" - integrity sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w== - dependencies: - mdn-data "2.12.2" - source-map-js "^1.0.1" - css-tree@~2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.2.1.tgz#36115d382d60afd271e377f9c5f67d02bd48c032" @@ -4843,15 +4763,6 @@ csso@^5.0.5: dependencies: css-tree "~2.2.0" -cssstyle@^5.3.3: - version "5.3.3" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-5.3.3.tgz#977f3868f379c17d619e9672839f9b5bb3db9861" - integrity sha512-OytmFH+13/QXONJcC75QNdMtKpceNk3u8ThBjyyYjkEcy/ekBwR1mMAuNvi3gdBPW3N5TlCzQ0WZw8H0lN/bDw== - dependencies: - "@asamuzakjp/css-color" "^4.0.3" - "@csstools/css-syntax-patches-for-csstree" "^1.0.14" - css-tree "^3.1.0" - csstype@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz" @@ -5154,14 +5065,6 @@ d3@^7.8.5: resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.17.tgz#bc46748004378b21a360c9fc7cf5231790762fb8" integrity sha512-yFk/2idb8OHPKkbAL8QaOaqENNoMhIaSHZerk3oQsECwkObkCpJyjYwCe+OHiq6UEdhe1m8ZGARRRO3ljFjlKg== -data-urls@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-6.0.0.tgz#95a7943c8ac14c1d563b771f2621cc50e8ec7744" - integrity sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA== - dependencies: - whatwg-mimetype "^4.0.0" - whatwg-url "^15.0.0" - date-fns@^2.30.0: version "2.30.0" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" @@ -5202,11 +5105,6 @@ debug@^4.1.0, debug@^4.1.1, debug@^4.3.2: dependencies: ms "2.1.2" -decimal.js@^10.6.0: - version "10.6.0" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.6.0.tgz#e649a43e3ab953a72192ff5983865e509f37ed9a" - integrity sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg== - decode-uri-component@^0.2.1: version "0.2.2" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" @@ -5523,11 +5421,6 @@ entities@^4.2.0, entities@^4.5.0: resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== -entities@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/entities/-/entities-6.0.1.tgz#c28c34a43379ca7f61d074130b2f5f7020a30694" - integrity sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g== - entities@~3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" @@ -6844,13 +6737,6 @@ hsluv@^1.0.1: resolved "https://registry.yarnpkg.com/hsluv/-/hsluv-1.0.1.tgz#8f957afd3f928d2de7f7569db7687f811bb2baed" integrity sha512-zCaFTiDqBLQjCCFBu0qg7z9ASYPd+Bxx2GDCVZJsnehjK80S+jByqhuFz0pCd2Aw3FSKr18AWbRlwnKR0YdizQ== -html-encoding-sniffer@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz#696df529a7cfd82446369dc5193e590a3735b448" - integrity sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ== - dependencies: - whatwg-encoding "^3.1.1" - html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz" @@ -6895,14 +6781,6 @@ http-parser-js@>=0.5.1: resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz" integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== -http-proxy-agent@^7.0.2: - version "7.0.2" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" - integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== - dependencies: - agent-base "^7.1.0" - debug "^4.3.4" - http-proxy-middleware@^2.0.7: version "2.0.9" resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz#e9e63d68afaa4eee3d147f39149ab84c0c2815ef" @@ -6923,7 +6801,7 @@ http-proxy@^1.18.1: follow-redirects "^1.0.0" requires-port "^1.0.0" -https-proxy-agent@^7.0.4, https-proxy-agent@^7.0.5, https-proxy-agent@^7.0.6: +https-proxy-agent@^7.0.4, https-proxy-agent@^7.0.5: version "7.0.6" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== @@ -6943,7 +6821,7 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@0.6, iconv-lite@0.6.3, iconv-lite@^0.6.3: +iconv-lite@0.6, iconv-lite@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -7291,11 +7169,6 @@ is-plain-object@^5.0.0: resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== -is-potential-custom-element-name@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" - integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== - is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" @@ -7570,40 +7443,6 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -jsdom-worker@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/jsdom-worker/-/jsdom-worker-0.3.0.tgz#aff32ec089d17f56a5a344a426b6fb2f56e7de54" - integrity sha512-nlPmN0i93+e6vxzov8xqLMR+MBs/TAYeSviehivzqovHH0AgooVx9pQ/otrygASppPvdR+V9Jqx5SMe8+FcADg== - dependencies: - mitt "^3.0.0" - uuid-v4 "^0.1.0" - -jsdom@^27.2.0: - version "27.2.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-27.2.0.tgz#499a41eef477c3632f44009e095cb8e418fdd714" - integrity sha512-454TI39PeRDW1LgpyLPyURtB4Zx1tklSr6+OFOipsxGUH1WMTvk6C65JQdrj455+DP2uJ1+veBEHTGFKWVLFoA== - dependencies: - "@acemir/cssom" "^0.9.23" - "@asamuzakjp/dom-selector" "^6.7.4" - cssstyle "^5.3.3" - data-urls "^6.0.0" - decimal.js "^10.6.0" - html-encoding-sniffer "^4.0.0" - http-proxy-agent "^7.0.2" - https-proxy-agent "^7.0.6" - is-potential-custom-element-name "^1.0.1" - parse5 "^8.0.0" - saxes "^6.0.0" - symbol-tree "^3.2.4" - tough-cookie "^6.0.0" - w3c-xmlserializer "^5.0.0" - webidl-conversions "^8.0.0" - whatwg-encoding "^3.1.1" - whatwg-mimetype "^4.0.0" - whatwg-url "^15.1.0" - ws "^8.18.3" - xml-name-validator "^5.0.0" - jsesc@^2.5.1: version "2.5.2" resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" @@ -7958,11 +7797,6 @@ lodash@^4.17.15, lodash@^4.17.21: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -lru-cache@^11.2.2: - version "11.2.2" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.2.2.tgz#40fd37edffcfae4b2940379c0722dc6eeaa75f24" - integrity sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg== - lru-cache@^4.1.2, lru-cache@^4.1.5: version "4.1.5" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz" @@ -8061,11 +7895,6 @@ mdn-data@2.0.30: resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc" integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA== -mdn-data@2.12.2: - version "2.12.2" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.12.2.tgz#9ae6c41a9e65adf61318b32bff7b64fbfb13f8cf" - integrity sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA== - mdurl@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz" @@ -8206,11 +8035,6 @@ minimist@^1.2.0, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== -mitt@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.0.tgz#69ef9bd5c80ff6f57473e8d89326d01c414be0bd" - integrity sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ== - moment@2.20.1: version "2.20.1" resolved "https://registry.npmjs.org/moment/-/moment-2.20.1.tgz" @@ -8769,13 +8593,6 @@ parse-passwd@^1.0.0: resolved "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz" integrity sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q== -parse5@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-8.0.0.tgz#aceb267f6b15f9b6e6ba9e35bfdd481fc2167b12" - integrity sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA== - dependencies: - entities "^6.0.0" - parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" @@ -9308,7 +9125,7 @@ psl@^1.1.33: resolved "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz" integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== -punycode@^2.1.0, punycode@^2.3.1: +punycode@^2.1.0: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== @@ -9849,13 +9666,6 @@ sax@^1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -saxes@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" - integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== - dependencies: - xmlchars "^2.2.0" - schema-to-ts@^0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/schema-to-ts/-/schema-to-ts-0.0.2.tgz#4b03f550279872f85de1ecdfa0384e413f197dde" @@ -10495,11 +10305,6 @@ svgo@^3.0.2: csso "^5.0.5" picocolors "^1.0.0" -symbol-tree@^3.2.4: - version "3.2.4" - resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" - integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== - sync-fetch@^0.4.1: version "0.4.2" resolved "https://registry.yarnpkg.com/sync-fetch/-/sync-fetch-0.4.2.tgz#40ec3398f7193198c44fd0753b7b77a27185a117" @@ -10631,18 +10436,6 @@ tinyrainbow@^3.0.3: resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-3.0.3.tgz#984a5b1c1b25854a9b6bccbe77964d0593d1ea42" integrity sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q== -tldts-core@^7.0.18: - version "7.0.18" - resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-7.0.18.tgz#78edfd38e8c35e20fb4d2cde63c759139e169d31" - integrity sha512-jqJC13oP4FFAahv4JT/0WTDrCF9Okv7lpKtOZUGPLiAnNbACcSg8Y8T+Z9xthOmRBqi/Sob4yi0TE0miRCvF7Q== - -tldts@^7.0.5: - version "7.0.18" - resolved "https://registry.yarnpkg.com/tldts/-/tldts-7.0.18.tgz#72cac7a2bdb6bba78f8a09fdf7ef84843b09aa94" - integrity sha512-lCcgTAgMxQ1JKOWrVGo6E69Ukbnx4Gc1wiYLRf6J5NN4HRYJtCby1rPF8rkQ4a6qqoFBK5dvjJ1zJ0F7VfDSvw== - dependencies: - tldts-core "^7.0.18" - to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" @@ -10696,20 +10489,6 @@ tough-cookie@^4.1.4: universalify "^0.2.0" url-parse "^1.5.3" -tough-cookie@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-6.0.0.tgz#11e418b7864a2c0d874702bc8ce0f011261940e5" - integrity sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w== - dependencies: - tldts "^7.0.5" - -tr46@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-6.0.0.tgz#f5a1ae546a0adb32a277a2278d0d17fa2f9093e6" - integrity sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw== - dependencies: - punycode "^2.3.1" - tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" @@ -11063,11 +10842,6 @@ utrie@^1.0.2: dependencies: base64-arraybuffer "^1.0.2" -uuid-v4@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/uuid-v4/-/uuid-v4-0.1.0.tgz#62d7b310406f6cecfea1528c69f1e8e0bcec5a3a" - integrity sha512-m11RYDtowtAIihBXMoGajOEKpAXrKbpKlpmxqyztMYQNGSY5nZAZ/oYch/w2HNS1RMA4WLGcZvuD8/wFMuCEzA== - uuid@^8.3.2: version "8.3.2" resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" @@ -11759,13 +11533,6 @@ vuedraggable@^2.24.3: dependencies: sortablejs "1.10.2" -w3c-xmlserializer@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c" - integrity sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA== - dependencies: - xml-name-validator "^5.0.0" - watchpack@^2.4.1: version "2.4.2" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.2.tgz#2feeaed67412e7c33184e5a79ca738fbd38564da" @@ -11786,11 +11553,6 @@ webidl-conversions@^3.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== -webidl-conversions@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-8.0.0.tgz#821c92aa4f88d88a31264d887e244cb9655690c6" - integrity sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA== - webpack-cli@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-6.0.1.tgz#a1ce25da5ba077151afd73adfa12e208e5089207" @@ -11943,31 +11705,11 @@ websocket-extensions@>=0.1.1: resolved "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz" integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== -whatwg-encoding@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5" - integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ== - dependencies: - iconv-lite "0.6.3" - whatwg-mimetype@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== -whatwg-mimetype@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" - integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== - -whatwg-url@^15.0.0, whatwg-url@^15.1.0: - version "15.1.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-15.1.0.tgz#5c433439b9a5789eeb3806bbd0da89a8bd40b8d7" - integrity sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g== - dependencies: - tr46 "^6.0.0" - webidl-conversions "^8.0.0" - whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -12088,11 +11830,6 @@ ws@^8.18.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.2.tgz#42738b2be57ced85f46154320aabb51ab003705a" integrity sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ== -ws@^8.18.3: - version "8.18.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" - integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== - xml-beautifier@^0.5.0: version "0.5.0" resolved "https://registry.npmjs.org/xml-beautifier/-/xml-beautifier-0.5.0.tgz" @@ -12112,11 +11849,6 @@ xml-name-validator@^4.0.0: resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz" integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== -xml-name-validator@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz#82be9b957f7afdacf961e5980f1bf227c0bf7673" - integrity sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg== - xml2js@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499" @@ -12130,11 +11862,6 @@ xmlbuilder@~11.0.0: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== -xmlchars@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" - integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== - y18n@^5.0.5: version "5.0.8" resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" From 066dc359ffd36f7deee8b52be645bd44adc32364 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Tue, 25 Nov 2025 08:15:44 -0500 Subject: [PATCH 13/18] Remove unnecessary scrollIntoView mock (happy-dom supports it) --- .../components/TagsMultiselect/HeadlessMultiselect.test.ts | 6 +----- client/src/components/Tool/ToolForm.test.js | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/client/src/components/TagsMultiselect/HeadlessMultiselect.test.ts b/client/src/components/TagsMultiselect/HeadlessMultiselect.test.ts index 325218bd062..1510d782c0f 100644 --- a/client/src/components/TagsMultiselect/HeadlessMultiselect.test.ts +++ b/client/src/components/TagsMultiselect/HeadlessMultiselect.test.ts @@ -1,15 +1,11 @@ import { getLocalVue } from "@tests/vitest/helpers"; import { mount } from "@vue/test-utils"; -import { describe, expect, it, vi } from "vitest"; +import { describe, expect, it } from "vitest"; import { nextTick } from "vue"; import HeadlessMultiselect from "./HeadlessMultiselect.vue"; describe("HeadlessMultiselect", () => { - // this function is not implemented in jsdom - // mocking it to avoid false errors - Element.prototype.scrollIntoView = vi.fn(); - const localVue = getLocalVue(); type Props = InstanceType["$props"]; diff --git a/client/src/components/Tool/ToolForm.test.js b/client/src/components/Tool/ToolForm.test.js index 11daa7fb473..aade2046cf7 100644 --- a/client/src/components/Tool/ToolForm.test.js +++ b/client/src/components/Tool/ToolForm.test.js @@ -41,9 +41,9 @@ describe("ToolForm", () => { }), ); - // the PersonViewer component uses a BPopover that doesn't work with jsdom properly. It would be + // the PersonViewer component uses a BPopover that doesn't work in the test environment. It would be // better to break PersonViewer and OrganizationViewer out into smaller subcomponents and just - // stub out the Popover piece I think. + // stub out the Popover piece. suppressBootstrapVueWarnings(); axiosMock = new MockAdapter(axios); From fe5b3ffc6c8b9850b790a8e85b395fc344812f39 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Tue, 25 Nov 2025 09:01:39 -0500 Subject: [PATCH 14/18] Remove unnecessary observer mocks (happy-dom supports them) --- client/tests/vitest/setup.ts | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/client/tests/vitest/setup.ts b/client/tests/vitest/setup.ts index 7987d637915..471ed8127fc 100644 --- a/client/tests/vitest/setup.ts +++ b/client/tests/vitest/setup.ts @@ -73,31 +73,6 @@ if (typeof structuredClone === "undefined") { }; } -// Mock IntersectionObserver -class MockIntersectionObserver { - observe = vi.fn(); - disconnect = vi.fn(); - unobserve = vi.fn(); -} - -Object.defineProperty(window, "IntersectionObserver", { - writable: true, - configurable: true, - value: MockIntersectionObserver, -}); - -// Mock ResizeObserver (not fully implemented in happy-dom) -class MockResizeObserver { - observe = vi.fn(); - disconnect = vi.fn(); - unobserve = vi.fn(); -} - -Object.defineProperty(window, "ResizeObserver", { - writable: true, - configurable: true, - value: MockResizeObserver, -}); // Mock HTMLDialogElement methods if not available if (typeof HTMLDialogElement !== "undefined") { From a1714ae99d4d4bc8897bfd8d69585cc351dea6db Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Tue, 25 Nov 2025 09:10:44 -0500 Subject: [PATCH 15/18] Remove unnecessary polyfills and mock KaTeX Happy-dom natively supports setImmediate, structuredClone, and HTMLDialogElement, so we can remove those polyfills. Mock KaTeX entirely since we don't test it directly - this avoids the quirks mode warning it logs at module load time. --- client/tests/vitest/setup.ts | 50 ++++++------------------------------ 1 file changed, 8 insertions(+), 42 deletions(-) diff --git a/client/tests/vitest/setup.ts b/client/tests/vitest/setup.ts index 471ed8127fc..b3bf8dd8767 100644 --- a/client/tests/vitest/setup.ts +++ b/client/tests/vitest/setup.ts @@ -1,10 +1,9 @@ // Vitest setup file - mirrors Jest setup with Vitest-compatible APIs -/// import "@testing-library/jest-dom/vitest"; import "fake-indexeddb/auto"; import "vitest-location-mock"; -import { afterAll, afterEach, beforeAll, vi } from "vitest"; +import { vi } from "vitest"; // Vue configuration import Vue from "vue"; @@ -24,6 +23,13 @@ vi.mock("@handsontable/vue", () => ({ HotTable: {}, })); +// Mock KaTeX to avoid quirks mode warning (it checks document.compatMode at module load) +vi.mock("katex", () => ({ + default: { + renderToString: (latex: string) => `${latex}`, + }, +})); + // Provide a mocked version of Vue to ensure above settings are not // overridden by a Vue library that gets imported later vi.doMock("vue", () => ({ @@ -47,39 +53,6 @@ if (typeof window !== "undefined" && window.navigator) { }); } -// Ensure document has proper doctype to avoid quirks mode (needed for KaTeX) -if (typeof document !== "undefined" && !document.doctype) { - const doctype = document.implementation.createDocumentType("html", "", ""); - if (document.childNodes.length > 0) { - document.insertBefore(doctype, document.childNodes[0]); - } else { - document.appendChild(doctype); - } -} - -// Replace setImmediate with setTimeout to fix certain tests -if (!global.setImmediate) { - Object.defineProperty(global, "setImmediate", { - writable: true, - configurable: true, - value: (fn: (...args: any[]) => void, ...args: any[]) => global.setTimeout(fn, 0, ...args), - }); -} - -// Add structuredClone polyfill if not available -if (typeof structuredClone === "undefined") { - global.structuredClone = (obj: any) => { - return JSON.parse(JSON.stringify(obj)); - }; -} - - -// Mock HTMLDialogElement methods if not available -if (typeof HTMLDialogElement !== "undefined") { - HTMLDialogElement.prototype.showModal = HTMLDialogElement.prototype.showModal || vi.fn(); - HTMLDialogElement.prototype.close = HTMLDialogElement.prototype.close || vi.fn(); -} - // Mock BroadcastChannel to fix Pinia state synchronization errors // Node.js's BroadcastChannel has a type mismatch with the browser API class MockBroadcastChannel extends EventTarget { @@ -111,13 +84,6 @@ const failOnConsole = (await import("vitest-fail-on-console")).default; failOnConsole({ shouldFailOnError: true, shouldFailOnWarn: true, - silenceMessage: (message: string) => { - // KaTeX warns about quirks mode at module load time before we can set doctype - if (message.includes("KaTeX doesn't work in quirks mode")) { - return true; - } - return false; - }, }); // Import and setup MSW if needed From a1e3b5810ce44a5c3d5972d5205d9f72ba78ca03 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Tue, 25 Nov 2025 09:11:32 -0500 Subject: [PATCH 16/18] Remove outdated jsdom reference from test comment --- client/src/components/Markdown/Sections/MarkdownGalaxy.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/Markdown/Sections/MarkdownGalaxy.test.js b/client/src/components/Markdown/Sections/MarkdownGalaxy.test.js index a0ed7472dc3..c571c77367b 100644 --- a/client/src/components/Markdown/Sections/MarkdownGalaxy.test.js +++ b/client/src/components/Markdown/Sections/MarkdownGalaxy.test.js @@ -91,7 +91,7 @@ describe("MarkdownContainer", () => { const container = wrapper.find(".collapse"); expect(container.attributes("style")).toBe("display: none;"); await link.trigger("click"); - // After click, style attribute is removed (undefined in happy-dom, "" in jsdom) + // After click, style attribute is removed expect(container.attributes("style")).toBeFalsy(); }); From 2f6b049258f6f8439800198779b2fe23c1fbd684 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Tue, 25 Nov 2025 09:12:44 -0500 Subject: [PATCH 17/18] Update ro-crate-zip-explorer to 0.4.1 (fixes sourcemap warnings) --- client/package.json | 2 +- client/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/package.json b/client/package.json index 5ece4f33964..5b2a2c8574b 100644 --- a/client/package.json +++ b/client/package.json @@ -99,7 +99,7 @@ "querystring-es3": "^0.2.1", "regenerator-runtime": "^0.14.0", "requirejs": "2.3.7", - "ro-crate-zip-explorer": "^0.4.0", + "ro-crate-zip-explorer": "^0.4.1", "rxjs": "^7.8.1", "rxjs-spy": "^8.0.2", "rxjs-spy-devtools-plugin": "^0.0.4", diff --git a/client/yarn.lock b/client/yarn.lock index bbfb6692c6a..b5f7475899c 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -9475,10 +9475,10 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" -ro-crate-zip-explorer@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/ro-crate-zip-explorer/-/ro-crate-zip-explorer-0.4.0.tgz#28e6a5a0c7dd7a7e5eb8ec5a271e8d108402ca62" - integrity sha512-23WC9WuJ+FMjOjC2uyXdT8IpZ/DYqWNqtMkFNgBtyqHLVXVY7nIvCgV+nMYPHPHnnDlr2m0fjQagXxd0xXiDUQ== +ro-crate-zip-explorer@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/ro-crate-zip-explorer/-/ro-crate-zip-explorer-0.4.1.tgz#bc2b94e32de5390df2b1622a58442b022eb50bf6" + integrity sha512-nnO5NLiW3w7LsuP+CA4zl/FS+NqhGvvtijMzFh+0m6+uySNORU/iYwirGuPelX0s9rBJ5jPv37Z3y51zLr10Rg== dependencies: jszip "^3.10.1" pako "^2.1.0" From 6827d43c1e987a51990dbf43a273faf04f0cfe65 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Tue, 25 Nov 2025 09:46:04 -0500 Subject: [PATCH 18/18] Extract default config to const to avoid duplication --- client/src/composables/__mocks__/config.ts | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/client/src/composables/__mocks__/config.ts b/client/src/composables/__mocks__/config.ts index 5d0bdce982b..2f6623faebc 100644 --- a/client/src/composables/__mocks__/config.ts +++ b/client/src/composables/__mocks__/config.ts @@ -1,8 +1,8 @@ import { vi } from "vitest"; import { ref } from "vue"; -// Default mock config values that tests can override -const mockConfig = ref({ +// Default mock config values +const defaultConfig = { allow_local_account_creation: true, enable_oidc: false, mailing_join_addr: "", @@ -14,7 +14,9 @@ const mockConfig = ref({ welcome_url: "", citation_bibtex: "", toolbox_auto_sort: false, -}); +}; + +const mockConfig = ref({ ...defaultConfig }); export const useConfig = vi.fn(() => ({ config: mockConfig, @@ -28,17 +30,5 @@ export function setMockConfig(config: Record) { // Helper function to reset config to defaults export function resetMockConfig() { - mockConfig.value = { - allow_local_account_creation: true, - enable_oidc: false, - mailing_join_addr: "", - prefer_custos_login: false, - registration_warning_message: "", - server_mail_configured: false, - show_welcome_with_login: false, - terms_url: "", - welcome_url: "", - citation_bibtex: "", - toolbox_auto_sort: false, - }; + mockConfig.value = { ...defaultConfig }; }