Browse Source

Maintenance: Desktop view - Improve ticket edit watch reset handling.

Dominik Klein 3 months ago
parent
commit
b035165a3f

+ 2 - 0
app/frontend/apps/desktop/entities/user/current/stores/taskbarTabs.ts

@@ -395,6 +395,8 @@ export const useUserCurrentTaskbarTabsStore = defineStore(
     }
 
     const resetActiveTaskbarTab = () => {
+      if (!activeTaskbarTabEntityKey.value) return
+
       activeTaskbarTabEntityKey.value = undefined
     }
 

+ 2 - 2
app/frontend/apps/desktop/pages/ticket/__tests__/ticket-create/ticket-create-apply-split-ticket-article.spec.ts

@@ -31,7 +31,7 @@ describe('ticket create view - splitting of a ticket article', async () => {
     })
 
     const view = await visitView(
-      '/ticket/create??splitTicketArticleId=ticket_article_gid',
+      '/ticket/create?splitTicketArticleId=ticket_article_gid',
     )
 
     const formUpdaterCalls = await waitForFormUpdaterQueryCalls()
@@ -74,7 +74,7 @@ describe('ticket create view - splitting of a ticket article', async () => {
     })
 
     const view = await visitView(
-      '/ticket/create??splitTicketArticleId=ticket_article_gid',
+      '/ticket/create?splitTicketArticleId=ticket_article_gid',
     )
 
     await waitForFormUpdaterQueryCalls()

+ 163 - 155
app/frontend/apps/desktop/pages/ticket/__tests__/ticket-create/ticket-create.spec.ts

@@ -44,49 +44,154 @@ describe('ticket create view', async () => {
       mockPermissions(['ticket.agent'])
     })
 
-    // FIXME: This test example has to be first, otherwise it will start to fail.
-    //   This is probably due to the leftover router instance, which has to be set up in a different way for this test.
-    it('supports updating dirty flag in the associated taskbar tab', async () => {
-      const uid = getUuid()
+    it('keeps form values on cancel unsaved changes', async () => {
+      handleMockFormUpdaterQuery()
 
-      mockUserCurrentTaskbarItemListQuery({
-        userCurrentTaskbarItemList: [
-          {
-            __typename: 'UserTaskbarItem',
-            id: convertToGraphQLId('Taskbar', 1),
-            key: `TicketCreateScreen-${uid}`,
-            callback: EnumTaskbarEntity.TicketCreate,
-            entityAccess: EnumTaskbarEntityAccess.Granted,
-            entity: {
-              __typename: 'UserTaskbarItemEntityTicketCreate',
-              uid,
-              title: '',
-              createArticleTypeKey: 'phone-in',
-            },
-            dirty: false,
-          },
-        ],
+      const view = await visitView('/ticket/create')
+
+      await view.events.type(await view.findByLabelText('Title'), 'Test Ticket')
+
+      await view.events.click(
+        await view.findByRole('button', { name: 'Discard Changes' }),
+      )
+
+      const dialog = await view.findByRole('dialog', {
+        name: 'Unsaved Changes',
       })
 
+      const dialogView = within(dialog)
+
+      await view.events.click(
+        dialogView.getByRole('button', { name: 'Cancel & Go Back' }),
+      )
+
+      expect(view.getByText('Test Ticket')).toBeInTheDocument()
+    })
+
+    it('prevents submission on incomplete form', async () => {
       handleMockFormUpdaterQuery()
 
-      const view = await visitView(`/ticket/create/${uid}`)
+      const view = await visitView('/ticket/create')
+
+      await view.events.type(await view.findByLabelText('Title'), 'Test Ticket')
+
+      await view.events.click(view.getByRole('button', { name: 'Create' }))
+
+      expect(await view.findAllByText('This field is required.')).toHaveLength(
+        4,
+      )
+    })
+
+    it('creates a new ticket', async () => {
+      handleMockFormUpdaterQuery()
+
+      const view = await visitView('/ticket/create')
+
+      await view.events.type(await view.findByLabelText('Title'), 'Test Ticket')
 
+      // Page title updates when title is set
       expect(
-        await view.findByRole('button', { name: 'Cancel & Go Back' }),
+        await view.findByRole('heading', { level: 1, name: 'Test Ticket' }),
       ).toBeInTheDocument()
 
+      // Page title defaults back when title is cleared
+      await view.events.clear(view.getByLabelText('Title'))
+      await waitFor(() =>
+        expect(
+          view.getByRole('heading', { level: 1, name: 'New Ticket' }),
+        ).toBeInTheDocument(),
+      )
+
       await view.events.type(view.getByLabelText('Title'), 'Test Ticket')
 
-      const calls = await waitForUserCurrentTaskbarItemUpdateMutationCalls()
+      // Customer field
+      await handleCustomerMock(view)
 
-      expect(calls.at(-1)?.variables).toEqual(
-        expect.objectContaining({
-          input: expect.objectContaining({
-            dirty: true,
-          }),
+      handleMockUserQuery()
+
+      await view.events.click(
+        view.getByRole('option', {
+          name: 'Avatar (Nicole Braun) Nicole Braun – Zammad Foundation',
         }),
       )
+
+      // Sidebar CUSTOMER
+      expect(view.getByLabelText('Avatar (Nicole Braun)')).toBeInTheDocument()
+      expect(view.getByText('Zammad Foundation')).toBeInTheDocument()
+      expect(view.getByText('open tickets')).toBeInTheDocument()
+      expect(view.getByText('nicole.braun@zammad.org')).toBeInTheDocument()
+      expect(view.getByText('closed tickets')).toBeInTheDocument()
+      expect(view.getByLabelText('Open tickets')).toHaveTextContent('17')
+
+      // Sidebar Organization
+      handleMockOrganizationQuery()
+
+      await view.events.click(view.getByLabelText('Organization'))
+
+      expect(view.getByText('Organization')).toBeInTheDocument()
+
+      expect(view.getByText('Members')).toBeInTheDocument()
+      expect(view.getByLabelText('Avatar (Nicole Braun)')).toBeInTheDocument()
+
+      // Text field
+      await view.events.type(
+        view.getByRole('textbox', { name: 'Text' }),
+        'Test ticket text',
+      )
+
+      // Group field
+      await view.events.click(view.getByLabelText('Group'))
+      await view.events.click(view.getByRole('option', { name: 'Users' }))
+
+      // State field
+      await view.events.click(view.getByLabelText('Priority'))
+      await view.events.click(view.getByRole('option', { name: '2 normal' }))
+
+      // Priority Field
+      await view.events.click(view.getByLabelText('State'))
+      await view.events.click(
+        view.getByRole('option', { name: 'pending reminder' }),
+      )
+
+      // Date selection Field on pending reminder
+      await view.events.click(view.getByText('Pending till'))
+
+      await waitFor(() => expect(view.getByRole('dialog')).toBeInTheDocument())
+
+      const dateCells: Element[] = view.getAllByRole('gridcell', { name: '29' })
+
+      await view.events.click(<Element>dateCells.at(-1))
+
+      // Submission
+      await view.events.click(view.getByRole('button', { name: 'Create' }))
+
+      const calls = await waitForTicketCreateMutationCalls()
+
+      expect(calls.at(-1)?.variables).toEqual({
+        input: {
+          article: {
+            body: 'Test ticket text',
+            cc: undefined,
+            contentType: 'text/html',
+            security: undefined,
+            sender: 'Customer',
+            type: 'phone',
+          },
+          customer: {
+            id: 'gid://zammad/User/2',
+          },
+          groupId: 'gid://zammad/Group/1',
+          objectAttributeValues: [],
+          pendingTime: '2024-11-29T00:00:00.000Z',
+          priorityId: 'gid://zammad/Ticket::Priority/2',
+          stateId: 'gid://zammad/Ticket::State/3',
+          title: 'Test Ticket',
+        },
+      })
+
+      expect(await view.findByRole('alert')).toHaveTextContent(
+        'Ticket has been created successfully.',
+      )
     })
 
     it('renders view correctly', async () => {
@@ -239,142 +344,47 @@ describe('ticket create view', async () => {
       )
     })
 
-    it('keeps form values on cancel unsaved changes', async () => {
-      handleMockFormUpdaterQuery()
-
-      const view = await visitView('/ticket/create')
-
-      await view.events.type(await view.findByLabelText('Title'), 'Test Ticket')
-
-      await view.events.click(
-        await view.findByRole('button', { name: 'Discard Changes' }),
-      )
+    it('supports updating dirty flag in the associated taskbar tab', async () => {
+      const uid = getUuid()
 
-      const dialog = await view.findByRole('dialog', {
-        name: 'Unsaved Changes',
+      mockUserCurrentTaskbarItemListQuery({
+        userCurrentTaskbarItemList: [
+          {
+            __typename: 'UserTaskbarItem',
+            id: convertToGraphQLId('Taskbar', 1),
+            key: `TicketCreateScreen-${uid}`,
+            callback: EnumTaskbarEntity.TicketCreate,
+            entityAccess: EnumTaskbarEntityAccess.Granted,
+            entity: {
+              __typename: 'UserTaskbarItemEntityTicketCreate',
+              uid,
+              title: '',
+              createArticleTypeKey: 'phone-in',
+            },
+            dirty: false,
+          },
+        ],
       })
 
-      const dialogView = within(dialog)
-
-      await view.events.click(
-        dialogView.getByRole('button', { name: 'Cancel & Go Back' }),
-      )
-
-      expect(view.getByText('Test Ticket')).toBeInTheDocument()
-    })
-
-    it('creates a new ticket', async () => {
       handleMockFormUpdaterQuery()
 
-      const view = await visitView('/ticket/create')
-
-      await view.events.type(await view.findByLabelText('Title'), 'Test Ticket')
+      const view = await visitView(`/ticket/create/${uid}`)
 
-      // Page title updates when title is set
       expect(
-        await view.findByRole('heading', { level: 1, name: 'Test Ticket' }),
+        await view.findByRole('button', { name: 'Cancel & Go Back' }),
       ).toBeInTheDocument()
 
-      // Page title defaults back when title is cleared
-      await view.events.clear(view.getByLabelText('Title'))
-      await waitFor(() =>
-        expect(
-          view.getByRole('heading', { level: 1, name: 'New Ticket' }),
-        ).toBeInTheDocument(),
-      )
-
       await view.events.type(view.getByLabelText('Title'), 'Test Ticket')
 
-      // Customer field
-      await handleCustomerMock(view)
-
-      handleMockUserQuery()
+      const calls = await waitForUserCurrentTaskbarItemUpdateMutationCalls()
 
-      await view.events.click(
-        view.getByRole('option', {
-          name: 'Avatar (Nicole Braun) Nicole Braun – Zammad Foundation',
+      expect(calls.at(-1)?.variables).toEqual(
+        expect.objectContaining({
+          input: expect.objectContaining({
+            dirty: true,
+          }),
         }),
       )
-
-      // Sidebar CUSTOMER
-      expect(view.getByLabelText('Avatar (Nicole Braun)')).toBeInTheDocument()
-      expect(view.getByText('Zammad Foundation')).toBeInTheDocument()
-      expect(view.getByText('open tickets')).toBeInTheDocument()
-      expect(view.getByText('nicole.braun@zammad.org')).toBeInTheDocument()
-      expect(view.getByText('closed tickets')).toBeInTheDocument()
-      expect(view.getByLabelText('Open tickets')).toHaveTextContent('17')
-
-      // Sidebar Organization
-      handleMockOrganizationQuery()
-
-      await view.events.click(view.getByLabelText('Organization'))
-
-      expect(view.getByText('Organization')).toBeInTheDocument()
-
-      expect(view.getByText('Members')).toBeInTheDocument()
-      expect(view.getByLabelText('Avatar (Nicole Braun)')).toBeInTheDocument()
-
-      // Text field
-      await view.events.type(
-        view.getByRole('textbox', { name: 'Text' }),
-        'Test ticket text',
-      )
-
-      // Group field
-      await view.events.click(view.getByLabelText('Group'))
-      await view.events.click(view.getByRole('option', { name: 'Users' }))
-
-      // State field
-      await view.events.click(view.getByLabelText('Priority'))
-      await view.events.click(view.getByRole('option', { name: '2 normal' }))
-
-      // Priority Field
-      await view.events.click(view.getByLabelText('State'))
-      await view.events.click(
-        view.getByRole('option', { name: 'pending reminder' }),
-      )
-
-      // Date selection Field on pending reminder
-      await view.events.click(view.getByText('Pending till'))
-
-      await waitFor(() => expect(view.getByRole('dialog')).toBeInTheDocument())
-
-      const dateCells: Element[] = view.getAllByRole('gridcell', { name: '29' })
-
-      await view.events.click(<Element>dateCells.at(-1))
-
-      // Submission
-      await view.events.click(view.getByRole('button', { name: 'Create' }))
-
-      const calls = await waitForTicketCreateMutationCalls()
-
-      expect(calls.at(-1)?.variables).toEqual({
-        input: {
-          article: {
-            body: 'Test ticket text',
-            cc: undefined,
-            contentType: 'text/html',
-            security: undefined,
-            sender: 'Customer',
-            type: 'phone',
-          },
-          customer: {
-            id: 'gid://zammad/User/2',
-          },
-          groupId: 'gid://zammad/Group/1',
-          objectAttributeValues: [],
-          pendingTime: '2024-11-29T00:00:00.000Z',
-          priorityId: 'gid://zammad/Ticket::Priority/2',
-          stateId: 'gid://zammad/Ticket::State/3',
-          title: 'Test Ticket',
-        },
-      })
-
-      await waitFor(() =>
-        expect(
-          view.getByText('Ticket has been created successfully.'),
-        ).toBeInTheDocument(),
-      )
     })
   })
 
@@ -453,10 +463,8 @@ describe('ticket create view', async () => {
           },
         })
 
-        await waitFor(() =>
-          expect(
-            view.getByText('Ticket has been created successfully.'),
-          ).toBeInTheDocument(),
+        expect(await view.findByRole('alert')).toHaveTextContent(
+          'Ticket has been created successfully.',
         )
       })
     })

+ 53 - 18
app/frontend/apps/desktop/pages/ticket/__tests__/ticket-detail-view/ticket-detail-view-edit.spec.ts

@@ -816,20 +816,22 @@ describe('Ticket detail view', () => {
     })
 
     it('discards complete form with an reply and afterwards only the reply directly', async () => {
-      mockTicketQuery({
-        ticket: createDummyTicket({
-          group: {
-            id: convertToGraphQLId('Group', 1),
-            emailAddress: {
-              name: 'Zammad Helpdesk',
-              emailAddress: 'zammad@localhost',
-            },
-          },
-          defaultPolicy: {
-            update: true,
-            agentReadAccess: true,
+      const ticket = createDummyTicket({
+        group: {
+          id: convertToGraphQLId('Group', 1),
+          emailAddress: {
+            name: 'Zammad Helpdesk',
+            emailAddress: 'zammad@localhost',
           },
-        }),
+        },
+        defaultPolicy: {
+          update: true,
+          agentReadAccess: true,
+        },
+      })
+
+      mockTicketQuery({
+        ticket,
       })
 
       mockTicketArticlesQuery({
@@ -917,9 +919,40 @@ describe('Ticket detail view', () => {
 
       const view = await visitView('/tickets/1')
 
+      const ticketMetaSidebar = within(view.getByLabelText('Content sidebar'))
+      expect(
+        await ticketMetaSidebar.findByLabelText('State'),
+      ).toBeInTheDocument()
+
+      await getTicketUpdatesSubscriptionHandler().trigger({
+        ticketUpdates: {
+          ticket: {
+            ...ticket,
+            state: {
+              ...ticket.state,
+              id: convertToGraphQLId('Ticket::State', 4),
+              name: 'closed',
+              stateType: {
+                ...ticket.state.stateType,
+                id: convertToGraphQLId('Ticket::StateType', 5),
+                name: 'closed',
+              },
+            },
+          },
+        },
+      })
+
+      await waitForNextTick()
+
       // Discard changes inside the reply form
       await view.events.click(view.getByRole('button', { name: 'Add reply' }))
 
+      await waitFor(() =>
+        expect(
+          view.queryByRole('textbox', { name: 'Text' }),
+        ).toBeInTheDocument(),
+      )
+
       await view.events.click(
         await view.findByRole('button', {
           name: 'Discard your unsaved changes',
@@ -934,11 +967,13 @@ describe('Ticket detail view', () => {
         view.getByRole('button', { name: 'Discard Changes' }),
       )
 
-      expect(
-        view.queryByRole('button', {
-          name: 'Discard your unsaved changes',
-        }),
-      ).not.toBeInTheDocument()
+      await waitFor(() => {
+        expect(
+          view.queryByRole('button', {
+            name: 'Discard your unsaved changes',
+          }),
+        ).not.toBeInTheDocument()
+      })
 
       await view.events.click(view.getByRole('button', { name: 'Add reply' }))
 

+ 5 - 0
app/frontend/apps/desktop/router/guards/before/activeTaskbarTab.ts

@@ -1,6 +1,7 @@
 // Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
 
 import type { EnumTaskbarEntity } from '#shared/graphql/types.ts'
+import log from '#shared/utils/log.ts'
 
 import { useUserCurrentTaskbarTabsStore } from '#desktop/entities/user/current/stores/taskbarTabs.ts'
 
@@ -59,6 +60,10 @@ const activeTaskbarTab: NavigationGuard = async (
   //   so it can be used for checking the entity access.
   to.meta.taskbarTabEntityKey = taskbarTabEntityKey
 
+  log.debug(
+    `Route guard for '${to.path}': active taskbar tab with entity key '${taskbarTabEntityKey}'.`,
+  )
+
   next()
 }
 

+ 1 - 1
app/frontend/apps/mobile/pages/ticket/__tests__/ticket-create.spec.ts

@@ -262,7 +262,7 @@ describe('Creating new ticket as agent', () => {
 
       await waitUntil(() => mockTicket.calls.resolve)
 
-      await expect(view.findByRole('alert')).resolves.toHaveTextContent(
+      expect(await view.findByRole('alert')).toHaveTextContent(
         'Ticket has been created successfully.',
       )
 

+ 4 - 1
app/frontend/shared/entities/ticket/composables/useTicketEdit.ts

@@ -53,7 +53,7 @@ export const useTicketEdit = (
 
   const ticketFormRelatedData = computed<Partial<TicketById>>(
     (currentTicketFormRelatedData) => {
-      if (!ticket.value || !form.value) return {}
+      if (!ticket.value) return {}
 
       const newTicketFormRelatedData = (
         TICKET_FORM_RELEVANT_KEYS as Array<keyof TicketById>
@@ -88,8 +88,11 @@ export const useTicketEdit = (
       initialTicketValue.value = {
         id: ticket.value.id,
         owner_id: ownerInternalId === 1 ? null : ownerInternalId,
+        isDefaultFollowUpStateSet: undefined, // the default value for reset situations.
       }
 
+      if (!form.value?.formInitialSettled) return
+
       form.value?.resetForm(
         {
           values: initialTicketValue.value,

+ 3 - 3
app/frontend/tests/graphql/builders/logger.ts

@@ -9,11 +9,11 @@ afterEach(() => {
 })
 
 const logger = {
-  log(...mesages: unknown[]) {
+  log(...messages: unknown[]) {
     if (process.env.VITEST_LOG_GQL_FACTORY) {
-      console.log(...mesages)
+      console.log(...messages)
     } else {
-      logs.push(mesages)
+      logs.push(messages)
     }
   },
   printMockerLog() {

+ 3 - 1
app/frontend/tests/support/components/renderComponent.ts

@@ -182,9 +182,11 @@ interface MockedRouter extends Router {
 
 let routerInitialized = false
 let router: MockedRouter
+const history = createWebHistory(isDesktop ? '/desktop' : '/mobile')
 
 export const getTestPlugins = () => [...plugins]
 export const getTestRouter = () => router
+export const getHistory = () => history
 
 // cannot use "as const" here, because ESLint fails with obscure error :shrug:
 const routerMethods = [
@@ -240,7 +242,7 @@ const initializeRouter = (
   }
 
   router = createRouter({
-    history: createWebHistory(isDesktop ? '/desktop' : '/mobile'),
+    history,
     routes: localRoutes,
   }) as MockedRouter
 

+ 76 - 49
app/frontend/tests/support/components/visitView.ts

@@ -11,15 +11,46 @@ import { useLocaleStore } from '#shared/stores/locale.ts'
 
 import mockApolloClient from '../mock-apollo-client.ts'
 
-import { getTestAppName } from './app.ts'
 import LayoutTestDesktopView from './LayoutTestDesktopView.vue'
 import LayoutTestMobileView from './LayoutTestMobileView.vue'
 import renderComponent, {
+  getHistory,
   getTestRouter,
   type ExtendedMountingOptions,
 } from './renderComponent.ts'
 
-import type { RouteRecordRaw } from 'vue-router'
+import type { NavigationGuard, RouteRecordRaw } from 'vue-router'
+
+const isDesktop = await vi.hoisted(async () => {
+  const { getTestAppName } = await import('./app.ts')
+  return getTestAppName() === 'desktop'
+})
+
+vi.mock('#shared/server/apollo/client.ts', async () => {
+  if (isDesktop) {
+    const { mockedApolloClient } = await import(
+      '#tests/graphql/builders/mocks.ts'
+    )
+    return {
+      clearApolloClientStore: async () => {
+        await mockedApolloClient.clearStore()
+      },
+      getApolloClient: () => mockedApolloClient,
+    }
+  }
+  return {
+    clearApolloClientStore: () => {
+      return Promise.resolve()
+    },
+    getApolloClient: () => {
+      return {
+        cache: {
+          gc: () => [],
+        },
+      }
+    },
+  }
+})
 
 Object.defineProperty(window, 'fetch', {
   value: (path: string) => {
@@ -33,71 +64,65 @@ const html = String.raw
 
 interface VisitViewOptions extends ExtendedMountingOptions<unknown> {
   mockApollo?: boolean
+  setLocale?: boolean
+}
+
+const { routes } = isDesktop
+  ? await import('#desktop/router/index.ts')
+  : await import('#mobile/router/index.ts')
+
+// remove LayoutMain layout, keep only actual content
+if (routes.at(-1)?.name === 'Main') {
+  const [mainRoutes] = routes.splice(routes.length - 1, 1)
+
+  routes.push(...(mainRoutes.children as RouteRecordRaw[]), {
+    path: '/testing-environment',
+    component: {
+      template: '<div></div>',
+    },
+  })
 }
 
-const isDesktop = getTestAppName() === 'desktop'
+// Always reset again before each renderComponent call. With this we
+// have an initial route for each test.
+const routerBeforeGuards: NavigationGuard[] = []
+
+// TODO: disable some stuff for now, because it will break a lot of test.
+// const routerBeforeGuards = [authenticationGuard, permissionGuard]
+if (isDesktop) {
+  // Always reset again before each renderComponent call. With this we
+  // have an initial route for each test.
+
+  //   const { default: systemSetupInfo } = await import(
+  //     '#desktop/router/guards/before/systemSetupInfo.ts'
+  //   )
+  //   routerBeforeGuards.push(systemSetupInfo)
+  const { default: activeTaskbarTab } = await import(
+    '#desktop/router/guards/before/activeTaskbarTab.ts'
+  )
+  routerBeforeGuards.push(activeTaskbarTab)
+}
 
 export const visitView = async (
   href: string,
   // rely on new way to mock apollo in desktop by default
   options: VisitViewOptions = { mockApollo: !isDesktop, setLocale: isDesktop },
 ) => {
-  const { routes } = isDesktop
-    ? await import('#desktop/router/index.ts')
-    : await import('#mobile/router/index.ts')
-
   if (options.mockApollo) {
-    vi.mock('#shared/server/apollo/client.ts', () => {
-      return {
-        clearApolloClientStore: () => {
-          return Promise.resolve()
-        },
-        getApolloClient: () => {
-          return {
-            cache: {
-              gc: () => [],
-            },
-          }
-        },
-      }
-    })
-
     mockApolloClient([])
   } else if (isDesktop) {
     // automocking is enabled when this file is imported because it happens on the top level
     await import('#tests/graphql/builders/mocks.ts')
   }
 
-  // remove LayoutMain layout, keep only actual content
-  if (routes.at(-1)?.name === 'Main') {
-    const [mainRoutes] = routes.splice(routes.length - 1, 1)
-
-    routes.push(...(mainRoutes.children as RouteRecordRaw[]), {
-      path: '/testing-environment',
-      component: {
-        template: '<div></div>',
-      },
-    })
-  }
-
-  // TODO: disable some stuff for now, because it will break a lot of test.
-  // const routerBeforeGuards = [authenticationGuard, permissionGuard]
-  const routerBeforeGuards = []
-  if (isDesktop) {
-    //   const { default: systemSetupInfo } = await import(
-    //     '#desktop/router/guards/before/systemSetupInfo.ts'
-    //   )
-    //   routerBeforeGuards.push(systemSetupInfo)
-    const { default: activeTaskbarTab } = await import(
-      '#desktop/router/guards/before/activeTaskbarTab.ts'
-    )
-    routerBeforeGuards.push(activeTaskbarTab)
-  }
-
+  // Reset the initial navigation state and the initial route.
   const testKey = random()
 
   useNotifications().clearAllNotifications()
 
+  const history = getHistory()
+  history.replace(href)
+
   const view = renderComponent(
     {
       template: html` <LayoutTest${isDesktop
@@ -127,7 +152,9 @@ export const visitView = async (
 
   const router = getTestRouter()
 
-  await router.replace(href)
+  // we don't call router.replace(href) here because it's called by the router plugin when mounting
+  // and it gets the correct href from the `history` object
+  await router.isReady()
 
   if (options.setLocale) {
     await useLocaleStore().setLocale()

Some files were not shown because too many files changed in this diff