frontend: polish app launch and release experience
This commit is contained in:
@@ -1,12 +1,19 @@
|
||||
import { describe, expect, it, vi, beforeEach } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { flushPromises, mount } from '@vue/test-utils'
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { PackageState, type PackageDataEntry } from '@/types/api'
|
||||
import { useAppLauncherStore } from '@/stores/appLauncher'
|
||||
import { useServerStore } from '@/stores/server'
|
||||
import AppIconGrid from '../AppIconGrid.vue'
|
||||
|
||||
const mockWindowOpen = vi.fn()
|
||||
|
||||
vi.mock('@/api/rpc-client', () => ({
|
||||
rpcClient: {
|
||||
call: vi.fn().mockResolvedValue({ credentials: [] }),
|
||||
},
|
||||
}))
|
||||
|
||||
vi.stubGlobal('open', mockWindowOpen)
|
||||
|
||||
function makePkg(id: string): PackageDataEntry {
|
||||
@@ -31,8 +38,12 @@ function makePkg(id: string): PackageDataEntry {
|
||||
}
|
||||
|
||||
describe('AppIconGrid', () => {
|
||||
let pinia: ReturnType<typeof createPinia>
|
||||
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia())
|
||||
vi.useRealTimers()
|
||||
pinia = createPinia()
|
||||
setActivePinia(pinia)
|
||||
vi.clearAllMocks()
|
||||
localStorage.clear()
|
||||
Object.defineProperty(window, 'innerWidth', {
|
||||
@@ -51,14 +62,32 @@ describe('AppIconGrid', () => {
|
||||
const wrapper = mount(AppIconGrid, {
|
||||
props: { apps: [['lnd', makePkg('lnd')]] },
|
||||
global: {
|
||||
plugins: [createPinia()],
|
||||
plugins: [pinia],
|
||||
},
|
||||
})
|
||||
|
||||
await wrapper.get('.app-icon-item').trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
expect(mockWindowOpen).not.toHaveBeenCalled()
|
||||
expect(useAppLauncherStore().panelAppId).toBe('lnd')
|
||||
expect(useAppLauncherStore(pinia).panelAppId).toBe('lnd')
|
||||
})
|
||||
|
||||
it('shows File Browser credentials before launch even when backend returns no credentials', async () => {
|
||||
const wrapper = mount(AppIconGrid, {
|
||||
props: { apps: [['filebrowser', makePkg('filebrowser')]] },
|
||||
global: {
|
||||
plugins: [pinia],
|
||||
},
|
||||
})
|
||||
|
||||
await wrapper.get('.app-icon-item').trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
expect(wrapper.text()).toContain('File Browser credentials')
|
||||
expect(wrapper.text()).toContain('Username')
|
||||
expect(wrapper.text()).toContain('admin')
|
||||
expect(useAppLauncherStore(pinia).panelAppId).toBeNull()
|
||||
})
|
||||
|
||||
it('routes desktop new-tab apps through app session on mobile', async () => {
|
||||
@@ -71,13 +100,78 @@ describe('AppIconGrid', () => {
|
||||
const wrapper = mount(AppIconGrid, {
|
||||
props: { apps: [['gitea', makePkg('gitea')]] },
|
||||
global: {
|
||||
plugins: [createPinia()],
|
||||
plugins: [pinia],
|
||||
},
|
||||
})
|
||||
|
||||
await wrapper.get('.app-icon-item').trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
expect(mockWindowOpen).not.toHaveBeenCalled()
|
||||
expect(useAppLauncherStore().panelAppId).toBeNull()
|
||||
expect(useAppLauncherStore(pinia).panelAppId).toBeNull()
|
||||
})
|
||||
|
||||
it('shows backend uninstall stage while an app is removing', () => {
|
||||
const pkg = makePkg('indeedhub')
|
||||
pkg.state = PackageState.Removing
|
||||
pkg['uninstall-stage'] = 'Stopping containers (2/7)'
|
||||
useServerStore(pinia).uninstallingApps.add('indeedhub')
|
||||
|
||||
const wrapper = mount(AppIconGrid, {
|
||||
props: { apps: [['indeedhub', pkg]] },
|
||||
global: {
|
||||
plugins: [pinia],
|
||||
},
|
||||
})
|
||||
|
||||
expect(wrapper.text()).toContain('Stopping containers (2/7)')
|
||||
})
|
||||
|
||||
it('supports legacy underscore uninstall stage data', () => {
|
||||
const pkg = makePkg('indeedhub')
|
||||
pkg.state = PackageState.Removing
|
||||
;(pkg as PackageDataEntry & { uninstall_stage?: string }).uninstall_stage = 'Removing app data'
|
||||
useServerStore(pinia).uninstallingApps.add('indeedhub')
|
||||
|
||||
const wrapper = mount(AppIconGrid, {
|
||||
props: { apps: [['indeedhub', pkg]] },
|
||||
global: {
|
||||
plugins: [pinia],
|
||||
},
|
||||
})
|
||||
|
||||
expect(wrapper.text()).toContain('Removing app data')
|
||||
})
|
||||
|
||||
it('opens app details on long press without launching the app', async () => {
|
||||
vi.useFakeTimers()
|
||||
const wrapper = mount(AppIconGrid, {
|
||||
props: { apps: [['lnd', makePkg('lnd')]] },
|
||||
global: {
|
||||
plugins: [pinia],
|
||||
},
|
||||
})
|
||||
|
||||
const icon = wrapper.get('.app-icon-item')
|
||||
await icon.trigger('pointerdown')
|
||||
vi.advanceTimersByTime(550)
|
||||
await icon.trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
expect(wrapper.emitted('goToApp')).toEqual([['lnd']])
|
||||
expect(useAppLauncherStore(pinia).panelAppId).toBeNull()
|
||||
})
|
||||
|
||||
it('opens app details from the keyboard options shortcut', async () => {
|
||||
const wrapper = mount(AppIconGrid, {
|
||||
props: { apps: [['lnd', makePkg('lnd')]] },
|
||||
global: {
|
||||
plugins: [pinia],
|
||||
},
|
||||
})
|
||||
|
||||
await wrapper.get('.app-icon-item').trigger('keydown.space')
|
||||
|
||||
expect(wrapper.emitted('goToApp')).toEqual([['lnd']])
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user