Skip to content

Vue3 - #20787

Draft
dannon wants to merge 95 commits into
galaxyproject:devfrom
dannon:vue3-2025-07
Draft

Vue3#20787
dannon wants to merge 95 commits into
galaxyproject:devfrom
dannon:vue3-2025-07

Conversation

@dannon

@dannon dannon commented Aug 19, 2025

Copy link
Copy Markdown
Member

The test migration is still a giant pain, there are warnings, but the app builds, runs, and much of it does work. Very obviously still a work in progress, but I'm making this into a PR for at least a little more visibility, hoping to reduce conflicts and recruit folks to help test manually, fix tests, etc.

I will try to keep this rebased frequently so the branch doesn't slip away into the darkness yet again. (and, I rebased yesterday and it's already got 6 conflicted files)

Also, I have separated this from the vite branch as that actually (somewhat unexpectedly) had evenmore blockers than this one did. We still want to swap over, but we can push this one first at this point. The previous perceived dependency on the vite migration was primarily typescript errors we have for the most part now addressed in the build.

Unit test status (~10% failing, which is still a lot.)
image

Notes:

  • We're running with significant whitespace mode on in the vue compiler -- this is not the default and we should swap over time.

STATUS AND TASK LIST

Vue 3 Migration - Parallel Work Task List

Current Status (2026-01-13)

  • Branch: vue3-2025-07 (~230 commits ahead of dev)
  • Build: Production build compiles successfully
  • Unit Tests: 203 failing / 83 passing files (699/601 tests)
  • Selenium Tests: Not yet run on this iteration

Narrative Summary: Understanding the Failures

The Core Problem: Bootstrap-Vue + @vue/compat Slot Incompatibility

The biggest blocker is Bootstrap-Vue's incompatibility with Vue 3's @vue/compat mode. This manifests as:

TypeError: Cannot read properties of null (reading 'ce')
    at renderSlot (vue.cjs.js:6178:32)

Why this happens: Bootstrap-Vue was built for Vue 2's slot API. Vue 3 fundamentally changed how slots work internally. The @vue/compat compatibility layer handles most Vue 2 patterns, but Bootstrap-Vue's internal slot manipulation triggers edge cases where the rendering context (currentRenderingInstance) is null when it shouldn't be.

Components affected:

  • BPopover - 8 files using it
  • BTable - 39 files using it
  • BModal with custom slots
  • BTabs / BTab
  • Any component passing slot props

This is why many tests show "empty DOMWrapper" errors - the components aren't rendering because of these slot errors.

Secondary Issues

1. Vue Test Utils v1 → v2 API Changes

We've built adapters to handle most of these, but some tests still use old patterns:

  • wrapper.destroy()wrapper.unmount()
  • propsDataprops
  • Some mount option differences

2. Axios Mock Issues

Tests that use axios directly (not through our API client) hit mock configuration issues with Vitest's different mocking approach.

3. Console Warning Failures

We use vitest-fail-on-console to catch unexpected warnings. Many Vue compat deprecation warnings trigger test failures. We've suppressed common ones, but some leak through.

4. Store Setup Issues

Some tests don't properly set up required Pinia stores (e.g., provideScopedWorkflowStores).


Parallelizable Task List

Category A: Quick Fixes (Individual files, no dependencies)

These can be done in parallel by anyone. Each is a small, self-contained fix.

Task File Issue Effort
A1 PersistentTaskProgressMonitorAlert.test.ts Missing import { vi } from 'vitest' 5 min
A2 ObjectStoreBadge.test.ts Wrong import path: tests/jest/helpers@tests/vitest/helpers 5 min
A3 FormData.test.ts Wrong import path: tests/jest/helpers@tests/vitest/helpers 5 min
A4 FormSelectMany.test.ts Wrong import path (verify) 5 min
A5 ToolsList.test.ts Wrong import path (verify) 5 min

Category B: VTU v2 API Updates (Pattern-based, can be split)

Fix tests using deprecated Vue Test Utils v1 patterns.

Task Pattern Count Description
B1 wrapper.destroy() 4 files Change to wrapper.unmount()
B2 propsData 5 files Change to props in mount options
B3 vi.spyOn() on non-function 8 files Fix spy setup (need to spy on method, not object)
B4 Empty DOMWrapper calls ~50 tests Fix selectors or add await for async rendering

How to find these:

# Find wrapper.destroy
grep -r "wrapper.destroy()" client/src --include="*.test.*"

# Find propsData
grep -r "propsData" client/src --include="*.test.*"

# Find problematic spyOn
grep -r "vi.spyOn" client/src --include="*.test.*"

Category C: Bootstrap-Vue Slot Issues (Requires component changes)

These tests fail because the components use Bootstrap-Vue components with slots. Two options for each:

  1. Skip the test temporarily (with TODO comment explaining why)
  2. Replace Bootstrap-Vue component with alternative
Task Component BV Usage Recommendation
C1 TemplateSummaryPopover.vue BPopover Replace with Floating UI or skip test
C2 InstanceDropdown.vue BDropdown Replace or skip test
C3 EditSecretsForm.vue Various Review and fix/skip
C4 VaultSecret.vue Form components Review and fix/skip
C5 Grid components (14 files) BTable Major effort - see Category E

Category D: Store/Context Setup Issues

Tests failing because required context/stores aren't set up.

Task Test File Missing Setup
D1 WorkflowInvocationState.test.ts provideScopedWorkflowStores
D2 JobStep.test.ts Workflow store setup
D3 Several workflow-related tests Same pattern

Fix approach: Add proper store setup in test's beforeEach:

import { provideScopedWorkflowStores } from "@/stores/workflowEditorStateStore";
// Then use in mount options or setup

Category E: Major Component Migrations (Blocking decisions)

These require team decision on approach before proceeding.

Task Scope Options
E1 BTable usage (39 files) 1) Migrate to Bootstrap-Vue-Next, 2) Replace with custom table, 3) Skip tests until full migration
E2 BPopover usage (8 files) 1) Migrate to Bootstrap-Vue-Next, 2) Replace with Floating UI, 3) Skip tests
E3 BModal with custom slots Same as above

Category F: Test Infrastructure

Task Description Effort
F1 Review vitest-fail-on-console rules Some warnings may need to be suppressed
F2 Fix axios mock configuration Update tests/vitest/__mocks__/axios.js if needed
F3 Review and update test helpers tests/vitest/helpers.js may need more compatibility shims

Priority Order

Phase 1: Quick Wins (Unblocks ~10 tests)

  1. A1-A5: Fix import paths and missing imports
  2. B1-B2: Fix wrapper.destroy() and propsData

Phase 2: Pattern Fixes (Unblocks ~30-50 tests)

  1. B3-B4: Fix vi.spyOn and empty DOMWrapper issues
  2. D1-D3: Fix store setup issues

Phase 3: Strategic Decisions

  1. E1-E3: Team decides on Bootstrap-Vue approach
    • If migrating to BV-Next: start E1-E3 migrations
    • If staying with compat: skip affected tests with TODOs

Phase 4: Component Replacements (if needed)

  1. C1-C5: Replace/fix Bootstrap-Vue slot components

How to test the changes?

(Select all options that apply)

  • I've included appropriate automated tests.
  • This is a refactoring of components with existing test coverage.
  • Instructions for manual testing are as follows:
    1. [add testing steps and prerequisites here if you didn't write automated tests covering all your changes]

License

  • I agree to license these and all my past contributions to the core galaxy codebase under the MIT license.

@jmchilton

Copy link
Copy Markdown
Member

Very exciting stuff - thank you so much!

Would you be okay if I wrote a wrapper Vue test mount that would allow us to use either of these patterns based on what version is available? I feel like we can start migrating all the tests without the upgrade that way. We an use the new paradigms now and it would simplify this branch a lot of getting using paradigms that will reduce conflicts in the future.

Also I think the upgrade of prettier in the middle there seems like it is a causing a lot of unrelated changes. Is it possible we can pull that out and apply to our current codebase or that we can remove it and apply it after the Vue 3 migration is done?

@dannon
dannon force-pushed the vue3-2025-07 branch 2 times, most recently from 8e8bdf8 to fe3fdcb Compare August 26, 2025 15:06
@dannon

dannon commented Oct 28, 2025

Copy link
Copy Markdown
Member Author

Post-release-rebase update:

The branch builds and runs successfully with all TypeScript errors resolved. Selenium testing found a few reactivity issues - collection builders weren't emitting events properly (fixed by swapping @update to @input), the workflow editor has some step update problems, and there's an accessibility issue with the dataset library multiselect. On the Jest side, tests are blocked because Babel transforms our Vue Router 4 ESM imports to CommonJS, which breaks Vue Router's conditional exports. I'm experimenting with Vitest as an alternative since we want to migrate to it anyway for the better Vite integration - got Bootstrap Vue and vue-rx mocked successfully, but components with slots still fail because of how @vue/compat handles rendering. Just trying to figure out the right order of operations here - whether to push through on Vitest now, stick with Jest until we're fully on Vue 3, or some hybrid approach.

@mvdbeek

mvdbeek commented Oct 28, 2025

Copy link
Copy Markdown
Member

Can you update the lock file so we can see the tests ?

@dannon dannon changed the title Vue3? Vue3 Nov 19, 2025
@dannon
dannon force-pushed the vue3-2025-07 branch 2 times, most recently from 98ec6ef to 7a92e2f Compare November 21, 2025 14:10
@dannon
dannon force-pushed the vue3-2025-07 branch 2 times, most recently from 5909563 to e399b43 Compare December 16, 2025 03:24
@dannon
dannon force-pushed the vue3-2025-07 branch 4 times, most recently from bc044ad to b3cfc95 Compare January 14, 2026 03:49
@davelopez

Copy link
Copy Markdown
Contributor

@dannon what would be the best way to work on the upgrades and fixes?

  • create PR's against this branch?
  • direct commit to this branch?
  • other?

Regarding Fix axios mock configuration (F2), I think we should completely replace axios mock usages with useServerMock for consistency and compatibility for when we replace the axios calls with openAPI fetcher ones in the future.

@dannon

dannon commented Jan 20, 2026

Copy link
Copy Markdown
Member Author

@davelopez Was just rebasing this and see your comment -- so sorry I missed it! Yeah, just push here or however you prefer, no worries!

dannon added 24 commits May 8, 2026 22:19
Switched to createRouter/createWebHistory, added setActivePinia call, and
suppress Vue compat warnings during migration.
Created adapters that allow old Vue 2 test patterns to work without modifying
individual test files:

- vue-test-utils-adapter.ts: Transforms { global: localVue, pinia, router }
  mount options to proper VTU v2 format with plugins array
- vue-router-adapter.ts: Provides VueRouter constructor for tests using the
  old `import VueRouter from 'vue-router'` and `new VueRouter()` pattern
- helpers.js: Added .use() method to getLocalVue() for localVue.use() calls
- setup.ts: Relaxed fail-on-console to not fail on Vue compat warnings

These adapters are a bridge during migration - tests can be updated to use
proper Vue 3 patterns incrementally while the adapters keep things working.
- Add missing vi import in PersistentTaskProgressMonitorAlert test
- Remove duplicate jest helper imports (now using vitest helpers)
- Update VueRouter usage to v4 pattern with createTestRouter()
- Change wrapper.destroy() to wrapper.unmount() (VTU v2 API)
- Change propsData to props in mount options (VTU v2 API)
Vue Router 4 deprecates the next() callback in favor of returning
values from navigation guards. beforeRouteEnter is the one exception
(still needs next(vm => ...)) since the component instance isn't
available yet — DataManager.vue is left as-is for that reason.
Uses the existing createConfiguredApp-based mounting utility instead
of the Vue 2 Vue.extend() + new constructor().$mount() pattern.
Replace vue2-teleport with native Vue 3 Teleport in HeadlessMultiselect,
and drop vue-class-component and vue-property-decorator which have no
remaining imports in the codebase.
- Remove duplicate v-bind attributes in storage overview charts
- Move v-for key to <template> tag in GTable (Vue 3 requirement)
- Replace vue-router/composables imports with vue-router (VR4 path)
- Remove Vue.set() usage, use direct property assignment (Vue 3 Proxy)
- Update eslint wrapper for flat config (eslint.config.mjs)
GTable: move :key to <template v-for> per Vue 3 requirement.
FormData: remove duplicate v-model:workflow-tab and convert remaining
:formats-visible.sync to v-model:formats-visible.

Also regenerate pnpm-lock.yaml after package.json conflicts during
rebase onto upstream/dev.
setup.ts referenced an undefined `Vue` global to register the
g-tooltip mock directive. In Vue 3 / VTU v2 there is no global Vue
constructor; register the directive on `config.global.directives`
so it is applied to every test mount.

This unblocks ~112 test files that were failing at setup time with
'Vue is not defined'.
- historyStore: replace Vue 2 set()/del() with direct property
  assignment / delete operator. del() was undefined and threw at
  runtime in tests.
- Test files: replace wrapper.destroy() with wrapper.unmount() for
  Vue Test Utils v2.
- Test files: add explicit describe/it/expect imports where missing
  (vitest globals are disabled).

Addresses ~30 of the remaining test failures (delete operator, VTU v2
unmount API, missing vitest globals). Heading slot rendering and
BootstrapVue compat-mode issues still account for the bulk of the
remaining failures.
happy-dom does not provide Worker, which breaks tests that import
composables using web workers (filter, selectMany). Add a MockWorker
to the global vitest setup mirroring the existing BroadcastChannel
mock.

The two test mock helpers (filter.ts, selectMany.ts) called
`vi.mock` without importing `vi`. With `globals: false` in
vitest.config.mts that throws ReferenceError. Import `vi`
explicitly.
New file added upstream still used the vue-router 3 'composables'
subpath import which doesn't exist in vue-router 4. Caught at build
time after rebase.
vitest-fail-on-console v0.10.1 only accepts shouldFailOnError and
shouldFailOnWarn as booleans. The predicate functions we were passing
were never invoked, so 360+ test failures were leaking through on Vue
compat warnings the filter was supposed to suppress.

Switching to silenceMessage(message, methodName) actually runs the
predicate. Recovers about 76 individual tests and 11 test files in
the existing suite, and gets compat-mode noise (resolveComponent,
resolveDirective, withDirectives, COMPONENT_FUNCTIONAL deprecation,
ref-owner warnings, etc.) properly suppressed during the migration.
Leftover from the recent rebase resolution -- I kept the imports while
auto-merge sorted out the body, but later commits on the branch already
removed every set()/del() call. ESLint was flagging the dead imports.
vue3-recommended rules in the eslint config caught a sweep of legacy
template patterns the autofix could resolve:

- :foo.sync='bar' -> v-model:foo='bar' (~75 spots)
- slot='name' -> v-slot:name in a few legacy templates
- Attribute ordering rules (v-model: first, v-bind groups together)

Also tidied up a handful of small things the linter was flagging:
- Duplicate 'emits' key in admin/JobsTable.vue
- Stray Vue 2 filter '| l' in Tool/ToolForm.vue (-> localize(...))
- 'no-unused-expressions' on the reactivity-pin pattern in
  useRemoteFileBrowser.ts (added 'void' to mark intent)
- '_router' prefix on unused-but-side-effect-having injectTestRouter
  return values in two test files (real fix is plumbing the router
  through global.plugins, follow-up).

Build passes; no runtime behavior change intended.
Vitest globals are off, so the implicit vi reference would error
out at module load. Same pattern as the earlier __mocks__ fixes.
createLocalVue was VTU v1 only -- VTU v2 doesn't export it. Use
getLocalVue from the shared helpers, which already returns a v2
global config object, and pass it via mount's global option.
Three tests were doing vi.mock('vue-router', () => ({ useRouter: ... }))
which fully replaced the module and dropped createRouter/default/etc.
Importers downstream (incl. our vue-router-adapter) blew up looking for
the missing exports.

Switching to vi.mock with importOriginal lets us override only the
hooks each test cares about while keeping the rest of the module
intact. Also bumps ToolsList.test back to plumbing the test router
through global.plugins, which it had been creating but never installing.
vue-virtual-scroll-list calls Vue.component(...) at module load -- a
Vue 2 global API that doesn't exist in 3. The lib has no Vue 3 release
and we still want it for production virtual scrolling. Workaround for
tests: a tiny mock that just renders each data-source through
data-component, aliased in vitest.config.mts. Unblocks
MultipleView.test.js (and any other test mounting MultipleViewList).

While in the area, three more tests had VTU v1 leftovers I converted:
- MarkdownVitessce: localVue.component(...) -> global.components in
  the mount call
- ToolPanel + ToolForm: router was being created via injectTestRouter
  but never installed; moved router and pinia into global.plugins so
  the components can actually find them
Two patches via pnpm to unblock the test suite while we're still on
Vue 3.5.18 + compat MODE 2.

@vue/compat: renderSlot and convertLegacyFunctionalComponent's Func
both deref currentRenderingInstance / instance directly, with no
guard. Under our test harness many components hit those code paths
with a null instance during the initial render pass, throwing 'ce'
and 'parent'/'vnode' errors. Add ?. guards on the obvious lines
(currentRenderingInstance.ce / .parent, instance.vnode.children /
.props, instance.parent.proxy). 41 'ce' first-errors, 14 'parent',
6 'vnode' first-errors -- all gone.

bootstrap-vue: the Vue-3 compat shim wraps every component's render
with 'function (h) { ... patchedH uses h ... }', expecting Vue 2's
render(createElement) signature. Vue 3 doesn't pass h, so h was an
object (the proxy / ctx) and h.apply blew up. Fall back to Vue.h
when the arg isn't callable. 19 'h.apply' first-errors -- gone.

Net: 138 / 340 files, 1294 / 2015 tests passing (was 127 / 1169
before Phase 3). Mostly the still-failing tests are real assertion
errors and empty-DOMWrapper issues now -- Phase 4 territory.
- Add renderStubDefaultSlot: true to getLocalVue's global config so
  stubbed wrapper components (BCard, BInputGroup, etc) still render
  the test's payload children instead of swallowing them.
- Extend the bootstrap-vue stub list with the rest of the layout
  family (BInputGroup*, BFormRadio*, BFormFile, BFormTags, BFormDatepicker,
  BFormTimepicker, BIcon*, BImg*, BListGroup*, BProgress*,
  BButtonGroup, BCard*, BContainer/BRow/BCol, BMedia*) so tests stop
  hitting bootstrap-vue's broken Vue-3 functional-component path.
- Teach the vue-test-utils-adapter to translate VTU v1 propsData ->
  props and top-level stubs -> global.stubs, so the long tail of
  unmigrated tests stops silently dropping those options.

No test count change yet -- BInputGroupAppend/etc are functional
components that VTU v2 doesn't reliably stub-match, so several tests
still fail. But this puts the scaffolding in place; per-component
fixes will pick up from here.
Extends the existing bootstrap-vue patch so patchedH translates the
Vue 2 nested data object { attrs, props, on, domProps } into the flat
object Vue 3's h() expects. Without this, class/data-* attributes and
event handlers were silently dropped on bootstrap-vue components --
which is why a BButton with class='foo' rendered as <button> with no
'foo' class.

Small immediate win (138 -> 139 files, 1294 -> 1301 tests). The
remaining DOMWrapper failures still need component-level work since
many tests rely on bootstrap-vue functional components whose
ctx-based class merging is more involved than a flat-data fix can
reach.
The previous commit (0cc2e2f) updated patches/bootstrap-vue@2.23.1.patch
but the committed lockfile still referenced the prior patch hash, so
CI's pnpm install --frozen-lockfile bailed with
ERR_PNPM_LOCKFILE_CONFIG_MISMATCH. Just regenerated the lockfile.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants