From 7a53282122322c2fb94824375922062a2b447622 Mon Sep 17 00:00:00 2001 From: jorgep Date: Fri, 20 Dec 2024 12:00:32 +0100 Subject: [PATCH 1/5] feat: refs #7056 update route meta information and add FormModel tests --- .../__tests__/components/FormModel.spec.js | 145 ++++++++++++++++++ test/vitest/helper.js | 4 +- 2 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 test/vitest/__tests__/components/FormModel.spec.js diff --git a/test/vitest/__tests__/components/FormModel.spec.js b/test/vitest/__tests__/components/FormModel.spec.js new file mode 100644 index 000000000..a608d26d6 --- /dev/null +++ b/test/vitest/__tests__/components/FormModel.spec.js @@ -0,0 +1,145 @@ +import { describe, expect, it, beforeAll, vi, afterAll } from 'vitest'; +import { createWrapper, axios } from 'app/test/vitest/helper'; +import FormModel from 'src/components/FormModel.vue'; + +describe('FormModel', () => { + const model = 'mockModel'; + const url = 'mockUrl'; + const formInitialData = { mockKey: 'mockVal' }; + + describe('modelValue', () => { + it('should use the provided model', () => { + const { vm } = mount({ propsData: { model: 'mockModel' } }); + expect(vm.modelValue).toBe('mockModel'); + }); + + it('should use the route meta title when model is not provided', () => { + const { vm } = mount({}); + expect(vm.modelValue).toBe('formModel_mockTitle'); + }); + }); + + describe('onMounted()', () => { + let mockGet; + + beforeAll(() => { + mockGet = vi.spyOn(axios, 'get').mockResolvedValue({ data: {} }); + }); + + afterAll(() => { + mockGet.mockRestore(); + }); + + it('should not fetch when has formInitialData', () => { + mount({ propsData: { url, model, autoLoad: true, formInitialData } }); + expect(mockGet).not.toHaveBeenCalled(); + }); + + it('should fetch when there is url and auto-load', () => { + mount({ propsData: { url, model, autoLoad: true } }); + expect(mockGet).toHaveBeenCalled(); + }); + + it('should not observe changes', () => { + const { vm } = mount({ + propsData: { url, model, observeFormChanges: false, formInitialData }, + }); + + expect(vm.hasChanges).toBe(true); + vm.reset(); + expect(vm.hasChanges).toBe(true); + }); + + it('should observe changes', async () => { + const { vm } = mount({ + propsData: { url, model, formInitialData }, + }); + vm.state.set(model, { mockKey: 'mockVal' }); + expect(vm.hasChanges).toBe(false); + + vm.formData.mockKey = 'newVal'; + await vm.$nextTick(); + expect(vm.hasChanges).toBe(true); + }); + }); + + describe.skip('watch()', () => { + let wrapper; + let vm; + + beforeAll(() => { + wrapper = mount({ + propsData: { url, model, formInitialData }, + }); + vm = wrapper.vm; + }); + + it('should call updateAndEmit when arrayData.store.data changes', async () => { + const updateAndEmitSpy = vi.spyOn(vm, 'updateAndEmit'); + await vm.$nextTick(); + console.log('vm.arrayData.store.data', vm.arrayData.store.data); + vm.arrayData.store.data = { newData: 'newValue' }; + await vm.$nextTick(); + vm.arrayData.store.data = { newData: 'anotherVal' }; + await vm.$nextTick(); + + expect(updateAndEmitSpy).toHaveBeenCalled(); + }); + + it('should call reset and fetch when $props.url or $props.filter changes', async () => { + const resetSpy = vi.spyOn(vm, 'reset'); + const fetchSpy = vi.spyOn(vm, 'fetch'); + + wrapper.setProps({ url: 'newMockUrl' }); + await wrapper.vm.$nextTick(); + expect(resetSpy).toHaveBeenCalled(); + expect(fetchSpy).toHaveBeenCalled(); + }); + }); + + describe('trimData()', () => { + let vm; + beforeAll(() => { + vm = mount({}).vm; + }); + + it('should trim whitespace from string values', () => { + const data = { key1: ' value1 ', key2: ' value2 ' }; + const trimmedData = vm.trimData(data); + expect(trimmedData).toEqual({ key1: 'value1', key2: 'value2' }); + }); + + it('should not modify non-string values', () => { + const data = { key1: 123, key2: true, key3: null, key4: undefined }; + const trimmedData = vm.trimData(data); + expect(trimmedData).toEqual(data); + }); + }); + + describe('onUnmounted()', () => { + it('should restore original data in the store if changes were made but not saved', async () => { + const wrapper = mount({ propsData: { model, formInitialData } }); + const vm = wrapper.vm; + vm.formData.mockKey = 'newVal'; + await vm.$nextTick(); + await wrapper.unmount(); + expect(vm.state.get(model)).toEqual(formInitialData); + }); + + it('should clear the store on unmount if clearStoreOnUnmount is true', async () => { + const wrapper = mount({ + propsData: { model, formInitialData, clearStoreOnUnmount: true }, + }); + const vm = wrapper.vm; + vm.hasChanges = false; + await wrapper.unmount(); + expect(vm.state.get(model)).toBeUndefined(); + }); + }); +}); + +function mount({ propsData = {} }) { + return createWrapper(FormModel, { + propsData, + }); +} diff --git a/test/vitest/helper.js b/test/vitest/helper.js index ce057c7c3..1e693ab63 100644 --- a/test/vitest/helper.js +++ b/test/vitest/helper.js @@ -26,7 +26,7 @@ vi.mock('vue-router', () => ({ params: { id: 1, }, - meta: { moduleName: 'mockName' }, + meta: { moduleName: 'mockModuleName' }, matched: [{ path: 'mockName/list' }], }, }, @@ -35,7 +35,7 @@ vi.mock('vue-router', () => ({ matched: [], query: {}, params: {}, - meta: { moduleName: 'mockName' }, + meta: { moduleName: 'mockModuleName', title: 'mockTitle', name: 'mockName' }, path: 'mockSection/list', }), onBeforeRouteLeave: () => {}, From 48742289fef97cf9d4a2e9d4b11e114c848d8ca7 Mon Sep 17 00:00:00 2001 From: jorgep Date: Mon, 23 Dec 2024 09:45:15 +0100 Subject: [PATCH 2/5] feat: refs #7056 add tests in FormModel --- .../__tests__/components/FormModel.spec.js | 77 +++++++------------ 1 file changed, 27 insertions(+), 50 deletions(-) diff --git a/test/vitest/__tests__/components/FormModel.spec.js b/test/vitest/__tests__/components/FormModel.spec.js index a608d26d6..469e52770 100644 --- a/test/vitest/__tests__/components/FormModel.spec.js +++ b/test/vitest/__tests__/components/FormModel.spec.js @@ -54,46 +54,13 @@ describe('FormModel', () => { const { vm } = mount({ propsData: { url, model, formInitialData }, }); - vm.state.set(model, { mockKey: 'mockVal' }); + vm.state.set(model, formInitialData); expect(vm.hasChanges).toBe(false); vm.formData.mockKey = 'newVal'; await vm.$nextTick(); expect(vm.hasChanges).toBe(true); - }); - }); - - describe.skip('watch()', () => { - let wrapper; - let vm; - - beforeAll(() => { - wrapper = mount({ - propsData: { url, model, formInitialData }, - }); - vm = wrapper.vm; - }); - - it('should call updateAndEmit when arrayData.store.data changes', async () => { - const updateAndEmitSpy = vi.spyOn(vm, 'updateAndEmit'); - await vm.$nextTick(); - console.log('vm.arrayData.store.data', vm.arrayData.store.data); - vm.arrayData.store.data = { newData: 'newValue' }; - await vm.$nextTick(); - vm.arrayData.store.data = { newData: 'anotherVal' }; - await vm.$nextTick(); - - expect(updateAndEmitSpy).toHaveBeenCalled(); - }); - - it('should call reset and fetch when $props.url or $props.filter changes', async () => { - const resetSpy = vi.spyOn(vm, 'reset'); - const fetchSpy = vi.spyOn(vm, 'fetch'); - - wrapper.setProps({ url: 'newMockUrl' }); - await wrapper.vm.$nextTick(); - expect(resetSpy).toHaveBeenCalled(); - expect(fetchSpy).toHaveBeenCalled(); + vm.formData.mockKey = 'mockVal'; }); }); @@ -116,24 +83,34 @@ describe('FormModel', () => { }); }); - describe('onUnmounted()', () => { - it('should restore original data in the store if changes were made but not saved', async () => { - const wrapper = mount({ propsData: { model, formInitialData } }); - const vm = wrapper.vm; - vm.formData.mockKey = 'newVal'; - await vm.$nextTick(); - await wrapper.unmount(); - expect(vm.state.get(model)).toEqual(formInitialData); + describe('save()', async () => { + it('should not call if there are not changes', async () => { + const { vm } = mount({ propsData: { url, model } }); + + await vm.save(); + expect(vm.hasChanges).toBe(false); }); - it('should clear the store on unmount if clearStoreOnUnmount is true', async () => { - const wrapper = mount({ - propsData: { model, formInitialData, clearStoreOnUnmount: true }, + it('should call axios.patch with the right data', async () => { + const spy = vi.spyOn(axios, 'patch').mockResolvedValue({ data: {} }); + const { vm } = mount({ propsData: { url, model, formInitialData } }); + vm.formData.mockKey = 'newVal'; + await vm.$nextTick(); + await vm.save(); + expect(spy).toHaveBeenCalled(); + vm.formData.mockKey = 'mockVal'; + }); + + it('should call axios.post with the right data', async () => { + const spy = vi.spyOn(axios, 'post').mockResolvedValue({ data: {} }); + const { vm } = mount({ + propsData: { url, model, formInitialData, urlCreate: 'mockUrlCreate' }, }); - const vm = wrapper.vm; - vm.hasChanges = false; - await wrapper.unmount(); - expect(vm.state.get(model)).toBeUndefined(); + vm.formData.mockKey = 'newVal'; + await vm.$nextTick(); + await vm.save(); + expect(spy).toHaveBeenCalled(); + vm.formData.mockKey = 'mockVal'; }); }); }); From 093034c523e561622549decdacc8e8678b6a22dc Mon Sep 17 00:00:00 2001 From: jorgep Date: Mon, 23 Dec 2024 13:01:59 +0100 Subject: [PATCH 3/5] chore: refs #7056 move test --- .../components => src/components/__tests__}/FormModel.spec.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {test/vitest/__tests__/components => src/components/__tests__}/FormModel.spec.js (100%) diff --git a/test/vitest/__tests__/components/FormModel.spec.js b/src/components/__tests__/FormModel.spec.js similarity index 100% rename from test/vitest/__tests__/components/FormModel.spec.js rename to src/components/__tests__/FormModel.spec.js From 7cb4dfe16a51e56f86a4f19ceb20e0f0ec6d43f3 Mon Sep 17 00:00:00 2001 From: jorgep Date: Mon, 23 Dec 2024 13:34:34 +0100 Subject: [PATCH 4/5] test: refs #7056 add save function and reload data tests for FormModel component --- src/components/__tests__/FormModel.spec.js | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/components/__tests__/FormModel.spec.js b/src/components/__tests__/FormModel.spec.js index 469e52770..66157d567 100644 --- a/src/components/__tests__/FormModel.spec.js +++ b/src/components/__tests__/FormModel.spec.js @@ -112,6 +112,33 @@ describe('FormModel', () => { expect(spy).toHaveBeenCalled(); vm.formData.mockKey = 'mockVal'; }); + + it('should use the saveFn', async () => { + const { vm } = mount({ + propsData: { url, model, formInitialData, saveFn: () => {} }, + }); + const spyPatch = vi.spyOn(axios, 'patch').mockResolvedValue({ data: {} }); + const spySaveFn = vi.spyOn(vm.$props, 'saveFn'); + + vm.formData.mockKey = 'newVal'; + await vm.$nextTick(); + await vm.save(); + expect(spyPatch).not.toHaveBeenCalled(); + expect(spySaveFn).toHaveBeenCalled(); + vm.formData.mockKey = 'mockVal'; + }); + + it('should reload the data after save', async () => { + const { vm } = mount({ + propsData: { url, model, formInitialData, reload: true }, + }); + vi.spyOn(axios, 'patch').mockResolvedValue({ data: {} }); + + vm.formData.mockKey = 'newVal'; + await vm.$nextTick(); + await vm.save(); + vm.formData.mockKey = 'mockVal'; + }); }); }); From f4aee047ff7f77e2dfbbc3f5dc10a049a2e8cd58 Mon Sep 17 00:00:00 2001 From: jorgep Date: Mon, 23 Dec 2024 14:21:36 +0100 Subject: [PATCH 5/5] test: refs #7056 update FormModel.spec.js to use dynamic model value in tests --- src/components/__tests__/FormModel.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/__tests__/FormModel.spec.js b/src/components/__tests__/FormModel.spec.js index 66157d567..e35684bc3 100644 --- a/src/components/__tests__/FormModel.spec.js +++ b/src/components/__tests__/FormModel.spec.js @@ -9,8 +9,8 @@ describe('FormModel', () => { describe('modelValue', () => { it('should use the provided model', () => { - const { vm } = mount({ propsData: { model: 'mockModel' } }); - expect(vm.modelValue).toBe('mockModel'); + const { vm } = mount({ propsData: { model } }); + expect(vm.modelValue).toBe(model); }); it('should use the route meta title when model is not provided', () => {