From ac02a13f5a4a270616edef733131cc6e741a1fe5 Mon Sep 17 00:00:00 2001 From: Chris Rohr <51920+chrisrohr@users.noreply.github.com> Date: Fri, 24 Feb 2023 15:50:54 -0500 Subject: [PATCH] Add ui for application errors (#143) Closes #127 --- ui/src/layouts/MainLayout.vue | 15 ++- ui/src/pages/ErrorsPage.vue | 91 +++++++++++++++++++ ui/src/router/routes.js | 3 +- ui/src/stores/errorStore.js | 46 ++++++++++ .../vitest/__tests__/pages/ErrorsPage.spec.js | 19 ++++ .../__tests__/stores/errorStore.spec.js | 61 +++++++++++++ .../vitest/__tests__/stores/hostStore.spec.js | 8 +- 7 files changed, 234 insertions(+), 9 deletions(-) create mode 100644 ui/src/pages/ErrorsPage.vue create mode 100644 ui/src/stores/errorStore.js create mode 100644 ui/test/vitest/__tests__/pages/ErrorsPage.spec.js create mode 100644 ui/test/vitest/__tests__/stores/errorStore.spec.js diff --git a/ui/src/layouts/MainLayout.vue b/ui/src/layouts/MainLayout.vue index 8e2742b8..b029e829 100644 --- a/ui/src/layouts/MainLayout.vue +++ b/ui/src/layouts/MainLayout.vue @@ -8,7 +8,7 @@ round icon="menu" aria-label="Menu" - @click="drawer = !drawer" + @click="drawer.value = !drawer.value" class="text-grey-4" v-if="authStore.isLoggedIn" /> @@ -44,8 +44,8 @@ show-if-above :mini="miniState" - @mouseover="miniState = false" - @mouseout="miniState = true" + @mouseover="miniState.value = false" + @mouseout="miniState.value = true" bordered :width="200" @@ -108,6 +108,15 @@ + + + + + + Errors + + + diff --git a/ui/src/pages/ErrorsPage.vue b/ui/src/pages/ErrorsPage.vue new file mode 100644 index 00000000..afb7a5a7 --- /dev/null +++ b/ui/src/pages/ErrorsPage.vue @@ -0,0 +1,91 @@ + + + diff --git a/ui/src/router/routes.js b/ui/src/router/routes.js index 0ad915ec..449d8344 100644 --- a/ui/src/router/routes.js +++ b/ui/src/router/routes.js @@ -11,7 +11,8 @@ const routes = [ { path: '/audits', name: 'AuditRecordsPage', component: () => import('pages/AuditRecordsPage.vue') }, { path: '/tasks', name: 'ManualTaskPage', component: () => import('pages/ManualTaskPage.vue') }, { path: '/builds', name: 'BuildsPage', component: () => import('pages/BuildsPage.vue') }, - { path: '/hostConfig', name: 'HostConfigurationPage', component: () => import('pages/HostConfigurationPage.vue') } + { path: '/hostConfig', name: 'HostConfigurationPage', component: () => import('pages/HostConfigurationPage.vue') }, + { path: '/errors', name: 'ErrorsPage', component: () => import('pages/ErrorsPage.vue') } ] }, diff --git a/ui/src/stores/errorStore.js b/ui/src/stores/errorStore.js new file mode 100644 index 00000000..a96d3281 --- /dev/null +++ b/ui/src/stores/errorStore.js @@ -0,0 +1,46 @@ +import { defineStore } from 'pinia' +import { api } from 'src/boot/axios' +import { ref } from 'vue' + +export const useErrorStore = defineStore('error', () => { + const errors = ref([]) + const loading = ref(false) + const pagination = ref({ + page: 1, + rowsPerPage: 25, + rowsNumber: 0 + }) + const status = ref('UNRESOLVED') + + async function load (props) { + loading.value = true + + if (props !== undefined) { + pagination.value.page = props.pagination.page + pagination.value.rowsPerPage = props.pagination.rowsPerPage + } + + const params = { + pageNumber: pagination.value.page, + pageSize: pagination.value.rowsPerPage, + status: status.value + } + + const response = await api.get('/kiwi/application-errors', { params }) + errors.value = response.data.items + pagination.value.rowsNumber = response.data.totalCount + loading.value = false + } + + function resolve (id) { + return api.put(`/kiwi/application-errors/resolve/${id}`) + .then(() => load()) + } + + function resolveAll () { + return api.put('/kiwi/application-errors/resolve') + .then(() => load()) + } + + return { errors, loading, pagination, load, resolve, resolveAll, status } +}) diff --git a/ui/test/vitest/__tests__/pages/ErrorsPage.spec.js b/ui/test/vitest/__tests__/pages/ErrorsPage.spec.js new file mode 100644 index 00000000..1f460781 --- /dev/null +++ b/ui/test/vitest/__tests__/pages/ErrorsPage.spec.js @@ -0,0 +1,19 @@ +import { installQuasar } from '@quasar/quasar-app-extension-testing-unit-vitest' +import { shallowMount } from '@vue/test-utils' +import { describe, expect, it } from 'vitest' +import ErrorsPage from 'pages/ErrorsPage.vue' +import { createTestingPinia } from '@pinia/testing' + +installQuasar() + +describe('ErrorsPage', () => { + it('should mount the errors page without errors', () => { + const wrapper = shallowMount(ErrorsPage, { + global: { + plugins: [createTestingPinia()] + } + }) + + expect(wrapper).toBeTruthy() + }) +}) diff --git a/ui/test/vitest/__tests__/stores/errorStore.spec.js b/ui/test/vitest/__tests__/stores/errorStore.spec.js new file mode 100644 index 00000000..55aa2681 --- /dev/null +++ b/ui/test/vitest/__tests__/stores/errorStore.spec.js @@ -0,0 +1,61 @@ +import { setActivePinia, createPinia } from 'pinia' +import { useErrorStore } from 'src/stores/errorStore' +import { beforeEach, vi, describe, it, expect } from 'vitest' +import { api } from 'src/boot/axios' + +beforeEach(() => { + setActivePinia(createPinia()) + + vi.mock('src/boot/axios', () => { + const api = { + get: vi.fn().mockImplementation(() => Promise.resolve({ data: { name: 'dev' } })), + put: vi.fn().mockImplementation(() => Promise.resolve(true)) + } + + return { api } + }) +}) + +describe('load', () => { + beforeEach(() => { + setActivePinia(createPinia()) + }) + + it('should use defaults', () => { + const errorStore = useErrorStore() + + errorStore.load() + + expect(api.get).toHaveBeenCalled() + expect(api.get).toHaveBeenCalledWith('/kiwi/application-errors', { params: { pageNumber: 1, pageSize: 25, status: 'UNRESOLVED' } }) + }) + + it('should use provided props', () => { + const errorStore = useErrorStore() + + errorStore.load({ pagination: { page: 5, rowsPerPage: 50 } }) + + expect(api.get).toHaveBeenCalled() + expect(api.get).toHaveBeenCalledWith('/kiwi/application-errors', { params: { pageNumber: 5, pageSize: 50, status: 'UNRESOLVED' } }) + }) +}) + +describe('resolve', () => { + it('should call resolve for given id', () => { + const errorStore = useErrorStore() + + errorStore.resolve(1) + + expect(api.put).toHaveBeenCalledWith('/kiwi/application-errors/resolve/1') + }) +}) + +describe('resolveAll', () => { + it('should call resolveAll', () => { + const errorStore = useErrorStore() + + errorStore.resolveAll() + + expect(api.put).toHaveBeenCalledWith('/kiwi/application-errors/resolve') + }) +}) diff --git a/ui/test/vitest/__tests__/stores/hostStore.spec.js b/ui/test/vitest/__tests__/stores/hostStore.spec.js index d3ad736d..2cc27818 100644 --- a/ui/test/vitest/__tests__/stores/hostStore.spec.js +++ b/ui/test/vitest/__tests__/stores/hostStore.spec.js @@ -1,14 +1,14 @@ import { setActivePinia, createPinia } from 'pinia' import { useHostStore } from 'src/stores/hostStore' -import { vi } from "vitest"; -import { api } from "src/boot/axios"; +import { vi, beforeEach, describe, it, expect } from 'vitest' +import { api } from 'src/boot/axios' beforeEach(() => { setActivePinia(createPinia()) vi.mock('src/boot/axios', () => { const api = { - get: vi.fn().mockImplementation(() => Promise.resolve({data: { name: 'dev' }})), + get: vi.fn().mockImplementation(() => Promise.resolve({ data: { name: 'dev' } })), post: vi.fn().mockImplementation(() => Promise.resolve(true)), delete: vi.fn().mockImplementation(() => Promise.resolve(true)), put: vi.fn().mockImplementation(() => Promise.resolve(true)) @@ -19,7 +19,6 @@ beforeEach(() => { }) describe('load', () => { - it('should load hosts', async () => { const hostStore = useHostStore() hostStore.environmentFilter = { value: 1 } @@ -29,7 +28,6 @@ describe('load', () => { expect(api.get).toHaveBeenCalled() expect(api.get).toHaveBeenCalledWith('/host/1', { componentFilter: '' }) }) - }) describe('create', () => {