Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions ui/src/__tests__/system-task-view.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { mount, flushPromises } from '@vue/test-utils'
import { nextTick } from 'vue'

// Runner list returned by GET rest/system/task.
const RUNNERS = [
{ key: 'importCatalogResource', label: 'ImportCatalogStatus', type: 'node', stats: { total: 3, running: 1, succeeded: 1, failed: 1 } },
{ key: 'subRunner', label: 'SubStatus', type: 'subscription', stats: { total: 0, running: 0, succeeded: 0, failed: 0 } },
]

// Single shared fake data table so we can assert load() calls across the test.
const dtStub = {
items: { value: [] }, totalItems: { value: 0 }, loading: { value: false },
search: { value: '' }, load: vi.fn(), loadAll: vi.fn(),
}
const useDataTableSpy = vi.fn(() => dtStub)

vi.mock('@ligoj/host', () => ({
useApi: () => ({ get: vi.fn().mockResolvedValue(RUNNERS) }),
useAppStore: () => ({ setBreadcrumbs: vi.fn() }),
useI18nStore: () => ({ t: (k) => k, locale: 'en' }),
useDataTable: (...args) => useDataTableSpy(...args),
NodeIcon: { name: 'NodeIcon', props: ['node'], template: '<i class="nicon" />' },
LjPageHeader: { template: '<div class="hdr"><slot name="subtitle" /></div>' },
LjDialog: { props: ['modelValue'], template: '<div v-if="modelValue" class="ljdialog"><slot /><slot name="footer" /></div>' },
LjButton: { template: '<button><slot /></button>' },
LjSegmented: { name: 'LjSegmented', props: ['modelValue', 'options'], emits: ['update:modelValue'], template: '<div class="seg" />' },
VibrantDataTable: { name: 'VibrantDataTable', props: ['headers', 'items', 'itemsLength', 'loading', 'defaultSort', 'defaultOrder'], template: '<div class="vdt" />' },
}))

import SystemTaskView from '../views/SystemTaskView.vue'

function mountView() {
return mount(SystemTaskView, { global: { stubs: { 'v-icon': true, 'v-progress-circular': true, 'v-tooltip': true } } })
}

describe('SystemTaskView', () => {
beforeEach(() => {
useDataTableSpy.mockClear()
dtStub.load.mockClear()
})

it('renders one card per runner, grouped, with stats', async () => {
const w = mountView()
await flushPromises()
const cards = w.findAll('.card')
expect(cards).toHaveLength(2)
// Two non-empty sections (node + subscription).
expect(w.findAll('.section')).toHaveLength(2)
// The node runner shows its total (3) and the succeeded segment is 1/3 wide.
expect(cards[0].text()).toContain('3')
const succeeded = cards[0].find('.seg.succeeded')
expect(succeeded.attributes('style')).toContain('33.3')
})

it('opens the dialog and wires useDataTable on the runner endpoint', async () => {
const w = mountView()
await flushPromises()
expect(w.find('.ljdialog').exists()).toBe(false)

await w.findAll('.card')[0].trigger('click')
expect(w.find('.ljdialog').exists()).toBe(true)
expect(useDataTableSpy).toHaveBeenCalledTimes(1)
const [endpoint, opts] = useDataTableSpy.mock.calls[0]
expect(endpoint).toBe('system/task/importCatalogResource')
expect(opts.defaultSort).toBe('start')
expect(opts.defaultOrder).toBe('desc')
})

it('passes the status filter through extraParams and reloads', async () => {
const w = mountView()
await flushPromises()
await w.findAll('.card')[0].trigger('click')

const opts = useDataTableSpy.mock.calls[0][1]
expect(opts.extraParams()).toEqual({})

// Change the status filter -> extraParams reflects it and the table reloads.
await w.findComponent({ name: 'LjSegmented' }).vm.$emit('update:modelValue', 'running')
await nextTick()
expect(opts.extraParams()).toEqual({ status: 'running' })
expect(dtStub.load).toHaveBeenCalled()
})
})
26 changes: 26 additions & 0 deletions ui/src/i18n/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,32 @@ export default {
'system.config.sourcePrefix': 'Source: {source}',
'system.config.sourceOverridden': '{base} — overridden',

// System → Tasks
'system.task.title': 'Tasks',
'system.task.countLabel': 'task runners',
'system.task.section.node': 'Node runners',
'system.task.section.subscription': 'Subscription runners',
'system.task.section.other': 'Other runners',
'system.task.type.node': 'Node',
'system.task.type.subscription': 'Subscription',
'system.task.type.other': 'Other',
'system.task.statTotal': 'total',
'system.task.status.running': 'Running',
'system.task.status.succeeded': 'Succeeded',
'system.task.status.failed': 'Failed',
'system.task.colStatus': 'Status',
'system.task.colAuthor': 'Author',
'system.task.colStart': 'Started',
'system.task.colDuration': 'Duration',
'system.task.colLocked': 'Locked entity',
'system.task.filter.all': 'All',
'system.task.noTask': 'No task',
'system.task.stillRunning': 'Still running',
'system.task.unit.d': 'd',
'system.task.unit.h': 'h',
'system.task.unit.m': 'min',
'system.task.unit.s': 's',

// System → Cache
'system.cache.title': 'Caches',
'system.cache.headerName': 'Cache',
Expand Down
26 changes: 26 additions & 0 deletions ui/src/i18n/fr.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,32 @@ export default {
'system.config.sourcePrefix': 'Source : {source}',
'system.config.sourceOverridden': '{base} — surchargée',

// Système → Tâches
'system.task.title': 'Tâches',
'system.task.countLabel': 'exécuteurs de tâches',
'system.task.section.node': 'Exécuteurs Nœud',
'system.task.section.subscription': 'Exécuteurs Souscription',
'system.task.section.other': 'Autres exécuteurs',
'system.task.type.node': 'Nœud',
'system.task.type.subscription': 'Souscription',
'system.task.type.other': 'Autre',
'system.task.statTotal': 'total',
'system.task.status.running': 'En cours',
'system.task.status.succeeded': 'Réussie',
'system.task.status.failed': 'Échouée',
'system.task.colStatus': 'Statut',
'system.task.colAuthor': 'Auteur',
'system.task.colStart': 'Début',
'system.task.colDuration': 'Durée',
'system.task.colLocked': 'Entité verrouillée',
'system.task.filter.all': 'Toutes',
'system.task.noTask': 'Aucune tâche',
'system.task.stillRunning': 'Toujours en cours',
'system.task.unit.d': 'j',
'system.task.unit.h': 'h',
'system.task.unit.m': 'min',
'system.task.unit.s': 's',

// Système → Cache
'system.cache.title': 'Caches',
'system.cache.headerName': 'Cache',
Expand Down
2 changes: 2 additions & 0 deletions ui/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ import SystemPluginView from './views/SystemPluginView.vue'
import SystemNodeView from './views/SystemNodeView.vue'
import SystemCacheView from './views/SystemCacheView.vue'
import SystemBenchView from './views/SystemBenchView.vue'
import SystemTaskView from './views/SystemTaskView.vue'

import ApiHomeView from './views/ApiHomeView.vue'
import ApiTokenView from './views/ApiTokenView.vue'
Expand Down Expand Up @@ -114,6 +115,7 @@ const routes = [
{ path: '/system/node', name: 'ui-system-node', component: SystemNodeView },
{ path: '/system/cache', name: 'ui-system-cache', component: SystemCacheView },
{ path: '/system/bench', name: 'ui-system-bench', component: SystemBenchView },
{ path: '/system/task', name: 'ui-system-task', component: SystemTaskView },

{ path: '/api', name: 'ui-api', component: ApiHomeView },
{ path: '/api/token', name: 'ui-api-token', component: ApiTokenView },
Expand Down
Loading