Переглянути джерело

Merge branch 'feature/refactoring-composable' into develop

Vincent GUFFON 3 роки тому
батько
коміт
bc4efa10e9
60 змінених файлів з 651 додано та 579 видалено
  1. 7 11
      components/Form/Organization/Address.vue
  2. 6 6
      components/Layout/AlertBar/SwitchYear.vue
  3. 5 5
      components/Layout/SubHeader/ActivityYear.vue
  4. 6 5
      components/Layout/SubHeader/DataTiming.vue
  5. 6 5
      components/Layout/SubHeader/DataTimingRange.vue
  6. 0 1
      components/Ui/Button/Submit.vue
  7. 6 7
      components/Ui/Form.vue
  8. 1 1
      components/Ui/Help.vue
  9. 6 6
      components/Ui/Image.vue
  10. 3 3
      components/Ui/Input/Autocomplete.vue
  11. 4 3
      components/Ui/Input/Checkbox.vue
  12. 3 3
      components/Ui/Input/DatePicker.vue
  13. 3 3
      components/Ui/Input/Email.vue
  14. 3 3
      components/Ui/Input/Enum.vue
  15. 6 3
      components/Ui/Input/Image.vue
  16. 3 3
      components/Ui/Input/Phone.vue
  17. 4 3
      components/Ui/Input/Text.vue
  18. 4 3
      components/Ui/Input/TextArea.vue
  19. 0 41
      composables/data/useAccess.ts
  20. 36 0
      composables/data/useAccessesProvider.ts
  21. 22 31
      composables/data/useAddressPostalUtils.ts
  22. 0 42
      composables/data/useCountry.ts
  23. 29 0
      composables/data/useCountryProvider.ts
  24. 22 31
      composables/data/useDataUtils.ts
  25. 0 64
      composables/data/useImage.ts
  26. 61 0
      composables/data/useImageProvider.ts
  27. 6 5
      composables/data/useMyProfile.ts
  28. 0 42
      composables/data/useTypeOfPractice.ts
  29. 29 0
      composables/data/useTypeOfPracticeProvider.ts
  30. 21 30
      composables/form/useError.ts
  31. 53 41
      composables/form/useForm.ts
  32. 18 21
      composables/form/useNavigationHelpers.ts
  33. 0 51
      composables/form/useNextStepFactory.ts
  34. 11 9
      composables/form/useValidator.ts
  35. 5 4
      pages/organization/address/_id.vue
  36. 6 5
      pages/organization/address/new.vue
  37. 5 4
      pages/organization/bank_account/_id.vue
  38. 5 5
      pages/organization/bank_account/new.vue
  39. 5 4
      pages/organization/contact_points/_id.vue
  40. 5 5
      pages/organization/contact_points/new.vue
  41. 8 10
      pages/organization/index.vue
  42. 5 4
      plugins/directives.js
  43. 6 6
      services/connection/urlBuilder.ts
  44. 23 0
      tests/unit/composables/data/useAccessesProvider.spec.ts
  45. 54 0
      tests/unit/composables/data/useAddressPostalUtils.spec.ts
  46. 30 0
      tests/unit/composables/data/useDataUtils.spec.ts
  47. 7 5
      tests/unit/composables/data/useMyProfile.spec.ts
  48. 28 0
      tests/unit/composables/form/useError.spec.ts
  49. 44 19
      tests/unit/composables/form/useForm.spec.ts
  50. 11 6
      tests/unit/composables/form/useValidator.spec.ts
  51. 1 1
      tests/unit/services/connection/connection.spec.ts
  52. 6 6
      tests/unit/services/connection/urlBuilder.spec.ts
  53. 1 1
      tests/unit/services/rights/abilitiesUtils.spec.ts
  54. 1 1
      tests/unit/services/serializer/denormalizer/baseDenormalizer.spec.ts
  55. 1 1
      tests/unit/services/serializer/denormalizer/yaml.spec.ts
  56. 1 1
      tests/unit/services/serializer/normalizer/baseNormalizer.spec.ts
  57. 2 2
      tests/unit/services/store/query.spec.ts
  58. 3 3
      tests/unit/services/store/repository.spec.ts
  59. 1 1
      tests/unit/services/utils/modelsUtils.spec.ts
  60. 3 3
      tests/unit/services/utils/objectProperties.spec.ts

+ 7 - 11
components/Form/Organization/Address.vue

@@ -103,7 +103,7 @@
 </template>
 
 <script lang="ts">
-import { defineComponent, useContext, computed, ComputedRef, Ref, ref } from '@nuxtjs/composition-api'
+import { defineComponent, useContext, computed, ComputedRef} from '@nuxtjs/composition-api'
 import { Repository as VuexRepository } from '@vuex-orm/core/dist/src/repository/Repository'
 import { Query, Model } from '@vuex-orm/core'
 import { OrganizationAddressPostal } from '~/models/Organization/OrganizationAddressPostal'
@@ -112,12 +112,12 @@ import {QUERY_TYPE, SUBMIT_TYPE} from '~/types/enums'
 import { repositoryHelper } from '~/services/store/repository'
 import { queryHelper } from '~/services/store/query'
 import { AddressPostal } from '~/models/Core/AddressPostal'
-import {UseCountry} from "~/composables/data/useCountry";
 import ModelsUtils from "~/services/utils/modelsUtils";
-import {UseAddressPostal} from "~/composables/data/useAddresspostal";
 import {AnyJson} from "~/types/interfaces";
 import DataProvider from "~/services/data/dataProvider";
-import {UseAccess} from "~/composables/data/useAccess";
+import {useAccessesProvider} from "~/composables/data/useAccessesProvider";
+import {useAddressPostalUtils} from "~/composables/data/useAddressPostalUtils";
+import {useCountryProvider} from "~/composables/data/useCountryProvider";
 
 export default defineComponent({
   props: {
@@ -128,6 +128,9 @@ export default defineComponent({
   },
   setup (props) {
     const { $dataProvider } = useContext()
+    const {getPhysicalByFullName: accessSearch} = useAccessesProvider($dataProvider)
+    const {searchFunction: addressSearch, updateCpAddress} = useAddressPostalUtils($dataProvider)
+    const {countries, fetchState: countriesFetchingState} = useCountryProvider($dataProvider)
 
     const repository: VuexRepository<Model> = repositoryHelper.getRepository(OrganizationAddressPostal)
     const query: Query = repository.with('addressPostal')
@@ -149,13 +152,6 @@ export default defineComponent({
         return {id:0, city: addressPostalItem.value?.addressCity, postcode: addressPostalItem.value?.postalCode}
       return {}
     })
-
-    const {countries, fetchState: countriesFetchingState} = new UseCountry().getAll()
-
-    const {searchFunction: addressSearch, updateCpAddress} = new UseAddressPostal().invoke()
-
-    const {getPhysicalByFullName: accessSearch} = new UseAccess().invoke()
-
     const {updateAccessAddress} = accessOwnerResearch($dataProvider, organizationAddressPostalItem, addressPostalItem)
 
     const getIdFromUri = (uri: string) => {

+ 6 - 6
components/Layout/AlertBar/SwitchYear.vue

@@ -14,14 +14,14 @@ Switch year bar : Barre qui s'affiche lorsque l'utilisateur n'est pas sur l'ann
 <script lang="ts">
 import { defineComponent, useContext, computed} from '@nuxtjs/composition-api'
 import {accessState, organizationState} from "~/types/interfaces";
-import {$useForm} from "~/composables/form/useForm";
+import {useForm} from "~/composables/form/useForm";
 import { useMyProfile } from '~/composables/data/useMyProfile'
 
 export default defineComponent({
   setup () {
-    const { store } = useContext()
-    const { markFormAsNotDirty } = $useForm()
-    const { updateMyProfile, setHistorical, setActivityYear } = useMyProfile()
+    const { store, $dataPersister } = useContext()
+    const { markAsNotDirty } = useForm(store)
+    const { updateMyProfile, setHistorical, setActivityYear } = useMyProfile($dataPersister, store)
 
     const profileAccess:accessState = store.state.profile.access
     const profileOrganization:organizationState = store.state.profile.organization
@@ -40,8 +40,8 @@ export default defineComponent({
       setHistorical(['present'])
       if(profileOrganization.currentActivityYear)
         setActivityYear(profileOrganization.currentActivityYear)
-      
-      markFormAsNotDirty()
+
+      markAsNotDirty()
       await updateMyProfile()
       window.location.reload()
     }

+ 5 - 5
components/Layout/SubHeader/ActivityYear.vue

@@ -21,13 +21,13 @@
 import { defineComponent, useContext } from '@nuxtjs/composition-api'
 import { useMyProfile } from '~/composables/data/useMyProfile'
 import { $organizationProfile } from '~/services/profile/organizationProfile'
-import { $useForm } from '~/composables/form/useForm'
+import {useForm} from "~/composables/form/useForm";
 
 export default defineComponent({
   setup () {
-    const { store } = useContext()
-    const { updateMyProfile, setActivityYear, activityYear } = useMyProfile()
-    const { markFormAsNotDirty } = $useForm()
+    const { store, $dataPersister } = useContext()
+    const { updateMyProfile, setActivityYear, activityYear } = useMyProfile($dataPersister, store)
+    const { markAsNotDirty } = useForm(store)
 
     const organizationProfile = $organizationProfile(store)
 
@@ -35,7 +35,7 @@ export default defineComponent({
     const label:string = organizationProfile.isSchool() ? 'schooling_year' : organizationProfile.isArtist() ? 'season_year' : 'cotisation_year'
 
     const updateActivityYear = async (newDate:number) => {
-      markFormAsNotDirty()
+      markAsNotDirty()
       setActivityYear(newDate)
       await updateMyProfile()
       window.location.reload()

+ 6 - 5
components/Layout/SubHeader/DataTiming.vue

@@ -25,14 +25,15 @@
 </template>
 
 <script lang="ts">
-import { defineComponent, onUnmounted, ref, watch, Ref, WatchStopHandle } from '@nuxtjs/composition-api'
-import { $useForm } from '~/composables/form/useForm'
+import {defineComponent, onUnmounted, ref, watch, Ref, WatchStopHandle, useContext} from '@nuxtjs/composition-api'
+import {useForm} from "~/composables/form/useForm";
 import { useMyProfile } from '~/composables/data/useMyProfile'
 
 export default defineComponent({
   setup () {
-    const { markFormAsNotDirty } = $useForm()
-    const { updateMyProfile, setHistorical, historical } = useMyProfile()
+    const { store, $dataPersister } = useContext()
+    const { markAsNotDirty } = useForm(store)
+    const { updateMyProfile, setHistorical, historical } = useMyProfile($dataPersister, store)
 
     const historicalBtn: Ref<Array<number>> = initHistoricalBtn(historical.value as Array<any>)
 
@@ -40,7 +41,7 @@ export default defineComponent({
       const historicalChoice: Array<string> = initHistoricalChoice(newValue)
 
       setHistorical(historicalChoice)
-      markFormAsNotDirty()
+      markAsNotDirty()
       await updateMyProfile()
       window.location.reload()
     })

+ 6 - 5
components/Layout/SubHeader/DataTimingRange.vue

@@ -37,15 +37,16 @@
 
 <script lang="ts">
 import {
-  defineComponent, onUnmounted, ref, watch, computed, ComputedRef, Ref, WatchStopHandle
+  defineComponent, onUnmounted, ref, watch, computed, ComputedRef, Ref, WatchStopHandle, useContext
 } from '@nuxtjs/composition-api'
 import { useMyProfile } from '~/composables/data/useMyProfile'
-import { $useForm } from '~/composables/form/useForm'
+import {useForm} from "~/composables/form/useForm";
 
 export default defineComponent({
   setup (_, { emit }) {
-    const { markFormAsNotDirty } = $useForm()
-    const { updateMyProfile, setHistoricalRange, historical } = useMyProfile()
+    const { store, $dataPersister } = useContext()
+    const { markAsNotDirty } = useForm(store)
+    const { updateMyProfile, setHistoricalRange, historical } = useMyProfile($dataPersister, store)
 
     const datesRange:ComputedRef<Array<any>> = computed(() => {
       return [historical.value.dateStart, historical.value.dateEnd]
@@ -67,7 +68,7 @@ export default defineComponent({
 
     const updateDateTimeRange = async (dates:Array<string>): Promise<any> => {
       setHistoricalRange(dates)
-      markFormAsNotDirty()
+      markAsNotDirty()
       await updateMyProfile()
       window.location.reload()
     }

+ 0 - 1
components/Ui/Button/Submit.vue

@@ -38,7 +38,6 @@
 
 <script lang="ts">
 import {computed, ComputedRef, defineComponent, ref, Ref} from "@nuxtjs/composition-api";
-import {$useForm} from "~/composables/form/useForm";
 
 export default defineComponent({
   props: {

+ 6 - 7
components/Ui/Form.vue

@@ -74,7 +74,7 @@ import {repositoryHelper} from '~/services/store/repository'
 import {queryHelper} from '~/services/store/query'
 import {FORM_STATUS, QUERY_TYPE, SUBMIT_TYPE, TYPE_ALERT} from '~/types/enums'
 import {AnyJson} from '~/types/interfaces'
-import {$useForm} from '~/composables/form/useForm'
+import {useForm} from "~/composables/form/useForm";
 import * as _ from 'lodash'
 import Form from "~/services/store/form";
 import Page from "~/services/store/page";
@@ -106,8 +106,7 @@ export default defineComponent({
   },
   setup(props) {
     const {$dataPersister, store, app: {router, i18n}} = useContext()
-    const {markFormAsDirty, markFormAsNotDirty, readonly} = $useForm()
-    const nextStepFactory = new UseNextStepFactory()
+    const {markAsDirty, markAsNotDirty, readonly, nextStepFactory} = useForm(store)
     const {id, query}: ToRefs = toRefs(props)
     const page = new Page(store)
     const form:Ref<any> = ref(null)
@@ -117,13 +116,13 @@ export default defineComponent({
     })
 
     const updateRepository = (newValue: string, field: string) => {
-      markFormAsDirty()
+      markAsDirty()
       repositoryHelper.updateStoreFromField(props.model, entry.value, newValue, field)
     }
 
     const submit = async (next: string|null = null) => {
       if(form.value.validate()){
-        markFormAsNotDirty()
+        markAsNotDirty()
 
         try {
           const response = await $dataPersister.invoke({
@@ -158,7 +157,7 @@ export default defineComponent({
 
     const nextStep = (next: string|null, response: AnyJson) =>{
       if(next === null) return
-      nextStepFactory.invoke(props.submitActions[next], response)[next]()
+      nextStepFactory(props.submitActions[next], response, router)[next]()
     }
 
     const showDialog: ComputedRef<boolean> = computed(() => {
@@ -175,7 +174,7 @@ export default defineComponent({
     }
 
     const quitForm = () => {
-      markFormAsNotDirty()
+      markAsNotDirty()
       store.commit('form/setShowConfirmToLeave', false)
 
       const entryCopy = query.value.first()

+ 1 - 1
components/Ui/Help.vue

@@ -22,7 +22,7 @@
       </v-icon>
     </template>
 
-    <div ref="slotDiv" class="tooltip" v-click-outside="onClickOutside">
+    <div ref="slotDiv" class="tooltip" v-click-out="onClickOutside">
       <slot></slot>
     </div>
   </v-tooltip>

+ 6 - 6
components/Ui/Image.vue

@@ -41,8 +41,8 @@
 
 
 <script lang="ts">
-import {defineComponent, onUnmounted, ref, Ref, watch} from '@nuxtjs/composition-api'
-import {UseImage} from "~/composables/data/useImage";
+import {defineComponent, onUnmounted, ref, Ref, useContext, watch} from '@nuxtjs/composition-api'
+import {useImageProvider} from "~/composables/data/useImageProvider";
 import {WatchStopHandle} from "@vue/composition-api";
 
 export default defineComponent({
@@ -82,12 +82,12 @@ export default defineComponent({
   setup(props) {
     const openUpload: Ref<Boolean> = ref(false)
     const imgSrcReload: Ref<any> = ref(null)
+    const {$dataProvider, $config} = useContext()
+    const {getOne, provideImg} = useImageProvider($dataProvider, $config)
 
-    const useImg = new UseImage()
-
-    const { imageLoaded, fetch } = useImg.getOne(props.id, props.imageByDefault, props.height, props.width)
+    const { imageLoaded, fetch } = getOne(props.id, props.imageByDefault, props.height, props.width)
     const unwatch: WatchStopHandle = watch(() => props.id, async (newValue, oldValue) => {
-      imgSrcReload.value = await useImg.provideImg(newValue as number, props.height, props.width)
+      imgSrcReload.value = await provideImg(newValue as number, props.height, props.width)
     })
 
     /**

+ 3 - 3
components/Ui/Input/Autocomplete.vue

@@ -37,7 +37,7 @@ import {computed, defineComponent, ComputedRef, Ref, ref, watch, onUnmounted, us
 import { AnyJson } from '~/types/interfaces'
 import * as _ from 'lodash'
 import {$objectProperties} from "~/services/utils/objectProperties";
-import {$useError} from "~/composables/form/useError";
+import {useError} from "~/composables/form/useError";
 
 export default defineComponent({
   props: {
@@ -113,9 +113,9 @@ export default defineComponent({
     },
   },
   setup (props, { emit }) {
-    const {app:{i18n}} = useContext()
+    const {app:{i18n}, store} = useContext()
     const search:Ref<string|null> = ref(null)
-    const {error, onChange} = $useError(props.field, emit)
+    const {error, onChange} = useError(props.field, emit, store)
 
     // On reconstruit les items à afficher...
     const itemsToDisplayed: ComputedRef<Array<AnyJson>> = computed(() => {

+ 4 - 3
components/Ui/Input/Checkbox.vue

@@ -21,8 +21,8 @@ Case à cocher
 </template>
 
 <script lang="ts">
-import { defineComponent } from '@nuxtjs/composition-api'
-import {$useError} from "~/composables/form/useError";
+import {defineComponent, useContext} from '@nuxtjs/composition-api'
+import {useError} from "~/composables/form/useError";
 
 export default defineComponent({
   props: {
@@ -46,7 +46,8 @@ export default defineComponent({
     }
   },
   setup (props, {emit}) {
-    const {error, onChange} = $useError(props.field, emit)
+    const {store} = useContext()
+    const {error, onChange} = useError(props.field, emit, store)
 
     return {
       label_field: props.label ?? props.field,

+ 3 - 3
components/Ui/Input/DatePicker.vue

@@ -43,7 +43,7 @@ Sélecteur de dates
 import { defineComponent, watch, ref, useContext, onUnmounted, computed, Ref, ComputedRef } from '@nuxtjs/composition-api'
 import { WatchStopHandle } from '@vue/composition-api'
 import DatesUtils from '~/services/utils/datesUtils'
-import {$useError} from "~/composables/form/useError";
+import {useError} from "~/composables/form/useError";
 
 export default defineComponent({
   props: {
@@ -81,9 +81,9 @@ export default defineComponent({
   },
   setup (props, { emit }) {
     const { data, range } = props
-    const { $moment } = useContext()
+    const { $moment, store } = useContext()
+    const {error, onChange} = useError(props.field, emit, store)
     const dateUtils = new DatesUtils($moment)
-    const {error, onChange} = $useError(props.field, emit)
 
     const datesParsed: Ref<Array<string>|string|null> = range ? ref(Array<string>()) : ref(null)
 

+ 3 - 3
components/Ui/Input/Email.vue

@@ -15,7 +15,7 @@ Champs de saisie de type Text dédié à la saisie d'emails
 
 <script lang="ts">
 import { defineComponent, useContext } from '@nuxtjs/composition-api'
-import {$useError} from "~/composables/form/useError";
+import {useError} from "~/composables/form/useError";
 
 export default defineComponent({
   props: {
@@ -46,8 +46,8 @@ export default defineComponent({
     }
   },
   setup (props, {emit}) {
-    const { app: { i18n } } = useContext()
-    const {error, onChange} = $useError(props.field, emit)
+    const { app: { i18n }, store } = useContext()
+    const {error, onChange} = useError(props.field, emit, store)
 
     const rules = [
       (email: string) => validEmail(email) || i18n.t('email_error')

+ 3 - 3
components/Ui/Input/Enum.vue

@@ -31,7 +31,7 @@ Liste déroulante dédiée à l'affichage d'objets Enum
 import {defineComponent, ref, useContext, useFetch, Ref} from '@nuxtjs/composition-api'
 import { EnumChoices } from '~/types/interfaces'
 import { QUERY_TYPE } from '~/types/enums'
-import {$useError} from "~/composables/form/useError";
+import {useError} from "~/composables/form/useError";
 
 export default defineComponent({
   props: {
@@ -68,8 +68,8 @@ export default defineComponent({
     const labelField = props.label ?? props.field
 
     const { enumType } = props
-    const { $dataProvider } = useContext()
-    const {error, onChange} = $useError(props.field, emit)
+    const { $dataProvider, store } = useContext()
+    const {error, onChange} = useError(props.field, emit, store)
 
     const items: Ref<Array<EnumChoices>> = ref([])
     useFetch(async () => {

+ 6 - 3
components/Ui/Input/Image.vue

@@ -62,7 +62,7 @@ import {defineComponent, onUnmounted, Ref, ref, useContext, useFetch, watch} fro
 import { Cropper } from 'vue-advanced-cropper'
 import 'vue-advanced-cropper/dist/style.css';
 import {AnyJson, ApiResponse} from "~/types/interfaces";
-import {UseImage} from "~/composables/data/useImage";
+import {useImageProvider} from "~/composables/data/useImageProvider";
 import {WatchStopHandle} from "@vue/composition-api";
 import {QUERY_TYPE} from "~/types/enums";
 import {File} from "~/models/Core/File";
@@ -88,6 +88,9 @@ export default defineComponent({
   },
   fetchOnServer: false,
   setup (props, {emit}) {
+    const {$dataProvider, $config, $dataPersister} = useContext()
+    const {getOne} = useImageProvider($dataProvider, $config)
+
     const fileToSave = new File()
     const cropper:Ref<any> = ref(null)
     const image: Ref<AnyJson> = ref({
@@ -103,7 +106,7 @@ export default defineComponent({
         height: (visibleArea || imageSize).height,
       };
     }
-    const {$dataProvider, $config, $dataPersister} = useContext()
+
 
     //Si l'id est renseigné, il faut récupérer l'Item File afin d'avoir les informations de config, le nom, etc.
     if(props.existingImageId){
@@ -125,7 +128,7 @@ export default defineComponent({
     }
 
     //On récupère l'image...
-    const { fetchState, imageLoaded } = new UseImage().getOne(props.existingImageId)
+    const { fetchState, imageLoaded } = getOne(props.existingImageId)
     const unwatch: WatchStopHandle = watch(imageLoaded, (newValue, oldValue) => {
       if (newValue === oldValue || typeof newValue === 'undefined') { return }
       image.value.src = newValue

+ 3 - 3
components/Ui/Input/Phone.vue

@@ -24,7 +24,7 @@ Champs de saisie d'un numéro de téléphone
 
 <script lang="ts">
 import { defineComponent, Ref, ref, useContext, computed } from '@nuxtjs/composition-api'
-import {$useError} from "~/composables/form/useError";
+import {useError} from "~/composables/form/useError";
 
 export default defineComponent({
   props: {
@@ -49,8 +49,8 @@ export default defineComponent({
     }
   },
   setup (props, {emit}) {
-    const { app: { i18n } } = useContext()
-    const {error, onChange} = $useError(props.field, emit)
+    const { app: { i18n }, store } = useContext()
+    const {error, onChange} = useError(props.field, emit, store)
 
     const nationalNumber: Ref<string | number> = ref('')
     const internationalNumber: Ref<string | number> = ref('')

+ 4 - 3
components/Ui/Input/Text.vue

@@ -20,8 +20,8 @@ Champs de saisie de texte
 </template>
 
 <script lang="ts">
-import { defineComponent } from '@nuxtjs/composition-api'
-import {$useError} from "~/composables/form/useError";
+import {defineComponent, useContext} from '@nuxtjs/composition-api'
+import {useError} from "~/composables/form/useError";
 import {mask} from 'vue-the-mask';
 
 export default defineComponent({
@@ -71,7 +71,8 @@ export default defineComponent({
     }
   },
   setup (props, {emit}) {
-    const {error: violations, onChange} = $useError(props.field, emit)
+    const {store} = useContext()
+    const {error: violations, onChange} = useError(props.field, emit, store)
 
     return {
       label_field: props.label ?? props.field,

+ 4 - 3
components/Ui/Input/TextArea.vue

@@ -18,8 +18,8 @@ Champs de saisie de bloc texte
 </template>
 
 <script lang="ts">
-import { defineComponent } from '@nuxtjs/composition-api'
-import {$useError} from "~/composables/form/useError";
+import {defineComponent, useContext} from '@nuxtjs/composition-api'
+import {useError} from "~/composables/form/useError";
 
 export default defineComponent({
   props: {
@@ -58,7 +58,8 @@ export default defineComponent({
     }
   },
   setup (props, {emit}) {
-    const {error: violations, onChange} = $useError(props.field, emit)
+    const {store} = useContext()
+    const {error: violations, onChange} = useError(props.field, emit, store)
 
     return {
       label_field: props.label ?? props.field,

+ 0 - 41
composables/data/useAccess.ts

@@ -1,41 +0,0 @@
-import { AnyJson } from '~/types/interfaces'
-import {QUERY_TYPE} from "~/types/enums";
-import { useContext} from '@nuxtjs/composition-api'
-import DataProvider from "~/services/data/dataProvider";
-
-/**
- * @category composables/data
- * @class UseAccess
- * Use Classe qui va récupérer les Accesses suivant des critères de recherche
- */
-export class UseAccess {
-  private $dataProvider!: DataProvider
-
-  constructor() {
-    const {$dataProvider} = useContext()
-    this.$dataProvider = $dataProvider
-  }
-
-  public invoke(): AnyJson{
-    return {
-      getPhysicalByFullName: (research: string) => this.getPhysicalByFullName(research),
-    }
-  }
-
-  private async getPhysicalByFullName(research: string): Promise<Array<AnyJson>>{
-    if(research){
-      const response = await this.$dataProvider.invoke({
-        type: QUERY_TYPE.DEFAULT,
-        url: `api/access_people`,
-        listArgs: {
-          filters:[
-            {key: 'person.isPhysical', value: 1},
-            {key: 'fullname', value: research}
-          ]
-        }
-      })
-      return response.data
-    }
-    return []
-  }
-}

+ 36 - 0
composables/data/useAccessesProvider.ts

@@ -0,0 +1,36 @@
+import { AnyJson } from '~/types/interfaces'
+import {QUERY_TYPE} from "~/types/enums";
+import DataProvider from "~/services/data/dataProvider";
+
+/**
+ * Composable function
+ * @category composables/data
+ * @param $dataProvider
+ */
+export function useAccessesProvider($dataProvider: DataProvider){
+
+  /**
+   * Retourne les personnes physique selon leur nom et prénom
+   * @param research
+   */
+  async function getPhysicalByFullName(research: string): Promise<Array<AnyJson>>{
+    if(research){
+      const response = await $dataProvider.invoke({
+        type: QUERY_TYPE.DEFAULT,
+        url: `api/access_people`,
+        listArgs: {
+          filters:[
+            {key: 'person.isPhysical', value: 1},
+            {key: 'fullname', value: research}
+          ]
+        }
+      })
+      return response.data
+    }
+    return []
+  }
+
+  return {
+    getPhysicalByFullName
+  }
+}

+ 22 - 31
composables/data/useAddresspostal.ts → composables/data/useAddressPostalUtils.ts

@@ -1,27 +1,13 @@
-import { AnyJson } from '~/types/interfaces'
+import {AnyJson} from '~/types/interfaces'
 import {QUERY_TYPE} from "~/types/enums";
-import { useContext } from '@nuxtjs/composition-api'
 import DataProvider from "~/services/data/dataProvider";
 
 /**
  * @category composables/data
- * @class UseAddressPostal
- * Use Classe pour gérer les deux champs postal code et adresseCity
+ * @param $dataProvider
+ * Composable class qui regroupe les utils des adresses postales
  */
-export class UseAddressPostal {
-  private $dataProvider!: DataProvider
-
-  constructor() {
-    const {$dataProvider} = useContext()
-    this.$dataProvider = $dataProvider
-  }
-
-  public invoke(): AnyJson{
-    return {
-      searchFunction: (research: string, field: string) => this.searchFunction(research, field),
-      updateCpAddress: (value:AnyJson, updateRepository: Function) => this.updateCpAddress(value, updateRepository),
-    }
-  }
+export function useAddressPostalUtils($dataProvider: DataProvider) {
 
   /**
    * Fonction de recherche qui utilise l'API gouvernematal pour autocompléter les CP et villes.
@@ -29,23 +15,23 @@ export class UseAddressPostal {
    * @param field
    * @private
    */
-  private async searchFunction (research: string, field: string): Promise<Array<AnyJson>>{
-    if(research){
-      const response = await this.$dataProvider.invoke({
+  async function searchFunction(research: string, field: string): Promise<Array<AnyJson>> {
+    if (research) {
+      const response = await $dataProvider.invoke({
         type: QUERY_TYPE.DEFAULT,
         url: `https://api-adresse.data.gouv.fr/search/?q=${research}&type=municipality&autocomplete=1&limit=20`,
         params: {
           noXaccessId: true
         }
       })
-      const apiResponse = response.data.features.map((data:AnyJson)=>data.properties)
+      const apiResponse = response.data.features.map((data: AnyJson) => data.properties)
 
       // Par défaut on insère les valeurs que l'utilisateur a écrit, car un nom de ville ou de CP peut être absent de l'API
       const defaultResponse = []
-      if(field === 'addressPostal.addressCity'){
-        defaultResponse.push({id:0, postcode: null, city: research})
-      }else{
-        defaultResponse.push({id:0, postcode: research, city: null})
+      if (field === 'addressPostal.addressCity') {
+        defaultResponse.push({id: 0, postcode: null, city: research})
+      } else {
+        defaultResponse.push({id: 0, postcode: research, city: null})
       }
 
       return defaultResponse.concat(apiResponse)
@@ -59,17 +45,22 @@ export class UseAddressPostal {
    * @param updateRepository
    * @private
    */
-  private updateCpAddress(value:AnyJson, updateRepository: Function): void{
+  function updateCpAddress(value: AnyJson, updateRepository: Function): void {
     //Si une valeur est présente
-    if(value){
-      if(value.city)
+    if (value) {
+      if (value.city)
         updateRepository(value.city, 'addressPostal.addressCity')
-      if(value.postcode)
+      if (value.postcode)
         updateRepository(value.postcode, 'addressPostal.postalCode')
-    }else{
+    } else {
       //Cas où on efface les valeurs des champs
       updateRepository(null, 'addressPostal.addressCity')
       updateRepository(null, 'addressPostal.postalCode')
     }
   }
+
+  return {
+    searchFunction,
+    updateCpAddress
+  }
 }

+ 0 - 42
composables/data/useCountry.ts

@@ -1,42 +0,0 @@
-import { AnyJson } from '~/types/interfaces'
-import {QUERY_TYPE} from "~/types/enums";
-import {Country} from "~/models/Core/Country";
-import { useContext, useFetch, computed } from '@nuxtjs/composition-api'
-import DataProvider from "~/services/data/dataProvider";
-import {repositoryHelper} from "~/services/store/repository";
-
-/**
- * @category composables/data
- * @class UseCountry
- * Use Classe qui va récupérer les Countries
- */
-export class UseCountry {
-  private $dataProvider!: DataProvider
-
-  constructor() {
-    const {$dataProvider} = useContext()
-    this.$dataProvider = $dataProvider
-  }
-
-  /**
-   * Récupération des Country via l'API
-   */
-  public getAll(): AnyJson{
-    const {fetch, fetchState} = useFetch(async () => {
-      await this.$dataProvider.invoke({
-        type: QUERY_TYPE.MODEL,
-        model: Country
-      })
-    })
-
-    const countries =  computed(() => {
-      return repositoryHelper.findCollectionFromModel(Country)
-    })
-
-    return {
-      countries,
-      fetch,
-      fetchState
-    }
-  }
-}

+ 29 - 0
composables/data/useCountryProvider.ts

@@ -0,0 +1,29 @@
+import {QUERY_TYPE} from "~/types/enums";
+import {Country} from "~/models/Core/Country";
+import { useFetch, computed } from '@nuxtjs/composition-api'
+import DataProvider from "~/services/data/dataProvider";
+import {repositoryHelper} from "~/services/store/repository";
+
+/**
+ * @category composables/data
+ * @param $dataProvider
+ * Composable Classe qui va récupérer les Countries
+ */
+export function useCountryProvider($dataprovider: DataProvider){
+  const {fetch, fetchState} = useFetch(async () => {
+    await $dataprovider.invoke({
+      type: QUERY_TYPE.MODEL,
+      model: Country
+    })
+  })
+
+  const countries =  computed(() => {
+    return repositoryHelper.findCollectionFromModel(Country)
+  })
+
+  return {
+    countries,
+    fetch,
+    fetchState
+  }
+}

+ 22 - 31
composables/data/useDataUtils.ts

@@ -1,6 +1,6 @@
 import { AnyJson } from '~/types/interfaces'
 import {FORM_STATUS, QUERY_TYPE} from "~/types/enums";
-import {ref, Ref, useContext, useFetch} from '@nuxtjs/composition-api'
+import {ref, Ref, useFetch} from '@nuxtjs/composition-api'
 import DataProvider from "~/services/data/dataProvider";
 import {Model} from "@vuex-orm/core";
 import {repositoryHelper} from "~/services/store/repository";
@@ -8,37 +8,24 @@ import {Store} from "vuex";
 
 /**
  * @category composables/data
- * @class UseDataUtils
- * Use Classe qui va récupérer les Accesses suivant des critères de recherche
+ * @param $dataProvider
+ * Composable Classe qui va récupérer les Accesses suivant des critères de recherche
  */
-export class UseDataUtils {
-  private $dataProvider: DataProvider
-  private route: Ref
-  private store: Store<any>
-
-  constructor() {
-    const { route, $dataProvider, store } = useContext()
-    this.$dataProvider = $dataProvider
-    this.store = store
-    this.route = route
-  }
-
-  public invoke(): AnyJson{
-    return {
-      getItemToEdit: (model: typeof Model) => this.getItemToEdit(model),
-      createItem: () => this.createItem(),
-    }
-  }
+export function useDataUtils($dataProvider: DataProvider){
 
   /**
-   * recherche l'item a éditer
+   * recherche l'item a éditer et alimente le dataprovider
+   * @param route
    * @param model
-   * @private
    */
-  private getItemToEdit(model: typeof Model){
-    const id = parseInt(this.route.value.params.id)
+  function getItemToEdit(route: Ref, model: typeof Model){
+    const id = parseInt(route.value.params.id)
+    if(!id){
+      throw new Error('id must be exist')
+    }
+
     const {fetchState} = useFetch(async () => {
-      await this.$dataProvider.invoke({
+      await $dataProvider.invoke({
         type: QUERY_TYPE.MODEL,
         model,
         id
@@ -52,15 +39,14 @@ export class UseDataUtils {
 
   /**
    * Créer l'Item quand l'utilisateur veux créer un nouvel enregistrement
-   * @param itemToCreate
+   * @param store
    * @param model
-   * @private
    */
-  private createItem(){
-    this.store.commit('form/setFormStatus', FORM_STATUS.CREATE)
+  function createItem(store: Store<any>, model: typeof Model){
+    store.commit('form/setFormStatus', FORM_STATUS.CREATE)
     const loading:Ref<Boolean> = ref(true)
     const item: Ref<any> = ref('')
-    const create = async (itemToCreate: AnyJson, model: typeof Model) =>{
+    const create = async (itemToCreate: AnyJson) =>{
       item.value = await repositoryHelper.persist(model, itemToCreate)
       loading.value = false
     }
@@ -71,4 +57,9 @@ export class UseDataUtils {
       item
     }
   }
+
+  return {
+    getItemToEdit,
+    createItem
+  }
 }

+ 0 - 64
composables/data/useImage.ts

@@ -1,64 +0,0 @@
-import {AnyJson, ApiResponse} from '~/types/interfaces'
-import {QUERY_TYPE} from "~/types/enums";
-import {useContext, useFetch, Ref, ref} from '@nuxtjs/composition-api'
-import DataProvider from "~/services/data/dataProvider";
-
-/**
- * @category composables/data
- * @class UseImage
- * Use Classe qui va récupérer les Images
- */
-export class UseImage {
-  private $dataProvider!: DataProvider
-  private $config!: AnyJson
-
-  constructor() {
-    const {$dataProvider, $config} = useContext()
-    this.$dataProvider = $dataProvider
-    this.$config = $config
-  }
-
-  /**
-   * Récupération d'une image via l'ancienne API
-   */
-  public getOne(id: number|undefined, imageByDefault: string = '', height: number|undefined = 0, width: number|undefined = 0): AnyJson{
-    const imageLoaded: Ref<String> = ref('')
-
-    const {fetchState, fetch} = useFetch(async () => {
-        try{
-          if(id){
-            imageLoaded.value = await this.provideImg(id, height, width)
-          }else
-            throw new Error('id is null')
-        }catch (e){
-          if(imageByDefault)
-            imageLoaded.value = require(`assets/images/byDefault/${imageByDefault}`)
-        }
-      }
-    )
-
-    return {
-      fetch,
-      fetchState,
-      imageLoaded
-    }
-  }
-
-  /**
-   * retourne l'image demandée
-   * @param id
-   * @param height
-   * @param width
-   */
-  public async provideImg(id: number, height: number = 0, width: number = 0){
-    return await this.$dataProvider.invoke({
-      type: QUERY_TYPE.IMAGE,
-      baseUrl: this.$config.baseURL_Legacy,
-      imgArgs: {
-        id: id,
-        height: height,
-        width: width
-      }
-    })
-  }
-}

+ 61 - 0
composables/data/useImageProvider.ts

@@ -0,0 +1,61 @@
+import {AnyJson} from '~/types/interfaces'
+import {QUERY_TYPE} from "~/types/enums";
+import {useFetch, Ref, ref} from '@nuxtjs/composition-api'
+import DataProvider from "~/services/data/dataProvider";
+
+/**
+ * @category composables/data
+ * @class UseImage
+ * Use Classe qui va récupérer les Images
+ */
+export function useImageProvider($dataProvider: DataProvider, $config: AnyJson) {
+
+  /**
+   * Récupération d'une image via l'ancienne API
+   */
+  function getOne(id: number | undefined, imageByDefault: string = '', height: number | undefined = 0, width: number | undefined = 0): AnyJson {
+    const imageLoaded: Ref<String> = ref('')
+
+    const {fetchState, fetch} = useFetch(async () => {
+        try {
+          if (id) {
+            imageLoaded.value = await provideImg(id, height, width)
+          } else
+            throw new Error('id is null')
+        } catch (e) {
+          if (imageByDefault)
+            imageLoaded.value = require(`assets/images/byDefault/${imageByDefault}`)
+        }
+      }
+    )
+
+    return {
+      fetch,
+      fetchState,
+      imageLoaded
+    }
+  }
+
+  /**
+   * retourne l'image demandée
+   * @param id
+   * @param height
+   * @param width
+   */
+  async function provideImg(id: number, height: number = 0, width: number = 0) {
+    return await $dataProvider.invoke({
+      type: QUERY_TYPE.IMAGE,
+      baseUrl: $config.baseURL_Legacy,
+      imgArgs: {
+        id: id,
+        height: height,
+        width: width
+      }
+    })
+  }
+
+  return {
+    getOne,
+    provideImg
+  }
+}

+ 6 - 5
composables/data/useMyProfile.ts

@@ -2,16 +2,17 @@ import {computed, ComputedRef} from '@nuxtjs/composition-api'
 import { Item, Model } from '@vuex-orm/core'
 import { repositoryHelper } from '~/services/store/repository'
 import { QUERY_TYPE } from '~/types/enums'
-import { Historical } from '~/types/interfaces'
+import {AccessStore, Historical} from '~/types/interfaces'
 import { MyProfile } from '~/models/Access/MyProfile'
 import { $accessProfile } from '@/services/profile/accessProfile'
-import { useContext } from '@nuxtjs/composition-api'
+import DataPersister from "~/services/data/dataPersister";
 
 /**
- * Composable function
+ * Composable function qui permet de gérer les opérations sur le profil connecté
+ * @param $dataPersister
+ * @param store
  */
-export function useMyProfile(){
-  const {$dataPersister, store} = useContext()
+export function useMyProfile($dataPersister: DataPersister, store: AccessStore){
   $accessProfile.setStore(store)
   const currentAccessId = $accessProfile.getCurrentAccessId()
   const myProfile = getMyProfileInstance(currentAccessId) as MyProfile

+ 0 - 42
composables/data/useTypeOfPractice.ts

@@ -1,42 +0,0 @@
-import { AnyJson } from '~/types/interfaces'
-import {QUERY_TYPE} from "~/types/enums";
-import { useContext, useFetch, computed } from '@nuxtjs/composition-api'
-import DataProvider from "~/services/data/dataProvider";
-import {repositoryHelper} from "~/services/store/repository";
-import {TypeOfPractice} from "~/models/Organization/TypeOfPractice";
-
-/**
- * @category composables/data
- * @class UseTypeOfPractice
- * Use Classe qui va récupérer les UseTypeOfPractices
- */
-export class UseTypeOfPractice {
-  private $dataProvider!: DataProvider
-
-  constructor() {
-    const {$dataProvider} = useContext()
-    this.$dataProvider = $dataProvider
-  }
-
-  /**
-   * Récupération des UseTypeOfPractices via l'API
-   */
-  public getAll(): AnyJson{
-    const {fetch, fetchState} = useFetch(async () => {
-      await this.$dataProvider.invoke({
-        type: QUERY_TYPE.MODEL,
-        model: TypeOfPractice
-      })
-    })
-
-    const typeOfPractices =  computed(() => {
-      return repositoryHelper.findCollectionFromModel(TypeOfPractice)
-    })
-
-    return {
-      typeOfPractices,
-      fetch,
-      fetchState
-    }
-  }
-}

+ 29 - 0
composables/data/useTypeOfPracticeProvider.ts

@@ -0,0 +1,29 @@
+import {QUERY_TYPE} from "~/types/enums";
+import { useFetch, computed } from '@nuxtjs/composition-api'
+import DataProvider from "~/services/data/dataProvider";
+import {repositoryHelper} from "~/services/store/repository";
+import {TypeOfPractice} from "~/models/Organization/TypeOfPractice";
+
+/**
+ * @category composables/data
+ * @class UseTypeOfPractice
+ * Composable qui va récupérer les UseTypeOfPractices
+ */
+export function useTypeOfPracticeProvider($dataProvider: DataProvider){
+  const {fetch, fetchState} = useFetch(async () => {
+    await $dataProvider.invoke({
+      type: QUERY_TYPE.MODEL,
+      model: TypeOfPractice
+    })
+  })
+
+  const typeOfPractices =  computed(() => {
+    return repositoryHelper.findCollectionFromModel(TypeOfPractice)
+  })
+
+  return {
+    typeOfPractices,
+    fetch,
+    fetchState
+  }
+}

+ 21 - 30
composables/form/useError.ts

@@ -1,41 +1,32 @@
-import { AnyJson, AnyStore } from '~/types/interfaces'
-import {computed, ComputedRef, useContext} from "@nuxtjs/composition-api";
+import { AnyStore } from '~/types/interfaces'
+import {computed, ComputedRef} from "@nuxtjs/composition-api";
 
 /**
  * @category composables/form
- * @class UseError
- * Use Classe pour gérer l'apparition de message si le formulaire est dirty
+ * Composable pour gérer l'apparition de message si le formulaire est dirty
+ * @param field
+ * @param emit
+ * @param store
  */
-export class UseError {
-  private store: AnyStore
-
-  constructor () {
-    const {store} = useContext()
-    this.store = store
-  }
+export function useError(field: string, emit: any, store: AnyStore){
+  const error:ComputedRef<Boolean> = computed(()=>{
+    return store.state.form.violations.indexOf(field) >= 0
+  })
 
   /**
-   * Composition function
+   * Lorsque la valeur d'un champ change, on supprime le fait qu'il puisse être "faux" dans le store, et on émet sa nouvelle valeur au parent
+   * @param emit
+   * @param fieldValue
+   * @param changeField
    */
-  public invoke (field: string, emit: any): AnyJson {
-    const error:ComputedRef<Boolean> = computed(()=>{
-      return this.store.state.form.violations.indexOf(field) >= 0
-    })
-
-    return {
-      onChange: (fieldValue:any) => this.onChange(emit, fieldValue, field),
-      error
-    }
+  function onChange (emit:any, fieldValue:any, changeField:string) {
+      const errors = store.state.form.violations.filter((field:string) => field !== changeField)
+      store.commit('form/setViolations', errors)
+      emit('update', fieldValue, changeField)
   }
 
-  /**
-   *
-   */
-  private onChange (emit:any, fieldValue:any, changeField:string) {
-    const errors = this.store.state.form.violations.filter((field:string) => field !== changeField)
-    this.store.commit('form/setViolations', errors)
-    emit('update', fieldValue, changeField)
+  return {
+    onChange: (fieldValue:any) => onChange(emit, fieldValue, field),
+    error
   }
 }
-
-export const $useError = (field: string, emit: any) => new UseError().invoke(field, emit)

+ 53 - 41
composables/form/useForm.ts

@@ -1,68 +1,81 @@
-import { AnyJson, AnyStore } from '~/types/interfaces'
-import {computed, ComputedRef, useContext} from "@nuxtjs/composition-api";
+import {AnyJson, AnyStore} from '~/types/interfaces'
+import {computed, ComputedRef} from "@nuxtjs/composition-api";
+import {Route} from "vue-router";
+import {FORM_STATUS, SUBMIT_TYPE} from "~/types/enums";
 
 /**
  * @category composables/form
- * @class UseDirtyForm
- * Use Classe pour gérer l'apparition de message si le formulaire est dirty
+ * @param store
+ * Composable pour gérer l'apparition de message si le formulaire est dirty
  */
-export class UseForm {
-  private store: AnyStore
-  private readonly handler: any
+export function useForm(store: AnyStore){
 
-  constructor () {
-    const {store} = useContext()
-    this.store = store
-    this.handler = getEventHandler()
-  }
+  const handler: any = getEventHandler()
+
+  const readonly: ComputedRef<boolean> = computed(() => {
+    return store.state.form.readonly
+  })
 
   /**
-   * Composition function
+   * définit le formulaire comme Dirty (modifié)
    */
-  public invoke (): AnyJson {
-    const readonly: ComputedRef<boolean> = computed(() => {
-      return this.store.state.form.readonly
-    })
-
-    return {
-      markFormAsDirty: () => this.markAsDirty(),
-      markFormAsNotDirty: () => this.markAsNotDirty(),
-      readonly
+  function markAsDirty () {
+    store.commit('form/setDirty', true)
+    if (process.browser) {
+      window.addEventListener('beforeunload', handler)
     }
   }
 
   /**
-   * définit le formulaire comme Dirty (modifié)
+   * Définit le formulaire comme non dirty (non modifié)
    */
-  private markAsDirty () {
-    this.store.commit('form/setDirty', true)
-    this.addEventListener()
+  function markAsNotDirty () {
+    store.commit('form/setDirty', false)
+    if (process.browser) {
+      window.removeEventListener('beforeunload', handler)
+    }
   }
 
   /**
-   * Définit le formulaire comme non dirty (non modifié)
+   * Action Sauvegarder qui redirige vers la page d'edit si on est en mode create
+   * @param route
+   * @param id
+   * @param router
    */
-  private markAsNotDirty () {
-    this.store.commit('form/setDirty', false)
-    this.clearEventListener()
+  function save(route: Route, id: number, router: any){
+    if(store.state.form.formStatus === FORM_STATUS.CREATE){
+      route.path += id
+      router.push(route)
+    }
   }
 
   /**
-   * Ajoute un Event avant le départ de la page
+   * Action sauvegarder et route suivante qui redirige vers une route
+   * @param route
+   * @param router
    */
-  private addEventListener () {
-    if (process.browser) {
-      window.addEventListener('beforeunload', this.handler)
-    }
+  function saveAndGoTo(route: Route, router: any){
+    router.push(route)
   }
 
   /**
-   * Supprime l'Event avant le départ de la page
+   * Factory des fonction permettant d'assurer l'étape suivant à la soumission d'un formulaire
+   * @param args
+   * @param response
+   * @param router
    */
-  private clearEventListener () {
-    if (process.browser) {
-      window.removeEventListener('beforeunload', this.handler)
-    }
+  function nextStepFactory(args: any, response: AnyJson, router: any){
+    const factory: AnyJson = {}
+    factory[SUBMIT_TYPE.SAVE] = () => save(args, response.id, router)
+    factory[SUBMIT_TYPE.SAVE_AND_BACK] = () => saveAndGoTo(args, router)
+    return factory
+  }
+
+  return {
+    markAsDirty,
+    markAsNotDirty,
+    nextStepFactory,
+    readonly
   }
 }
 
@@ -74,4 +87,3 @@ function getEventHandler () {
     e.returnValue = ''
   }
 }
-export const $useForm = () => new UseForm().invoke()

+ 18 - 21
composables/form/useNavigationHelpers.ts

@@ -1,29 +1,26 @@
-import { onMounted, ref, useContext, Ref } from '@nuxtjs/composition-api'
+import { onMounted, ref, Ref } from '@nuxtjs/composition-api'
 import * as _ from 'lodash'
 
 /**
  * @category composables/form
- * @class UseNavigationHelpers
- * Use Classe pour gérer les expansions des accordions
+ * Composable pour gérer les expansions des accordions
  */
-export class UseNavigationHelpers {
-  public static expansionPanels () {
-    const { route } = useContext()
-    const panel: Ref<number> = ref(0)
-    const activeAccordionId = route.value.query.accordion
-    onMounted(() => {
-      setTimeout(function () {
-        _.each(document.getElementsByClassName('v-expansion-panel'), (element, index) => {
-          if (element.id == activeAccordionId) {
-            panel.value = index
-          }
-        })
-        if (!panel.value) { panel.value = 0 }
-      }, 0)
-    })
+export function useNavigationHelpers(route: Ref){
+  const panel: Ref<number> = ref(0)
+  const activeAccordionId = route.value.query.accordion
 
-    return {
-      panel
-    }
+  onMounted(() => {
+    setTimeout(function () {
+      _.each(document.getElementsByClassName('v-expansion-panel'), (element, index) => {
+        if (element.id == activeAccordionId) {
+          panel.value = index
+        }
+      })
+      if (!panel.value) { panel.value = 0 }
+    }, 0)
+  })
+
+  return {
+    panel
   }
 }

+ 0 - 51
composables/form/useNextStepFactory.ts

@@ -1,51 +0,0 @@
-import {FORM_STATUS, SUBMIT_TYPE} from "~/types/enums";
-import {useContext} from "@nuxtjs/composition-api";
-import {Store} from "vuex";
-import {AnyJson} from "~/types/interfaces";
-import {Route} from "vue-router";
-
-/**
- * @category composables/form
- * @class UseNextStepFactory
- * Use Classe pour gérer actions post submit
- */
-export default class UseNextStepFactory{
-  private store: Store<any>
-  private router: any
-
-  constructor() {
-    const {store, app: {router}} = useContext()
-
-    this.store = store
-    this.router = router
-  }
-
-
-  invoke(args: any, response: AnyJson){
-    const factory: AnyJson = {}
-    factory[SUBMIT_TYPE.SAVE] = () => this.save(args, response.id)
-    factory[SUBMIT_TYPE.SAVE_AND_BACK] = () => this.saveAndGoTo(args)
-
-    return factory
-  }
-
-  /**
-   * Action Sauvegarder qui redirige vers la page d'edit si on est en mode create
-   * @param route
-   * @param id
-   */
-  save(route: Route, id: number){
-    if(this.store.state.form.formStatus === FORM_STATUS.CREATE){
-      route.path += id
-      this.router.push(route)
-    }
-  }
-
-  /**
-   * Action sauvegarder et route suivante qui redirige vers une route
-   * @param route
-   */
-  saveAndGoTo(route: Route){
-    this.router.push(route)
-  }
-}

+ 11 - 9
composables/form/useValidator.ts

@@ -1,27 +1,28 @@
 import { ref, Ref } from '@nuxtjs/composition-api'
 import VueI18n from 'vue-i18n'
 import { QUERY_TYPE } from '~/types/enums'
-import { DataManager } from '~/types/interfaces'
+import DataProvider from "~/services/data/dataProvider";
 
 /**
  * @category composables/form
- * @class UseValidator
- * Use Classe pour des utils de verifications
+ * Composable pour des utils de verifications
  */
-class UseValidator {
+export function useValidator($dataProvider: DataProvider, i18n: VueI18n) {
+
   /**
    * Use méthode fournissant une fonction pour tester la validité d'un Siret ainsi que la gestion du message d'erreur
    */
-  public static useHandleSiret (i18n: VueI18n, $dataManager: DataManager) {
+  function useHandleSiret() {
     const siretError: Ref<boolean> = ref(false)
     const siretErrorMessage: Ref<string> = ref('')
 
     const checkSiret = async (siret: string) => {
-      const response = await $dataManager.invoke({
+      const response = await $dataProvider.invoke({
         type: QUERY_TYPE.DEFAULT,
         url: '/api/siret-checking',
         id: siret
       })
+
       if (typeof response !== 'undefined') {
         siretError.value = !response.isCorrect
         siretErrorMessage.value = response.isCorrect ? '' : i18n.t('siret_error') as string
@@ -30,13 +31,14 @@ class UseValidator {
         siretErrorMessage.value = ''
       }
     }
-
     return {
       siretError,
       siretErrorMessage,
       checkSiret
     }
   }
-}
 
-export default UseValidator
+  return {
+    useHandleSiret
+  }
+}

+ 5 - 4
pages/organization/address/_id.vue

@@ -10,15 +10,16 @@
 </template>
 
 <script lang="ts">
-import { defineComponent } from '@nuxtjs/composition-api'
+import {defineComponent, useContext} from '@nuxtjs/composition-api'
 import { OrganizationAddressPostal } from '~/models/Organization/OrganizationAddressPostal'
-import {UseDataUtils} from "~/composables/data/useDataUtils";
+import {useDataUtils} from "~/composables/data/useDataUtils";
 
 export default defineComponent({
   name: 'EditOrganizationAddressEdit',
   setup () {
-    const {getItemToEdit} = new UseDataUtils().invoke()
-    const {id, fetchState} = getItemToEdit(OrganizationAddressPostal)
+    const {$dataProvider, route} = useContext()
+    const {getItemToEdit} = useDataUtils($dataProvider)
+    const {id, fetchState} = getItemToEdit(route, OrganizationAddressPostal)
     return {
       id,
       fetchState

+ 6 - 5
pages/organization/address/new.vue

@@ -10,20 +10,21 @@
 </template>
 
 <script lang="ts">
-import {defineComponent} from '@nuxtjs/composition-api'
+import {defineComponent, useContext} from '@nuxtjs/composition-api'
 import { OrganizationAddressPostal } from '~/models/Organization/OrganizationAddressPostal'
 import {AddressPostal} from "~/models/Core/AddressPostal";
-import {UseDataUtils} from "~/composables/data/useDataUtils";
+import {useDataUtils} from "~/composables/data/useDataUtils";
 
 export default defineComponent({
   name: 'NewOrganizationAddress',
   setup () {
-    const {createItem} = new UseDataUtils().invoke()
-    const {create, loading, item} = createItem()
+    const {$dataProvider, store} = useContext()
+    const {createItem} = useDataUtils($dataProvider)
+    const {create, loading, item} = createItem(store, OrganizationAddressPostal)
 
     if(process.client){
       const itemToCreate: OrganizationAddressPostal = new OrganizationAddressPostal({addressPostal: new AddressPostal()})
-      create(itemToCreate, OrganizationAddressPostal)
+      create(itemToCreate)
     }
 
     return {

+ 5 - 4
pages/organization/bank_account/_id.vue

@@ -11,15 +11,16 @@
 </template>
 
 <script lang="ts">
-import { defineComponent } from '@nuxtjs/composition-api'
-import {UseDataUtils} from "~/composables/data/useDataUtils";
+import {defineComponent, useContext} from '@nuxtjs/composition-api'
+import {useDataUtils} from "~/composables/data/useDataUtils";
 import {BankAccount} from "~/models/Core/BankAccount";
 
 export default defineComponent({
   name: 'EditBankAccount',
   setup () {
-    const {getItemToEdit} = new UseDataUtils().invoke()
-    const {id, fetchState} = getItemToEdit(BankAccount)
+    const {$dataProvider, route} = useContext()
+    const {getItemToEdit} = useDataUtils($dataProvider)
+    const {id, fetchState} = getItemToEdit(route, BankAccount)
 
     return {
       id,

+ 5 - 5
pages/organization/bank_account/new.vue

@@ -11,19 +11,19 @@
 
 <script lang="ts">
 import {defineComponent, useContext} from '@nuxtjs/composition-api'
-import {UseDataUtils} from "~/composables/data/useDataUtils";
+import {useDataUtils} from "~/composables/data/useDataUtils";
 import {BankAccount} from "~/models/Core/BankAccount";
 
 export default defineComponent({
   name: 'NewBankAccount',
   setup (){
-    const {store} = useContext()
-    const {createItem} = new UseDataUtils().invoke()
-    const {create, loading, item} = createItem()
+    const {$dataProvider, store} = useContext()
+    const {createItem} = useDataUtils($dataProvider)
+    const {create, loading, item} = createItem(store, BankAccount)
 
     if(process.client){
       const itemToCreate: BankAccount = new BankAccount({organization:[`/api/organizations/${store.state.profile.organization.id}`]})
-      create(itemToCreate, BankAccount)
+      create(itemToCreate)
     }
 
     return {

+ 5 - 4
pages/organization/contact_points/_id.vue

@@ -11,15 +11,16 @@
 </template>
 
 <script lang="ts">
-import { defineComponent } from '@nuxtjs/composition-api'
+import {defineComponent, useContext} from '@nuxtjs/composition-api'
 import { ContactPoint } from '~/models/Core/ContactPoint'
-import {UseDataUtils} from "~/composables/data/useDataUtils";
+import {useDataUtils} from "~/composables/data/useDataUtils";
 
 export default defineComponent({
   name: 'EditContactPoint',
   setup () {
-    const {getItemToEdit} = new UseDataUtils().invoke()
-    const {id, fetchState} = getItemToEdit(ContactPoint)
+    const {$dataProvider, route} = useContext()
+    const {getItemToEdit} = useDataUtils($dataProvider)
+    const {id, fetchState} = getItemToEdit(route, ContactPoint)
 
     return {
       id,

+ 5 - 5
pages/organization/contact_points/new.vue

@@ -12,18 +12,18 @@
 <script lang="ts">
 import {defineComponent, useContext} from '@nuxtjs/composition-api'
 import {ContactPoint} from "~/models/Core/ContactPoint";
-import {UseDataUtils} from "~/composables/data/useDataUtils";
+import {useDataUtils} from "~/composables/data/useDataUtils";
 
 export default defineComponent({
   name: 'NewContactPoint',
   setup (){
-    const {store} = useContext()
-    const {createItem} = new UseDataUtils().invoke()
-    const {create, loading, item} = createItem()
+    const {$dataProvider, store} = useContext()
+    const {createItem} = useDataUtils($dataProvider)
+    const {create, loading, item} = createItem(store, ContactPoint)
 
     if(process.client){
       const itemToCreate: ContactPoint = new ContactPoint({organization:[`/api/organizations/${store.state.profile.organization.id}`]})
-      create(itemToCreate, ContactPoint)
+      create(itemToCreate)
     }
 
     return {

+ 8 - 10
pages/organization/index.vue

@@ -454,11 +454,11 @@ import { OrganizationAddressPostal } from '~/models/Organization/OrganizationAdd
 import { ContactPoint } from '~/models/Core/ContactPoint'
 import { BankAccount } from '~/models/Core/BankAccount'
 import { repositoryHelper } from '~/services/store/repository'
-import UseValidator from '~/composables/form/useValidator'
-import { UseNavigationHelpers } from '~/composables/form/useNavigationHelpers'
+import {useValidator} from "~/composables/form/useValidator";
+import {useNavigationHelpers} from '~/composables/form/useNavigationHelpers'
 import I18N from '~/services/utils/i18n'
 import {Country} from "~/models/Core/Country";
-import {UseTypeOfPractice} from "~/composables/data/useTypeOfPractice";
+import {useTypeOfPracticeProvider} from "~/composables/data/useTypeOfPracticeProvider";
 import ModelsUtils from "~/services/utils/modelsUtils";
 import {NetworkOrganization} from "~/models/Network/NetworkOrganization";
 import {OrganizationArticle} from "~/models/Organization/OrganizationArticle";
@@ -466,21 +466,19 @@ import {OrganizationArticle} from "~/models/Organization/OrganizationArticle";
 export default defineComponent({
   name: 'OrganizationParent',
   setup () {
-    const { store, app: { i18n }, $dataProvider } = useContext()
+    const { store, app: { i18n }, $dataProvider, route } = useContext()
+    const {typeOfPractices, fetchState:typeOfPracticesFetchingState} = useTypeOfPracticeProvider($dataProvider)
+    const { panel } = useNavigationHelpers(route)
+    const { siretError, siretErrorMessage, checkSiret } = useValidator($dataProvider, i18n).useHandleSiret()
+
     const organizationProfile = reactive($organizationProfile(store))
     const id: number = store.state.profile.organization.id
 
-    const { siretError, siretErrorMessage, checkSiret } = UseValidator.useHandleSiret(i18n, $dataProvider)
-
-    const {typeOfPractices, fetchState:typeOfPracticesFetchingState} = new UseTypeOfPractice().getAll()
-
     const checkSiretHook = async (siret: string, field: string, updateRepository: any) => {
       await checkSiret(siret)
       if (!siretError.value) { updateRepository(siret, field) }
     }
 
-    const { panel } = UseNavigationHelpers.expansionPanels()
-
     const formatPhoneNumber = (number: string): string => {
       return I18N.formatPhoneNumber(number)
     }

+ 5 - 4
plugins/directives.js

@@ -1,12 +1,12 @@
 import Vue from "vue";
 
 /**
- * Register a specific event: v-click-outside
+ * Register a specific event: v-click-out
  *
  * Example for some modale div:
  *
  *   <template>
- *     <div v-click-outside="onClickOutside">
+ *     <div v-click-out="onClickOutside">
   *      <slot></slot>
   *    </div>
  *   </template>
@@ -18,11 +18,12 @@ import Vue from "vue";
  *     }
  *   </script>
  */
-Vue.directive("click-outside", {
+Vue.directive("click-out", {
   bind: function (el, binding, vnode) {
     el.clickOutsideEvent = (event) => {
       if (!(el === event.target || el.contains(event.target))) {
-        vnode.context[binding.expression](event)
+        if(vnode.context[binding.expression])
+          vnode.context[binding.expression](event)
       }
     };
     document.body.addEventListener("click", el.clickOutsideEvent);

+ 6 - 6
services/connection/urlBuilder.ts

@@ -1,5 +1,5 @@
 import {Model} from '@vuex-orm/core'
-import {DataPersisterArgs, DataProviderArgs, ImageArgs, UrlArgs} from '~/types/interfaces'
+import {ImageArgs, UrlArgs} from '~/types/interfaces'
 import {QUERY_TYPE} from '~/types/enums'
 import {repositoryHelper} from '~/services/store/repository'
 import TypesTesting from "~/services/utils/typesTesting";
@@ -60,11 +60,11 @@ class UrlBuilder {
    * @param baseUrl
    * @private
    */
-  private static getDefaultUrl (url?: string, baseUrl: string = ''): string {
+  private static getDefaultUrl (url?: string, baseUrl: string|null = null): string {
     if (!url) {
       throw new Error('no url')
     }
-    return UrlBuilder.concat(baseUrl, url)
+    return baseUrl ? UrlBuilder.concat(baseUrl, url) : url
   }
 
   /**
@@ -74,7 +74,7 @@ class UrlBuilder {
    */
   private static getEnumUrl (enumType?: string): string {
     if (typeof enumType === 'undefined') {
-      throw new TypeError('enumType must be defined')
+      throw new Error('enumType must be defined')
     }
     return UrlBuilder.concat(UrlBuilder.ROOT, 'enum', enumType)
   }
@@ -90,14 +90,14 @@ class UrlBuilder {
    */
   private static getModelUrl (model?: typeof Model, rootModel?: typeof Model, rootId?: number): string {
     if (typeof model === 'undefined') {
-      throw new TypeError('model must be defined')
+      throw new Error('model must be defined')
     }
 
     const entity = repositoryHelper.getEntity(model)
 
     if (typeof rootModel !== 'undefined') {
       if (typeof rootId === 'undefined') {
-        throw new TypeError('Root ID must be defined')
+        throw new Error('Root ID must be defined')
       }
 
       const rootUrl = UrlBuilder.getModelUrl(rootModel) as string

+ 23 - 0
tests/unit/composables/data/useAccessesProvider.spec.ts

@@ -0,0 +1,23 @@
+import DataProvider from "~/services/data/dataProvider";
+import {useAccessesProvider} from "~/composables/data/useAccessesProvider";
+jest.mock('~/services/data/dataProvider')
+
+let useMyAccessesProviderMount:any
+const dataproviderMock = DataProvider as jest.Mocked<typeof DataProvider>
+
+beforeAll(() => {
+  useMyAccessesProviderMount = useAccessesProvider(dataproviderMock.prototype)
+})
+
+describe('getPhysicalByFullName()', () => {
+  it('should return [] if no research params', async () => {
+    const result = await useMyAccessesProviderMount.getPhysicalByFullName()
+    expect(result).toHaveLength(0)
+  })
+
+  it('should return an array of results if there is research param', async () => {
+    dataproviderMock.prototype.invoke = jest.fn().mockReturnValue({data:['foo bar']})
+    const result = await useMyAccessesProviderMount.getPhysicalByFullName('foo bar')
+    expect(result).toStrictEqual(['foo bar'])
+  })
+})

+ 54 - 0
tests/unit/composables/data/useAddressPostalUtils.spec.ts

@@ -0,0 +1,54 @@
+import DataProvider from "~/services/data/dataProvider";
+import {useAddressPostalUtils} from "~/composables/data/useAddressPostalUtils";
+jest.mock('~/services/data/dataProvider')
+
+let useAddressPostalUtilsMount:any
+const dataproviderMock = DataProvider as jest.Mocked<typeof DataProvider>
+
+beforeAll(() => {
+  useAddressPostalUtilsMount = useAddressPostalUtils(dataproviderMock.prototype)
+})
+
+describe('searchFunction()', () => {
+  const apiResponse = {
+    data:{
+      features: [
+        {
+          properties: {
+            id: 1, postcode: '00001', city: 'foo'
+          }
+        },
+        {
+          properties: {
+            id: 2, postcode: '00002', city: 'bar'
+          }
+        }
+      ]
+    }
+  }
+
+  it('should return [] if no research params', async () => {
+    const result = await useAddressPostalUtilsMount.searchFunction(null, 'field')
+    expect(result).toHaveLength(0)
+  })
+
+  it('should return response with default postcode value', async () => {
+    dataproviderMock.prototype.invoke = jest.fn().mockReturnValue(apiResponse)
+    const result = await useAddressPostalUtilsMount.searchFunction('foo', 'postalCode')
+    expect(result).toStrictEqual([
+      {id: 0, postcode: 'foo', city: null},
+      {id: 1, postcode: '00001', city: 'foo'},
+      {id: 2, postcode: '00002', city: 'bar'}
+    ])
+  })
+
+  it('should return response with default city value', async () => {
+    dataproviderMock.prototype.invoke = jest.fn().mockReturnValue(apiResponse)
+    const resultCity = await useAddressPostalUtilsMount.searchFunction('foo', 'addressPostal.addressCity')
+    expect(resultCity).toStrictEqual([
+      {id: 0, postcode: null, city: 'foo'},
+      {id: 1, postcode: '00001', city: 'foo'},
+      {id: 2, postcode: '00002', city: 'bar'}
+    ])
+  })
+})

+ 30 - 0
tests/unit/composables/data/useDataUtils.spec.ts

@@ -0,0 +1,30 @@
+import DataProvider from "~/services/data/dataProvider";
+import {useDataUtils} from "~/composables/data/useDataUtils";
+import {Organization} from "~/models/Organization/Organization";
+import {Ref, ref} from "@nuxtjs/composition-api";
+import {mountComposition} from "~/tests/unit/Helpers";
+jest.mock('~/services/data/dataProvider')
+
+
+let useDataUtilsMount:any
+const dataproviderMock = DataProvider as jest.Mocked<typeof DataProvider>
+
+beforeAll(() => {
+  useDataUtilsMount = useDataUtils(dataproviderMock.prototype)
+})
+
+describe('getItemToEdit()', () => {
+  let route:Ref<{}>
+
+  beforeAll(() => {
+    const component = mountComposition(() => {
+      route = ref({})
+    });
+  })
+
+
+  it('should throw an error if route id is empty', () => {
+    route.value = {params: 'foo'}
+    expect(() => useDataUtilsMount.getItemToEdit(route, Organization)).toThrowError('id must be exist')
+  })
+})

+ 7 - 5
tests/unit/composables/data/useMyProfile.spec.ts

@@ -1,13 +1,17 @@
-import { createStore, initLocalVue, mountComposition } from '~/tests/unit/Helpers'
+import { createStore, initLocalVue } from '~/tests/unit/Helpers'
 import { accessProfile as accessModule } from '~/tests/unit/fixture/state/profile'
 import { AnyStore } from '~/types/interfaces'
 import { useMyProfile } from '~/composables/data/useMyProfile'
 import { repositoryHelper } from '~/services/store/repository'
 import {$accessProfile} from "~/services/profile/accessProfile";
 import {_exportedForTesting} from "~/composables/data/useMyProfile"
+import DataPersister from "~/services/data/dataPersister";
+jest.mock('~/services/data/dataPersister')
+
 let store:AnyStore
 let useMyProfileMount:any
 let repositoryHelperMock = repositoryHelper as jest.Mocked<typeof repositoryHelper>
+const DataPersisterMock = DataPersister as jest.Mocked<typeof DataPersister>
 
 beforeAll(() => {
   store = createStore()
@@ -20,14 +24,12 @@ beforeAll(() => {
   $accessProfileMock.getCurrentAccessId = jest.fn().mockReturnValue(1)
   repositoryHelperMock.findItemFromModel = jest.fn()
 
-  const component = mountComposition(() => {
-    useMyProfileMount = useMyProfile()
-  });
+  useMyProfileMount = useMyProfile(DataPersisterMock.prototype, store)
 })
 
 describe('setActivityYear()', () => {
   it('should throw an error if year is negative nor eq to 0', () => {
-   expect(() => useMyProfileMount.setActivityYear(-1)).toThrow()
+   expect(() => useMyProfileMount.setActivityYear(-1)).toThrowError('year must be positive')
   })
   it('should call updateStoreFromField', () => {
     repositoryHelperMock.updateStoreFromField = jest.fn()

+ 28 - 0
tests/unit/composables/form/useError.spec.ts

@@ -0,0 +1,28 @@
+import {useError} from "~/composables/form/useError";
+import {createStore} from "~/tests/unit/Helpers";
+import {form as formModule} from "~/tests/unit/fixture/state/profile";
+import {AnyStore} from "~/types/interfaces";
+let store:AnyStore
+
+let useErrorMount:any
+const emit = jest.fn()
+
+beforeAll(() => {
+  store = createStore()
+  store.registerModule('form', formModule)
+  store.commit('form/setViolations', ['foo', 'bar'])
+
+  useErrorMount = useError('foo', emit, store)
+})
+
+describe('onChange()', () => {
+  it('delete foo inside store', () => {
+    useErrorMount.onChange('bob')
+    expect(store.state.form.violations).toHaveLength(1)
+  })
+
+  it('emit is called', () => {
+    useErrorMount.onChange('bob')
+    expect(emit).toHaveBeenCalled
+  })
+})

+ 44 - 19
tests/unit/composables/form/useForm.spec.ts

@@ -1,40 +1,65 @@
 import {createStore, initLocalVue, mountComposition} from '~/tests/unit/Helpers'
 import { form } from '~/tests/unit/fixture/state/profile'
-import { $useForm, UseForm } from '~/composables/form/useForm'
+import {useForm} from "~/composables/form/useForm";
 import { AnyStore } from '~/types/interfaces'
+import {FORM_STATUS, SUBMIT_TYPE} from "~/types/enums";
+import {Route} from "vue-router";
 
 let store: AnyStore
+let useFormMount:any
 
 beforeAll(() => {
   store = createStore()
   store.registerModule('form', form)
   initLocalVue({store: store})
-})
 
+  const component = mountComposition(() => {
+    useFormMount = useForm(store)
+  });
+})
 
 describe('markFormAsDirty()', () => {
-  it('should call addEventListener one time', async () => {
-    const spy = jest.spyOn(UseForm.prototype as any, 'addEventListener')
-    spy.mockImplementation(() => {})
-
-    const component = mountComposition(() => {
-      const {markFormAsDirty} = $useForm();
-      markFormAsDirty()
-      expect(spy).toHaveBeenCalled()
-    });
+  it('should commit the form state dirty to true', async () => {
+    useFormMount.markAsDirty()
+    expect(store.state.form.dirty).toBeTruthy()
   })
 })
 
 describe('markAsNotDirty()', () => {
-  it('should call clearEventListener one time', async () => {
-    const spy = jest.spyOn(UseForm.prototype as any, 'clearEventListener')
-    spy.mockImplementation(() => {})
+  it('should commit the form state dirty to false', async () => {
+    useFormMount.markAsNotDirty()
+    expect(store.state.form.dirty).toBeFalsy()
+  })
+})
 
-    const component = mountComposition(() => {
-      const { markFormAsNotDirty } = $useForm()
-      markFormAsNotDirty()
-      expect(spy).toHaveBeenCalled()
-    });
+describe('nextStepFactory()', () => {
+  const route: Route = {
+    path: 'ma_route',
+    hash: 'hash',
+    query: {},
+    params: {},
+    fullPath: 'ma_route',
+    matched: []
+  }
 
+  it('should push the new route when the action is save and back', async () => {
+    const router: any = []
+    useFormMount.nextStepFactory(route, {}, router)[SUBMIT_TYPE.SAVE_AND_BACK]()
+    expect(router).toHaveLength(1)
+  })
+
+  it('should dont push the new route when the action is save and the form is EDIT', async () => {
+    store.commit('form/setFormStatus', FORM_STATUS.EDIT)
+    const router: any = []
+    useFormMount.nextStepFactory(route, {}, router)[SUBMIT_TYPE.SAVE]()
+    expect(router).toHaveLength(0)
+  })
+
+  it('should push the new route when the action is save and the form is CREATE', async () => {
+    store.commit('form/setFormStatus', FORM_STATUS.CREATE)
+    const router: any = []
+    useFormMount.nextStepFactory(route, {}, router)[SUBMIT_TYPE.SAVE]()
+    expect(router).toHaveLength(1)
   })
 })
+

+ 11 - 6
tests/unit/composables/form/useValidator.spec.ts

@@ -1,11 +1,16 @@
 import VueI18n from 'vue-i18n'
-import UseValidator from '~/composables/form/useValidator'
+import {useValidator} from '~/composables/form/useValidator'
 import DataProvider from '~/services/data/dataProvider'
-
 jest.mock('~/services/data/dataProvider')
+
+let useValidatorMount:any
 const DataProviderMock = DataProvider as jest.MockedClass<typeof DataProvider>
 const VueI18nMock = VueI18n as jest.MockedClass<typeof VueI18n>
 
+beforeAll(() => {
+  useValidatorMount = useValidator(DataProviderMock.prototype, VueI18nMock.prototype)
+})
+
 describe('useHandleSiret()', () => {
   beforeEach(() => {
     // Efface toutes les instances et les appels au constructeur et à toutes les méthodes :
@@ -13,14 +18,14 @@ describe('useHandleSiret()', () => {
   })
 
   it('should init 3 parameters correctly', async () => {
-    const siretResponse = UseValidator.useHandleSiret(VueI18nMock.prototype, DataProviderMock.prototype)
+    const siretResponse = useValidatorMount.useHandleSiret()
     expect(siretResponse.siretErrorMessage.value).toEqual('')
     expect(siretResponse.siretError.value).toBeFalsy()
     expect(siretResponse.checkSiret).toBeDefined()
   })
 
   it('should update parameters if Siret is correct', async () => {
-    const siretResponse = UseValidator.useHandleSiret(VueI18nMock.prototype, DataProviderMock.prototype)
+    const siretResponse = useValidatorMount.useHandleSiret()
     DataProviderMock.prototype.invoke.mockImplementation(async () => {
       return { isCorrect: true }
     })
@@ -30,7 +35,7 @@ describe('useHandleSiret()', () => {
   })
 
   it('should update parameters if Siret is not correct', async () => {
-    const siretResponse = UseValidator.useHandleSiret(VueI18nMock.prototype, DataProviderMock.prototype)
+    const siretResponse = useValidatorMount.useHandleSiret()
     DataProviderMock.prototype.invoke.mockImplementation(async () => {
       return { isCorrect: false }
     })
@@ -41,7 +46,7 @@ describe('useHandleSiret()', () => {
   })
 
   it('should update parameters if there is no response during the check siret', async () => {
-    const siretResponse = UseValidator.useHandleSiret(VueI18nMock.prototype, DataProviderMock.prototype)
+    const siretResponse = useValidatorMount.useHandleSiret()
     DataProviderMock.prototype.invoke.mockImplementation(jest.fn())
     await siretResponse.checkSiret('123456')
     expect(siretResponse.siretErrorMessage.value).toEqual('')

+ 1 - 1
tests/unit/services/connection/connection.spec.ts

@@ -61,7 +61,7 @@ describe('invoke()', () => {
 
   describe('put()', () => {
     it('should throw an error if data missing', async () => {
-      expect(() => Connection.invoke(HTTP_METHOD.PUT, 'users', { type: QUERY_TYPE.MODEL })).toThrow()
+      expect(() => Connection.invoke(HTTP_METHOD.PUT, 'users', { type: QUERY_TYPE.MODEL })).toThrowError('*args* is not a dataPersisterArgs')
     })
 
     it('should return item data', async () => {

+ 6 - 6
tests/unit/services/connection/urlBuilder.spec.ts

@@ -10,14 +10,14 @@ describe('invoke()', () => {
     it('should throw an error if URL is missing', () => {
       expect(() => UrlBuilder.build({
         type: QUERY_TYPE.DEFAULT
-      })).toThrow()
+      })).toThrowError('no url')
     })
 
     it('should return the URL concat with Root URL', () => {
       expect(UrlBuilder.build({
         type: QUERY_TYPE.DEFAULT,
         url: 'users'
-      })).toEqual('/users')
+      })).toEqual('users')
     })
   })
 
@@ -25,7 +25,7 @@ describe('invoke()', () => {
     it('should throw an error if enumType is missing', () => {
       expect(() => UrlBuilder.build({
         type: QUERY_TYPE.ENUM
-      })).toThrow()
+      })).toThrowError('enumType must be defined')
     })
 
     it('should return the Enum URL concat with Root URL', () => {
@@ -40,7 +40,7 @@ describe('invoke()', () => {
     it('should throw an error if model is missing', () => {
       expect(() => UrlBuilder.build({
         type: QUERY_TYPE.MODEL
-      })).toThrow()
+      })).toThrowError('model must be defined')
     })
 
     it('should return the Model URL concat with Root URL', () => {
@@ -61,7 +61,7 @@ describe('invoke()', () => {
         type: QUERY_TYPE.MODEL,
         model: User,
         rootModel: Organization
-      })).toThrow()
+      })).toThrowError('Root ID must be defined')
     })
 
     it('should return the Root Model URL, Model Url concat with Root URL', () => {
@@ -96,7 +96,7 @@ describe('invoke()', () => {
     it('should throw an error if imgArgs is missing', () => {
       expect(() => UrlBuilder.build({
         type: QUERY_TYPE.IMAGE
-      })).toThrow()
+      })).toThrowError('*args* is not a dataProviderArgs')
     })
 
     it('should return the File download URL concat with Root URL', () => {

+ 1 - 1
tests/unit/services/rights/abilitiesUtils.spec.ts

@@ -92,7 +92,7 @@ describe('getAbilitiesByRoles()', () => {
 
 describe('getAbilitiesByConfig()', () => {
   it('should throw an error if the path is wrong', () => {
-    expect(() => abilitiesUtils.getAbilitiesByConfig('wrong_path')).toThrow()
+    expect(() => abilitiesUtils.getAbilitiesByConfig('wrong_path')).toThrowError()
   })
 })
 

+ 1 - 1
tests/unit/services/serializer/denormalizer/baseDenormalizer.spec.ts

@@ -5,6 +5,6 @@ class DummyDenormalizer extends BaseDenormalizer {}
 
 describe('support()', () => {
   it('should trow an error if support doesnt defined in DummyDenormalizer', () => {
-    expect(() => DummyDenormalizer.support(DENORMALIZER_TYPE.HYDRA)).toThrow()
+    expect(() => DummyDenormalizer.support(DENORMALIZER_TYPE.HYDRA)).toThrowError('Not implemented')
   })
 })

+ 1 - 1
tests/unit/services/serializer/denormalizer/yaml.spec.ts

@@ -14,7 +14,7 @@ describe('support()', () => {
 describe('denormalize()', () => {
   it('should throw an error if file doesnt exist', () => {
     const path = './tests/unit/fixture/files/not_exist_file.yaml'
-    expect(() => Yaml.denormalize({ path })).toThrow()
+    expect(() => Yaml.denormalize({ path })).toThrowError()
   })
 
   it('should parse a Yaml file and return a JSON Object', () => {

+ 1 - 1
tests/unit/services/serializer/normalizer/baseNormalizer.spec.ts

@@ -5,6 +5,6 @@ class DummyNormalizer extends BaseNormalizer {}
 
 describe('support()', () => {
   it('should trow an error if support doesnt defined in DummyNoramlizer', () => {
-    expect(() => DummyNormalizer.support(QUERY_TYPE.ENUM)).toThrow()
+    expect(() => DummyNormalizer.support(QUERY_TYPE.ENUM)).toThrowError('Not implemented')
   })
 })

+ 2 - 2
tests/unit/services/store/query.spec.ts

@@ -14,7 +14,7 @@ beforeEach(() => {
 
 describe('getItem()', () => {
   it('should throw an error if the Item is not found', () => {
-    expect(() => queryHelper.getItem(query, 1)).toThrow()
+    expect(() => queryHelper.getItem(query, 1)).toThrowError('item not found')
   })
 
   it('should return an Item', () => {
@@ -43,7 +43,7 @@ describe('getCollection()', () => {
 
 describe('getFlattenEntry()', () => {
   it('should throw an error if the Item is not found', () => {
-    expect(() => queryHelper.getFlattenEntry(query, 1)).toThrow()
+    expect(() => queryHelper.getFlattenEntry(query, 1)).toThrowError('item not found')
   })
 
   it('should find an Item and return a JSON flatten object', () => {

+ 3 - 3
tests/unit/services/store/repository.spec.ts

@@ -27,7 +27,7 @@ describe('getEntity()', () => {
 
 describe('updateStoreFromField()', () => {
   it('should throw an error if the field doest exist', () => {
-    expect(() => repositoryHelper.updateStoreFromField(User, {}, 'Foo Bar', 'name')).toThrow()
+    expect(() => repositoryHelper.updateStoreFromField(User, {}, 'Foo Bar', 'name')).toThrowError('field not found')
   })
 
   it('should update the store', () => {
@@ -41,7 +41,7 @@ describe('updateStoreFromField()', () => {
 
 describe('persist()', () => {
   it('should throw an error if the entry is empty', () => {
-    expect(() => repositoryHelper.persist(User, {})).toThrow()
+    expect(() => repositoryHelper.persist(User, {})).toThrowError()
   })
 
   it('should persist the entry inside the store', () => {
@@ -54,7 +54,7 @@ describe('persist()', () => {
 
 describe('findItemFromModel()', () => {
   it('should throw an error if the Item is not found', () => {
-    expect(() => repositoryHelper.findItemFromModel(User, 1)).toThrow()
+    expect(() => repositoryHelper.findItemFromModel(User, 1)).toThrowError('Item not found')
   })
 
   it('should return the correct item of the Model', () => {

+ 1 - 1
tests/unit/services/utils/modelsUtils.spec.ts

@@ -6,7 +6,7 @@ describe('extractIdFromUri()', () => {
   })
 
   it('should return Error if uri is not an number', () => {
-    expect(() => ModelsUtils.extractIdFromUri('/api/person/id')).toThrow()
+    expect(() => ModelsUtils.extractIdFromUri('/api/person/id')).toThrowError('id is not a number')
   })
 
   it('should return the id s uri', () => {

+ 3 - 3
tests/unit/services/utils/objectProperties.spec.ts

@@ -3,7 +3,7 @@ import { AnyJson } from '~/types/interfaces'
 
 describe('cloneAndFlatten()', () => {
   it('should throw an error if args is not an object', () =>
-    expect(() => $objectProperties.cloneAndFlatten(String as AnyJson)).toThrow()
+    expect(() => $objectProperties.cloneAndFlatten(String as AnyJson)).toThrowError('Expecting an object parameter')
   )
 
   it('should return same values for flat objects', () =>
@@ -39,7 +39,7 @@ describe('cloneAndFlatten()', () => {
 
 describe('cloneAndNest()', () => {
   it('should throw an error if args is not an object', () =>
-    expect(() => $objectProperties.cloneAndNest(String as AnyJson)).toThrow()
+    expect(() => $objectProperties.cloneAndNest(String as AnyJson)).toThrowError('Expecting an object parameter')
   )
 
   it('should return same values for flat objects', () =>
@@ -65,7 +65,7 @@ describe('cloneAndNest()', () => {
 
 describe('sortObjectByKey()', () => {
   it('should throw an error if args is not an array', () =>
-    expect(() => $objectProperties.sortObjectByKey('foo')).toThrow()
+    expect(() => $objectProperties.sortObjectByKey('foo')).toThrowError('Expecting an object parameter')
   )
   it('should sort an array by his keys', () =>
     expect($objectProperties.sortObjectByKey({ b: 1, d: 2, c: 3, a: 4 })).toStrictEqual({ a: 4, b: 1, c: 3, d: 2 })