Skip to content

Commit

Permalink
feat(certification/exploitations): recherche avec tri
Browse files Browse the repository at this point in the history
  • Loading branch information
thom4parisot committed Mar 28, 2024
1 parent 1f0bc45 commit 599033c
Show file tree
Hide file tree
Showing 8 changed files with 493 additions and 65 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"src/components/Features/SingleItem*.test.js",
"src/components/Features/Table.test.js",
"src/components/OperatorSetup/*.test.js",
"src/pages/certification/exploitations/index.test.js",
"src/pages/exploitations/\\[numeroBio\\]/index.test.js"
],
"rules": {
Expand Down
13 changes: 2 additions & 11 deletions src/cartobio-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ export async function getOperatorNcviFeatures ({ evv, numeroBio }) {
* @param {string} input
* @returns {Promise<AgenceBioNormalizedOperatorWithRecord[]>}
*/
export async function searchOperators (input) {
const { data } = await apiClient.post(`/v2/certification/operators/search`, { input })
export async function searchOperators ({ input, page, sort, order }) {
const { data } = await apiClient.post(`/v2/certification/search`, { input, page, sort, order })

return data
}
Expand All @@ -53,15 +53,6 @@ export async function pacageLookup (pacage) {
return data
}

/**
* @returns {Promise<AgenceBioNormalizedOperatorWithRecord[]>}
*/
export async function fetchLatestOperators () {
const { data } = await apiClient.get(`/v2/certification/operators/latest`, { timeout: 10000 })

return data.operators
}


/**
* @param {string} numeroBio
Expand Down
49 changes: 49 additions & 0 deletions src/components/Certification/TableSort.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<template>
<label v-if="$slots.default" :for="id">
<slot name="default" />
</label>

<button v-if="showControl" type="button" :aria-pressed="isAscending || isDescending" :id="id" class="fr-btn fr-btn--tertiary-no-outline" @click="emit('update:modelValue', { sort: code, order: !isAscending ? 'asc' : 'desc' })">
<svg width="14" height="16" viewBox="0 0 14 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 0L13 6H1L7 0Z" :fill="isAscending ? '#000091' : '#929292'"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 16L1 10L13 10L7 16Z" :fill="isDescending ? '#000091' : '#929292'"/>
</svg>
</button>
</template>

<script setup>
import { computed } from 'vue'
const emit = defineEmits(['update:modelValue'])
const props = defineProps({
code: {
type: String,
required: true
},
modelValue: {
type: Object,
required: true
},
showControl: {
type: Boolean,
default : true
}
})
const isAscending = computed(() => props.modelValue.sort === props.code && props.modelValue.order === 'asc')
const isDescending = computed(() => props.modelValue.sort === props.code && props.modelValue.order === 'desc')
const id = computed(() => `sort-order-${props.code}`)
</script>

<style scoped>
label,
button {
cursor: pointer;
vertical-align: middle;
}
button {
padding: .5rem;
}
</style>
51 changes: 51 additions & 0 deletions src/pages/__fixtures__/search-records.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
[
{
"commune": "Crest",
"codePostal": "26400",
"numeroBio": 1,
"nom": "opérateur 1",
"dateEngagement": null,
"record_id": null,
"audit_date": null,
"certification_date_debut": null,
"certification_date_fin": null,
"certification_state": null
},
{
"commune": "Crest",
"codePostal": "26400",
"numeroBio": 2,
"nom": "opérateur 2",
"dateEngagement": null,
"record_id": null,
"audit_date": null,
"certification_date_debut": null,
"certification_date_fin": null,
"certification_state": null
},

{
"commune": "Die",
"codePostal": "26150",
"numeroBio": 3,
"nom": "opérateur 3",
"dateEngagement": "2020-02-02",
"record_id": "a-b-c-d",
"audit_date": null,
"certification_date_debut": null,
"certification_date_fin": null,
"certification_state": "OPERATOR_DRAFT"
},
{
"commune": "Die",
"codePostal": "26150",
"numeroBio": 4,
"nom": "opérateur 4",
"dateEngagement": "2020-02-02",
"record_id": "e-f-g-h",
"audit_date": "2024-01-01",
"certification_date_debut": "2024-01-01",
"certification_date_fin": "2024-03-31",
"certification_state": "CERTIFIED"
}
]
14 changes: 14 additions & 0 deletions src/pages/__fixtures__/user.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"id": 1,
"prenom": "Test",
"nom": "Utilisateur",
"organismeCertificateur": {
"id": 999,
"nom": "CartobiOC",
"numeroControleEu": "FR-BIO-999"
},
"groups": [
{ "nom": "Auditeur"}
],
"mainGroup": {"nom": "Auditeur"}
}
149 changes: 149 additions & 0 deletions src/pages/certification/exploitations/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
import { flushPromises, mount } from "@vue/test-utils"
import { createTestingPinia } from "@pinia/testing"
import axios, { AxiosError } from 'axios'

import { useUserStore } from '@/stores/index.js'

import records from '../../__fixtures__/search-records.json' assert { type: 'json' }
import userFixture from '../../__fixtures__/user.json' assert { type: 'json' }

import Page from './index.vue'

const pinia = createTestingPinia({ createSpy: vi.fn, stubActions: false })
const user = useUserStore(pinia)

beforeEach(() => user.user = userFixture)
afterEach(() => user.$reset())

describe("certification/exploitations", () => {
it("should display a page with a warning because of no results", async () => {
axios.__createMock.post.mockResolvedValueOnce({
data: {
pagination: {
total: 0,
page: 1,
page_max: 1
},
records: []
}
})

const wrapper = mount(Page)

expect(wrapper.find('tbody').text()).toContain('Chargement des données…')

await flushPromises()
expect(wrapper.find('.fr-alert__title').text()).toContain('Aucune exploitation trouvée')
})

it("should display a page with 2 results and 2 pages", async () => {
axios.__createMock.post.mockResolvedValueOnce({
data: {
pagination: {
total: 4,
page: 1,
page_max: 2
},
records: records.slice(0, 2)
}
})

const wrapper = mount(Page)

await flushPromises()
expect(axios.__createMock.post).toHaveBeenCalled(1)
expect(wrapper.findAll('.operator-record')).toHaveLength(2)
expect(wrapper.find('.results-total').text()).toEqual('2 sur 4 résultats')
expect(wrapper.find('.pagination-page-previous').attributes('disabled')).toEqual('')
expect(wrapper.find('.pagination-page-next').attributes('disabled')).toBeUndefined()

// navigate to next page
axios.__createMock.post.mockResolvedValue({
data: {
pagination: {
total: 4,
page: 2,
page_max: 2
},
records: records.slice(2, 4)
}
})

await wrapper.find('.pagination-page-next').trigger('click')
await wrapper.setProps({ page: '2' }) // workaround vue-router not picking up above change
await flushPromises()

expect(axios.__createMock.post).toHaveBeenCalled(2)
expect(wrapper.find('.pagination-page-previous').attributes('disabled')).toBeUndefined()
expect(wrapper.find('.pagination-page-next').attributes('disabled')).toEqual('')

await wrapper.find('#search-results-page-selector').setValue('1')
wrapper.setProps({ page: '1' }) // workaround vue-router not picking up above change
expect(wrapper.emitted()).toHaveProperty('change')
await flushPromises()
expect(axios.__createMock.post).toHaveBeenCalled(3)
})

it("should search, sort then reset filters", async () => {
axios.__createMock.post.mockResolvedValue({
data: {
pagination: {
total: 4,
page: 1,
page_max: 1
},
records
}
})

const wrapper = mount(Page, {
props: { search: 'ferme', page: '2', sort: 'nom', order: 'asc' }
})

await flushPromises()
expect(wrapper.findAll('.operator-record')).toHaveLength(4)
expect(wrapper.findAll('thead [aria-sort]')).toHaveLength(1)
expect(wrapper.find('.table-header-nom').attributes()).toHaveProperty('aria-sort', 'ascending')
expect(wrapper.find('.table-header-nom button').attributes()).toHaveProperty('aria-pressed', 'true')

// // change search to nothing
// // it should reset the filters
await wrapper.find('#search-input').setValue('')
await wrapper.find('form').trigger('submit')

// todo: rather test against a router that works in tests, outside of components
await wrapper.setProps({ search: '', page: '1' })
await flushPromises()

expect(axios.__createMock.post).toHaveBeenLastCalledWith('/v2/certification/search', { input: '', page: 1, sort: 'audit_date', order: 'desc' })
expect(wrapper.find('.table-header-audit_date').attributes()).toHaveProperty('aria-sort', 'descending')
expect(wrapper.find('.table-header-audit_date button').attributes()).toHaveProperty('aria-pressed', 'true')
})

it('should respond with an error in case server is unreachable', async () => {
const error = new AxiosError('Network Error', 'ERR_NETWORK')
axios.__createMock.post.mockRejectedValueOnce(error)

const wrapper = mount(Page)
await flushPromises()

expect(wrapper.find('tbody').text()).toContain('Une erreur de réseau est survenue')
})

it('should throw an error when uncaught', () => {
const error = new AxiosError('Server is down')
error.response = { status: 500 }
axios.__createMock.post.mockRejectedValueOnce(error)

const wrapper = mount(Page, {
global: {
config: {
errorHandler (error) {
expect(error.message).toBe('Server is down')
}
}
}
})
})
})
Loading

0 comments on commit 599033c

Please sign in to comment.