Browse Source

refactor menu builders

Olivier Massot 1 year ago
parent
commit
089f4043fc

+ 30 - 18
services/layout/menuBuilder/abstractMenuBuilder.ts

@@ -91,7 +91,7 @@ abstract class AbstractMenuBuilder implements MenuBuilder {
     label: string,
     icon?: IconItem,
     to: string = '',
-    type: MENU_LINK_TYPE = MENU_LINK_TYPE.INTERNAL
+    type: MENU_LINK_TYPE = MENU_LINK_TYPE.INTERNAL,
   ): MenuItem {
     let url: string
 
@@ -131,24 +131,36 @@ abstract class AbstractMenuBuilder implements MenuBuilder {
     return builder.build()
   }
 
-  protected addChildItemIfAllowed(
-    children: MenuItems,
-    pageName: string,
-    icon?: IconItem,
-  ) {
-    if (this.ability.can('display', pageName)) {
-      const to = this.router.resolve({ name: pageName }).href
-      if (!to) {
-        throw new Error('unknown page name : ' + pageName)
+  /**
+   * Make a list of MenuItems according to user abilities
+   *
+   * @param items
+   * @protected
+   */
+  protected makeChildren(
+    items: Array<{ pageName: string; icon?: string }>,
+  ): MenuItems {
+    const children: MenuItems = []
+
+    items.forEach((item) => {
+      const { pageName, icon } = item
+
+      if (this.ability.can('display', pageName)) {
+        const to = this.router.resolve({ name: pageName }).href
+        if (!to) {
+          throw new Error('unknown page name : ' + pageName)
+        }
+        children.push({
+          icon: icon ? { name: icon } : undefined,
+          label: pageName,
+          to,
+          type: MENU_LINK_TYPE.INTERNAL,
+          active: false,
+        })
       }
-      children.push({
-        icon,
-        label: pageName,
-        to,
-        type: MENU_LINK_TYPE.INTERNAL,
-        active: false,
-      })
-    }
+    })
+
+    return children
   }
 }
 export default AbstractMenuBuilder

+ 1 - 1
services/layout/menuBuilder/accountMenuBuilder.ts

@@ -136,7 +136,7 @@ export default class AccountMenuBuilder extends AbstractMenuBuilder {
       )
     }
 
-    this.addChildItemIfAllowed(children, 'subscription_page')
+    children.push(...this.makeChildren([{ pageName: 'subscription_page' }]))
 
     if (this.ability.can('display', 'my_bills_page')) {
       children.push(

+ 14 - 45
services/layout/menuBuilder/parametersMenuBuilder.ts

@@ -11,51 +11,20 @@ export default class ParametersMenuBuilder extends AbstractMenuBuilder {
    * Construit le menu Header Configuration, ou null si aucune page accessible
    */
   build(): MenuGroup | null {
-    const children: MenuItems = []
-
-    this.addChildItemIfAllowed(children, 'parameters_general_page', {
-      name: 'fas fa-gears',
-    })
-
-    this.addChildItemIfAllowed(children, 'parameters_website_page', {
-      name: 'fas fa-globe-americas',
-    })
-
-    this.addChildItemIfAllowed(children, 'parameters_teaching_page', {
-      name: 'fas fa-school',
-    })
-
-    this.addChildItemIfAllowed(children, 'parameters_intranet_page', {
-      name: 'fas fa-arrows-down-to-people',
-    })
-
-    this.addChildItemIfAllowed(children, 'parameters_education_notation_page', {
-      name: 'fas fa-graduation-cap',
-    })
-
-    this.addChildItemIfAllowed(children, 'parameters_bulletin_page', {
-      name: 'fas fa-file-lines',
-    })
-
-    this.addChildItemIfAllowed(children, 'parameters_education_timings_page', {
-      name: 'fas fa-clock',
-    })
-
-    this.addChildItemIfAllowed(children, 'parameters_residence_areas_page', {
-      name: 'fas fa-location-dot',
-    })
-
-    this.addChildItemIfAllowed(children, 'parameters_attendances_page', {
-      name: 'fas fa-user-times',
-    })
-
-    this.addChildItemIfAllowed(children, 'parameters_sms_page', {
-      name: 'fas fa-mobile',
-    })
-
-    this.addChildItemIfAllowed(children, 'parameters_super_admin_page', {
-      name: 'fas fa-user-gear',
-    })
+    // prettier-ignore
+    const children: MenuItems = this.makeChildren([
+      { pageName: 'parameters_general_page', icon: 'fas fa-gears' },
+      { pageName: 'parameters_website_page', icon: 'fas fa-globe-americas' },
+      { pageName: 'parameters_teaching_page', icon: 'fas fa-school' },
+      { pageName: 'parameters_intranet_page', icon: 'fas fa-arrows-down-to-people' },
+      { pageName: 'parameters_education_notation_page', icon: 'fas fa-graduation-cap' },
+      { pageName: 'parameters_bulletin_page', icon: 'fas fa-file-lines' },
+      { pageName: 'parameters_education_timings_page', icon: 'fas fa-clock' },
+      { pageName: 'parameters_residence_areas_page', icon: 'fas fa-location-dot' },
+      { pageName: 'parameters_attendances_page', icon: 'fas fa-user-times' },
+      { pageName: 'parameters_sms_page', icon: 'fas fa-mobile' },
+      { pageName: 'parameters_super_admin_page', icon: 'fas fa-user-gear' },
+    ])
 
     if (children.length > 0) {
       return this.createGroup('parameters', undefined, children)

+ 48 - 1
tests/units/services/layout/menuBuilder/abstractMenuBuilder.test.ts

@@ -6,6 +6,7 @@ import AbstractMenuBuilder from '~/services/layout/menuBuilder/abstractMenuBuild
 import type { IconItem, MenuGroup, MenuItem, MenuItems } from '~/types/layout'
 import { MENU_LINK_TYPE } from '~/types/enum/layout'
 import type { AccessProfile, organizationState } from '~/types/interfaces'
+import {result} from 'lodash-es';
 
 class TestableAbstractMenuBuilder extends AbstractMenuBuilder {
   static readonly menuName = 'TestableMenu'
@@ -35,6 +36,14 @@ class TestableAbstractMenuBuilder extends AbstractMenuBuilder {
   public buildSubmenu(menuBuilder: typeof AbstractMenuBuilder) {
     return super.buildSubmenu(menuBuilder)
   }
+
+  public addChildItemIfAllowed(
+    children: MenuItems,
+    pageName: string,
+    icon?: IconItem,
+  ) {
+    return super.addChildItemIfAllowed(children, pageName, icon)
+  }
 }
 
 let runtimeConfig: RuntimeConfig
@@ -167,5 +176,43 @@ describe('buildSubmenu', () => {
 })
 
 describe('addChildItemIfAllowed', () => {
-  test('simple call', () => {})
+  test('simple call', () => {
+    ability.can = vi.fn(() => true)
+    // @ts-ignore
+    router.resolve = vi.fn(() => {
+      return { href: 'foo' }
+    })
+
+    const children: MenuItems = []
+
+    menuBuilder.addChildItemIfAllowed(children, 'foo_page')
+
+    expect(children).toEqual([
+      { label: 'foo_page', icon: undefined, to: 'foo', type: 0, active: false },
+    ])
+  })
+  test('not allowed', () => {
+    ability.can = vi.fn(() => false)
+
+    const children: MenuItems = []
+
+    menuBuilder.addChildItemIfAllowed(children, 'foo_page')
+
+    expect(children).toEqual([])
+  })
+  test('unknown page name', () => {
+    ability.can = vi.fn(() => true)
+    // @ts-ignore
+    router.resolve = vi.fn(() => {
+      return null
+    })
+
+    const children: MenuItems = []
+
+    expect(() =>
+      menuBuilder.addChildItemIfAllowed(children, 'foo_page'),
+    ).toThrowError()
+
+    expect(children).toEqual([])
+  })
 })

+ 25 - 1
tests/units/services/layout/menuBuilder/accountMenuBuilder.test.ts

@@ -45,7 +45,9 @@ describe('build', () => {
   test('has all items (mister)', () => {
     ability.can = vi.fn(() => true)
     // @ts-ignore
-    router.resolve = vi.fn(() => 'foo')
+    router.resolve = vi.fn(() => {
+      return {href: 'foo '}
+    })
 
     // Should return a MenuGroup
     const result = menuBuilder.build() as MenuGroup
@@ -293,6 +295,28 @@ describe('build', () => {
     })
   })
 
+  test('has only rights for menu subscription', () => {
+    ability.can = vi.fn(
+      (action: string, subject: string) =>
+        action === 'display' && subject === 'subscription_page',
+    )
+    // @ts-ignore
+    router.resolve = vi.fn(() => {
+      return { href: 'subscription' }
+    })
+
+    // @ts-ignore
+    expect(menuBuilder.build().children[0]).toEqual({
+      label: 'subscription_page',
+      icon: undefined,
+      to: 'subscription',
+      type: MENU_LINK_TYPE.INTERNAL,
+      active: false,
+    })
+
+    expect(router.resolve).toHaveBeenCalledWith({ name: 'subscription_page' })
+  })
+
   test('has only rights for menu my_bills', () => {
     ability.can = vi.fn(
       (action: string, subject: string) =>

+ 139 - 85
tests/units/services/layout/menuBuilder/parametersMenuBuilder.test.ts

@@ -1,104 +1,158 @@
-// TODO: Supprimer l'extension off quand la composition du menu paramètres sera définitive
-import { describe, test, it, expect, beforeEach, vi } from 'vitest'
-import type {RuntimeConfig} from "@nuxt/schema";
-import type {AnyAbility} from "@casl/ability/dist/types";
-import type {AccessProfile, organizationState} from "~/types/interfaces";
-import ParametersMenuBuilder from "~/services/layout/menuBuilder/parametersMenuBuilder";
-import type {MenuGroup} from "~/types/layout";
-import {MENU_LINK_TYPE} from "~/types/enum/layout";
+import { describe, test, expect, beforeEach, vi } from 'vitest'
+import type { RuntimeConfig } from '@nuxt/schema'
+import type { AnyAbility } from '@casl/ability/dist/types'
+import type { Router } from 'vue-router'
+import type { AccessProfile, organizationState } from '~/types/interfaces'
+import ParametersMenuBuilder from '~/services/layout/menuBuilder/parametersMenuBuilder'
+import type { MenuGroup } from '~/types/layout'
+import { MENU_LINK_TYPE } from '~/types/enum/layout'
 
 let runtimeConfig: RuntimeConfig
 let ability: AnyAbility
 let organizationProfile: organizationState
 let accessProfile: AccessProfile
 let menuBuilder: ParametersMenuBuilder
-
-beforeEach(()=> {
-    runtimeConfig = vi.fn() as any as RuntimeConfig
-    ability = vi.fn() as any as AnyAbility
-    organizationProfile = vi.fn() as any as organizationState
-    accessProfile = vi.fn() as any as AccessProfile
-
-    runtimeConfig.baseUrlAdminLegacy = 'https://mydomain.com/'
-
-    menuBuilder = new ParametersMenuBuilder(runtimeConfig, ability, organizationProfile, accessProfile)
+let router: Router
+
+beforeEach(() => {
+  runtimeConfig = vi.fn() as any as RuntimeConfig
+  ability = vi.fn() as any as AnyAbility
+  organizationProfile = vi.fn() as any as organizationState
+  accessProfile = vi.fn() as any as AccessProfile
+  // @ts-ignore
+  router = vi.fn() as Router
+
+  runtimeConfig.baseUrlAdminLegacy = 'https://mydomain.com/'
+
+  menuBuilder = new ParametersMenuBuilder(
+    runtimeConfig,
+    ability,
+    organizationProfile,
+    accessProfile,
+    router,
+  )
 })
 
 describe('getMenuName', () => {
-    test('validate name', () => {
-        expect(menuBuilder.getMenuName()).toEqual("Parameters")
-    })
+  test('validate name', () => {
+    expect(menuBuilder.getMenuName()).toEqual('Parameters')
+  })
 })
 
 describe('build', () => {
-    test('has all items', () => {
-        ability.can = vi.fn(() => true)
-
-        // Should return a MenuGroup
-        const result = menuBuilder.build() as MenuGroup
-
-        expect(result.label).toEqual('parameters')
-        expect(result.icon).toEqual(undefined)
-        // @ts-ignore
-        expect(result.children.length).toEqual(6)
+  test('has all items', () => {
+    ability.can = vi.fn(() => true)
+    // @ts-ignore
+    organizationProfile.isSchool = vi.fn(() => true)
+    organizationProfile.hasModule = vi.fn((name) => name === 'Sms')
+
+    // Should return a MenuGroup
+    const result = menuBuilder.build() as MenuGroup
+
+    expect(result.label).toEqual('parameters')
+    expect(result.icon).toEqual(undefined)
+    // @ts-ignore
+    expect(result.children.length).toEqual(11)
+  })
+
+  test('has no items', () => {
+    ability.can = vi.fn(() => false)
+    expect(menuBuilder.build()).toEqual(null)
+  })
+
+  test('has only rights for menu general_params', () => {
+    ability.can = vi.fn(
+      (action: string, subject: string) =>
+        action === 'display' && subject === 'parameters_page',
+    )
+
+    // @ts-ignore
+    expect(menuBuilder.build().children[0]).toEqual({
+      label: 'general_params',
+      icon: { name: 'fas fa-cogs' },
+      to: 'https://mydomain.com/#/parameters',
+      type: MENU_LINK_TYPE.V1,
+      active: false,
     })
-
-    test('has no items', () => {
-        ability.can = vi.fn(() => false)
-        expect(menuBuilder.build()).toEqual(null)
+  })
+
+  test('has only rights for menu communication_params', () => {
+    ability.can = vi.fn(
+      (action: string, subject: string) =>
+        action === 'display' && subject === 'parameters_communication_page',
+    )
+
+    // @ts-ignore
+    expect(menuBuilder.build().children[0]).toEqual({
+      label: 'communication_params',
+      icon: { name: 'fas fa-comments' },
+      to: 'https://mydomain.com/#/parameters/communication',
+      type: MENU_LINK_TYPE.V1,
+      active: false,
     })
-
-    test('has only rights for menu general_params', () => {
-        ability.can = vi.fn((action: string, subject: string) => action === 'display' && subject === 'parameters_page')
-
-        // @ts-ignore
-        expect(menuBuilder.build().children[0]).toEqual(
-            {label: 'general_params', icon: {name: 'fas fa-cogs'}, to: 'https://mydomain.com/#/parameters', type: MENU_LINK_TYPE.V1, active: false}
-        )
+  })
+
+  test('has only rights for menu students_params', () => {
+    ability.can = vi.fn(
+      (action: string, subject: string) =>
+        action === 'display' && subject === 'parameters_student_page',
+    )
+
+    // @ts-ignore
+    expect(menuBuilder.build().children[0]).toEqual({
+      label: 'students_params',
+      icon: { name: 'fas fa-users' },
+      to: 'https://mydomain.com/#/parameters/student',
+      type: MENU_LINK_TYPE.V1,
+      active: false,
     })
-
-    test('has only rights for menu communication_params', () => {
-        ability.can = vi.fn((action: string, subject: string) => action === 'display' && subject === 'parameters_communication_page')
-
-        // @ts-ignore
-        expect(menuBuilder.build().children[0]).toEqual(
-            {label: 'communication_params', icon: {name: 'fas fa-comments'}, to: 'https://mydomain.com/#/parameters/communication', type: MENU_LINK_TYPE.V1, active: false}
-        )
+  })
+
+  test('has only rights for menu education_params', () => {
+    ability.can = vi.fn(
+      (action: string, subject: string) =>
+        action === 'display' && subject === 'parameters_education_page',
+    )
+
+    // @ts-ignore
+    expect(menuBuilder.build().children[0]).toEqual({
+      label: 'education_params',
+      icon: { name: 'fas fa-graduation-cap' },
+      to: 'https://mydomain.com/#/parameters/education',
+      type: MENU_LINK_TYPE.V1,
+      active: false,
     })
-
-    test('has only rights for menu students_params', () => {
-        ability.can = vi.fn((action: string, subject: string) => action === 'display' && subject === 'parameters_student_page')
-
-        // @ts-ignore
-        expect(menuBuilder.build().children[0]).toEqual(
-            {label: 'students_params', icon: {name: 'fas fa-users'}, to: 'https://mydomain.com/#/parameters/student', type: MENU_LINK_TYPE.V1, active: false}
-        )
-    })
-
-    test('has only rights for menu education_params', () => {
-        ability.can = vi.fn((action: string, subject: string) => action === 'display' && subject === 'parameters_education_page')
-
-        // @ts-ignore
-        expect(menuBuilder.build().children[0]).toEqual(
-            {label: 'education_params', icon: {name: 'fas fa-graduation-cap'}, to: 'https://mydomain.com/#/parameters/education', type: MENU_LINK_TYPE.V1, active: false}
-        )
+  })
+
+  test('has only rights for menu bills_params', () => {
+    ability.can = vi.fn(
+      (action: string, subject: string) =>
+        action === 'display' && subject === 'parameters_bills_page',
+    )
+
+    // @ts-ignore
+    expect(menuBuilder.build().children[0]).toEqual({
+      label: 'bills_params',
+      icon: { name: 'fas fa-euro-sign' },
+      to: 'https://mydomain.com/#/parameters/billing',
+      type: MENU_LINK_TYPE.V1,
+      active: false,
     })
-
-    test('has only rights for menu bills_params', () => {
-        ability.can = vi.fn((action: string, subject: string) => action === 'display' && subject === 'parameters_bills_page')
-
-        // @ts-ignore
-        expect(menuBuilder.build().children[0]).toEqual(
-            {label: 'bills_params', icon: {name: 'fas fa-euro-sign'}, to: 'https://mydomain.com/#/parameters/billing', type: MENU_LINK_TYPE.V1, active: false}
-        )
-    })
-
-    test('has only rights for menu secure_params', () => {
-        ability.can = vi.fn((action: string, subject: string) => action === 'display' && subject === 'parameters_secure_page')
-
-        // @ts-ignore
-        expect(menuBuilder.build().children[0]).toEqual(
-            {label: 'secure_params', icon: {name: 'fas fa-lock'}, to: 'https://mydomain.com/#/parameters/secure', type: MENU_LINK_TYPE.V1, active: false}
-        )
+  })
+
+  test('has only rights for menu secure_params', () => {
+    ability.can = vi.fn(
+      (action: string, subject: string) =>
+        action === 'display' && subject === 'parameters_secure_page',
+    )
+
+    // @ts-ignore
+    expect(menuBuilder.build().children[0]).toEqual({
+      label: 'secure_params',
+      icon: { name: 'fas fa-lock' },
+      to: 'https://mydomain.com/#/parameters/secure',
+      type: MENU_LINK_TYPE.V1,
+      active: false,
     })
+  })
 })