Skip to content

Vue 3 Migration#1487

Open
lreading wants to merge 14 commits intomainfrom
feat/vue-compat
Open

Vue 3 Migration#1487
lreading wants to merge 14 commits intomainfrom
feat/vue-compat

Conversation

@lreading
Copy link
Copy Markdown
Collaborator

@lreading lreading commented Mar 10, 2026

Summary:

This upgrades Threat Dragon to Vue 3 using vue-compat, with the goal of making the smallest set of changes needed to keep the app working and releasable while we continue paying down migration debt in follow-up work.

This is still a transitional PR. There is temporary compat code here, especially around Jest and a few Vue 2 APIs. I do not consider that the final state. The goal of this PR is to get the app running correctly on Vue 3 first, then continue cleanup in smaller follow-ons.

Closes #444

Current State

  • Web app is working
  • Desktop app is working
  • Site unit tests are re-enabled and green with a temporary Jest compat layer
  • Web e2e tests are green
  • Desktop e2e tests are green
  • Trivy is currently expected to fail and is unrelated to this PR

What I Tested

Unit tests: all passing, 2 newly skipped
e2e tests: all passing
Desktop e2e tests: all passing
Visual regression: I had AI help me set up some playwright / visual regression tests to help identify any significant drifts in the UI. I compared 2.6.0 from the demo site to my local, as well as the 2.6.0 published AppImage against a local build. The only notable differences remaining include some minor margin/padding drift that does not materially change the layout or UX.

Help Wanted

I would especially appreciate help testing these areas:

  • Provider flows (GitHub, GitLab, etc - I was only testing using local session)
  • Diagram editing and threat/property editing flows (tested, but would love a second set of eyes)

Known Tradeoffs

  • The Jest compat layer is messy and temporary. It keeps the existing unit tests mostly intact during the migration
  • There are still follow-up cleanup items for BootstrapVue, i18n, Vue compat warnings, router semantics, and test harness cleanup. I need to document more of these somewhere outside of this PR.

Description for the changelog:

Upgrade to Vue3

Declaration:

Thanks for submitting a pull request, please make sure:

  • content meets the license for this project
  • appropriate unit tests have been created and/or modified
  • you have considered any changes required for the functional tests
  • you have read the contribution guide and agree to the Code of Conduct
  • either no AI-generated content has been used in this pull request
  • or any use of AI in this pull request has been disclosed below:
    • AI Tools: Cursor / Codex
    • LLMs and versions: "auto" / "
    • Prompts:
      • Read the vue3 migration guide and the docs on the vue3 migration build (provided links). Please review the dependencies and the code, and provide a detailed plan on how to upgrade to vue3, with as few pull requests as possible. PRs should be reasonably single-purpose, and small enough to review. Each PR should have a working build and tests.
      • Make the minimal changes necessary to run vu3
      • Create a visual regression test framework to compare the TD demo site to our local, as well as the locally built desktop app against the 2.6.0 release
      • Create a jest compat layer to ensure that all unit tests pass, without having to touch too many spec files to keep the PR as small and targeted as possible

Other info:

When auditing production dependencies, there are now 0 vulnerabilities!
image

I think this is ready for review? 🤞

…est stack

- Unit tests are broken, but there's a pattern to fix them
- Much of this work done using Cursor / "auto"
- I've done extremely minimal testing of the web app
- I have not tested the electron build
@lreading lreading self-assigned this Mar 10, 2026
@lreading lreading requested a review from jgadsden as a code owner March 10, 2026 05:43
@lreading lreading marked this pull request as draft March 10, 2026 05:43
@jgadsden
Copy link
Copy Markdown
Collaborator

I promise hands off from my side :)

font-size: $icon-height;
max-height: $icon-height;
margin: 0 5px 0 5px;
color: $white;
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why, but the header fontawesome icons for links changed color. This fixes that.

});
if (routerInstance === null) {
routerInstance = createRouter({
history: createWebHashHistory(),
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are known issues with vue3 router in history mode in Electron. This also aligns with the current state of our web app, so there shouldn't be any breaking changes here in urls / deep linking.

More info: https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/commonIssues.html#blank-screen-on-builds-but-works-fine-on-serve

import Toast, { toastOptions, installToastGlobalProperties } from './plugins/toastification.js';

Vue.config.productionTip = false;
let appProxy;
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Desktop entry uses appProxy so Electron IPC handlers registered at load time can access $store, $router, $t, $bvModal, $toast when they run asynchronously.


const getWrapper = (propsData) => shallowMount(TdGraphThreats, {
localVue,
const getWrapper = (propsData) => shallowMount(TdGraphThreats, mountOptions(localVue, {
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some API differences between Vue 2 and Vue 3 around the shallowMount. findComponent just becomes find, and there are a few other differences. This test is a pretty good example of how migration will work for many other tests.

"vue": "^3.4.21",
"vue-i18n": "^9.14.1",
"vue-router": "^4.4.5",
"vue-toastification": "^2.0.0-rc.5",
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving vue-toastification for compatibility, however we should switch to a different toast library. rc5 is the latest version, and there has been no updates since. https://github.com/Maronato/vue-toastification/releases

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh my, last release 4.5 years ago - should we raise a separate issue to track the need for replacing vue-toastification ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, 100%
I'm keeping a running tally of follow-on items for the complete vue migration. This will be on that list for sure! Once I'm confident that this is in a good place for review, I will create issues for all the follow-ups (i18n, bootstrap, build/runtime compat warnings, removing compat, etc)

"@vue/eslint-config-standard": "^7.0.0",
"@vue/test-utils": "^1.3.0",
"@vue/test-utils": "^2.4.6",
"@vue/vue2-jest": "^27.0.0",
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not 100% sure if we need both vue2-jest and vue3-jest, but we can leave for now.

@lreading
Copy link
Copy Markdown
Collaborator Author

I still have a lot of manual testing to do, but I wanted to highlight that the unit tests are going to make this PR scope grow out of control. I don't want to burden anyone with a PR that size - this is already large enough. There are probably ~30-40 more tests that need updating.

We can handle this in a few ways, but here's I'm proposing:

  • Temporarily disable the site unit tests as part of the PR and CI actions
  • If Desktop unit test coverage #1486 is merged, handle the merge conflicts in this branch
  • Incremental PRs addressing broken tests
  • Hold off on other work/updates until the unit tests are fully restored and enabled

Other options may include:

  1. Fixing all unit tests in this PR
  2. Selectively skip broken tests, fix with follow-on PRs
  3. Dedicated migration branch, incremental PRs fixing tests

The first 2 options above would still make this PR significantly larger.
Option 3 would also result in a massive PR to main, but it would have been incrementally reviewed. This strategy risks drift from main, potentially leading to some nasty merge conflicts, unless we did a code-freeze.

@jgadsden - do any of these strategies resonate with you, or do you have any other suggestions on how to keep this under control? 😅

throw error;
}
});
await this.$store.dispatch(LOGOUT);
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOGOUT clears provider.selected, and some of the views assume that there will always be a value for that. This was causing a runtime error in the desktop, but I didn't notice it in the web version oddly enough. Doing the await Promise.resolve is a bit of an ugly pattern, but I don't foresee any issues with dispatching the event after the navigation completes. This did fix the bug.

"@fortawesome/free-brands-svg-icons": "^6.2.0",
"@fortawesome/free-solid-svg-icons": "^6.2.0",
"@fortawesome/vue-fontawesome": "^2.0.8",
"@fortawesome/vue-fontawesome": "^3.1.3",
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was trying to keep the scope down, but for whatever reason I could not get vue-fontawesome v2.x to work inside electron. It was working fine in web, but it was completely breaking the ability to render anything in desktop. Just an unhelpful console error with a blank page. Fortunately, this 2-3 upgrade seems pretty low-lift, and the desktop client is now working again.


- name: Run unit tests
run: npm run test:unit
# TODO: Reinstate tests as part of build
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jgadsden - Do we want to leave this disabled to keep this PR somewhat reasonable in size?
I'm fine with fixing all unit tests as part of this PR, but the size will grow a lot. I want to make sure you can actually review when you have the time!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point @lreading , we can reinstate when late it is convenient

md="6"
>
<b-form-group label-cols="auto" id="outofscope-group">
<b-form-checkbox id="outofscope" v-model="cellRef.data.outOfScope" @change="onChangeScope()">{{
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All b-form-checkbox components had a runtime error, complaining that "assign" is not a function. This was happening in the Bootstrap code, not ours. I couldn't find a ton of information online, but applying the correct classes and constructing these using divs and inputs retains the same style and behavior.

Alternatively, we could create a td-checkbox component, but I think it's overkill, at least for now.

.contains('Web Application Config (Store)')
.should('be.visible');

cy.get('#show_outofscope').click({ force: true });
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we're using inputs now, the correct semantics are check and uncheck - click was no longer working the way we expected.

@lreading
Copy link
Copy Markdown
Collaborator Author

I'll be completing #1275 prior to merging this. Having additional e2e coverage for desktop will reduce the risk of regressions when doing breaking dependency upgrades.

@lreading lreading added the Tech Debt & Maintainability Strategic pillar from Threat Dragon's roadmap label Mar 15, 2026
@@ -0,0 +1,61 @@
import * as actual from 'vue-i18n/dist/vue-i18n.cjs';
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than updating or disabling tens of tests, we can add a temporary compat layer that helps the tests pass assuming the vue2 APIs.

@@ -0,0 +1,768 @@
import * as actual from '@vue/test-utils/dist/vue-test-utils.cjs.js';
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is a bit of a disaster. Needs careful consideration if we want to continue with this pattern or not.

By adding this compat layer, we do not need to update every test that was using the vue2 test utils. Without this layer, we either disable a bunch of tests, or update all of the tests in this PR.

Now whether having this compat layer makes the PR easier or harder to review is 💯% up for discussion....


it('is the default path', () => {
expect(homeRoute.path).toEqual('');
expect(homeRoute.path).toEqual('/');
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Best I can understand, this is just a semantic difference in the new vue router version.

@@ -0,0 +1,10 @@
import { createLocalVue as compatCreateLocalVue } from '@vue/test-utils';
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another shim for the 2-3 migration to avoid having to update as many tests.

@lreading
Copy link
Copy Markdown
Collaborator Author

lreading commented Apr 7, 2026

@jgadsden - Thinking about unit tests more: I tried to make a compat layer for unit testing. It's big, ugly, difficult to review, and messy. However, it does allow us to keep the unit tests mostly in tact, and keep passing tests as a CI gate.

I did test the compat layer a bit by intentionally introducing regressions to make sure the tests started failing. This was only a spot-check though.

The other options I considered:

  • Disable unit tests in CI and start chipping away at restoring them
  • Update most of the suites to be skipped
  • Update all unit tests as part of this PR

Each one has its own tradeoffs. My personal preference is this compat layer - however, I do understand that it is adding to the burden of review.. This is already a pretty beefy PR.

Do you have a strong preference here? I'm happy to revert the compat layer or explore other options!


I think this is finally ready for review. 🤞

I did not thoroughly test any providers outside of the local session.
I think the next highest value testing would be around diagramming and TM properties. I did test those pretty thoroughly, but the graphing code is pretty complex and has a lot of branching - I would love another set of eyes!

@lreading lreading changed the title DO NOT MERGE (WIP): Vue 3 migration staging ground Vue 3 Migration Apr 7, 2026
@lreading lreading marked this pull request as ready for review April 7, 2026 03:03
@jgadsden
Copy link
Copy Markdown
Collaborator

jgadsden commented Apr 7, 2026

Thanks @lreading , a huge task (but not thankless!)
My thoughts are that it will need considerable manual testing, will try and get to it some time this week

@lreading
Copy link
Copy Markdown
Collaborator Author

lreading commented Apr 7, 2026

Agreed! No rush on this, and I invite anyone in the community who is following along to pull and test these changes as well. I will leave a comment on the issue and make a short post in our Slack channel. 😄

@lreading lreading mentioned this pull request Apr 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Tech Debt & Maintainability Strategic pillar from Threat Dragon's roadmap

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrate to Vue 3

2 participants