Skip to content
Snippets Groups Projects
Commit 22d0c625 authored by ml bonhomme's avatar ml bonhomme :bee: Committed by Erwan Rouchet
Browse files

Pinia folder picker

parent 750d86ed
No related branches found
No related tags found
1 merge request!1505Pinia folder picker
import axios, { AxiosRequestConfig } from 'axios'
import { ElementList, PageNumberPagination, UUID, TextOrientation, ElementLight, Element, Polygon } from '@/types'
import { ElementList, PageNumberPagination, UUID, TextOrientation, ElementLight, Element, Polygon, CorpusLight } from '@/types'
import { unique, PageNumberPaginationParameters } from '.'
type ConfidenceOperator = 'eq' | 'lt' | 'lte' | 'gt' | 'gte'
......@@ -38,19 +38,19 @@ interface BaseElementListParameters extends PageNumberPaginationParameters {
worker_version?: UUID
}
interface ElementListParameters extends BaseElementListParameters {
export interface ElementListParameters extends BaseElementListParameters {
corpus: UUID
top_level?: boolean
order: Exclude<ElementOrdering, 'position'>
order?: Exclude<ElementOrdering, 'position'>
}
interface ElementParentListParameters extends BaseElementListParameters {
id: UUID
recursive?: boolean
order: Exclude<ElementOrdering, 'position'>
order?: Exclude<ElementOrdering, 'position'>
}
interface ElementChildrenListParameters extends BaseElementListParameters {
export interface ElementChildrenListParameters extends BaseElementListParameters {
id: UUID
recursive?: boolean
}
......@@ -67,14 +67,17 @@ interface ElementChildrenDestroyParameters extends Pick<ElementChildrenListParam
* Generic method for element list endpoints (ListElements, ListElementParents, ListElementChildren)
* with support for `If-Modified-Since`. May return null if the API returns HTTP 304 Not Modified.
*/
async function elementList<T extends BaseElementListParameters> (url: string, { modifiedSince, ...params }: T): Promise<PageNumberPagination<ElementList> | null> {
const payload: AxiosRequestConfig = { params }
async function elementList<T extends BaseElementListParameters | { with_corpus?: true}> (url: string, payload: T): Promise<PageNumberPagination<ElementList & { corpus: CorpusLight }>>
async function elementList<T extends BaseElementListParameters | { modifiedSince?: undefined }> (url: string, payload: T): Promise<PageNumberPagination<ElementList>>
async function elementList<T extends BaseElementListParameters> (url: string, payload: T): Promise<PageNumberPagination<ElementList> | null> {
const { modifiedSince, ...params } = payload
const config: AxiosRequestConfig = { params }
if (modifiedSince) {
payload.headers = { 'If-Modified-Since': modifiedSince, 'Cache-Control': 'no-cache' }
config.headers = { 'If-Modified-Since': modifiedSince, 'Cache-Control': 'no-cache' }
// Axios treats HTTP 3xx as errors by default because the browser is supposed to handle them
payload.validateStatus = status => (status >= 200 && status < 300) || status === 304
config.validateStatus = status => (status >= 200 && status < 300) || status === 304
}
const resp = await axios.get(url, payload)
const resp = await axios.get(url, config)
if (resp.status === 304) return null
return resp.data
}
......
......@@ -17,7 +17,7 @@
<a
class="line"
:class="{ 'has-text-primary': currentFolderId === folder.id }"
v-on:click="$emit('selected-folder', folder)"
v-on:click="$emit('selectedFolder', folder)"
>
<span class="has-text-grey mr-1">
{{ truncateShort(typeName(folder.type)) }}
......@@ -33,16 +33,19 @@
:current-folder-id="currentFolderId"
:exclude="exclude"
v-if="expanded"
v-on:selected-folder="f => $emit('selected-folder', f)"
v-on:selected-folder="(f: Folder) => $emit('selectedFolder', f)"
/>
</li>
</template>
<script>
import { mapState } from 'vuex'
<script lang="ts">
import { mapState } from 'pinia'
import { PropType, defineComponent } from 'vue'
import { corporaMixin, truncateMixin } from '@/mixins.js'
import FolderList from './FolderList.vue'
export default {
import { Folder, useFolderPickerStore } from '@/stores/folderpicker'
export default defineComponent({
mixins: [
corporaMixin,
truncateMixin
......@@ -50,10 +53,14 @@ export default {
components: {
FolderList
},
emits: ['selected-folder'],
emits: {
selectedFolder (payload: Folder) {
return payload.id !== undefined
}
},
props: {
folder: {
type: Object,
type: Object as PropType<Folder>,
required: true
},
exclude: {
......@@ -71,7 +78,7 @@ export default {
loading: false
}),
computed: {
...mapState('folderpicker', ['folderPagination']),
...mapState(useFolderPickerStore, ['folderPagination']),
corpusId () {
return this.folder.corpus.id
},
......@@ -80,7 +87,7 @@ export default {
return !pagination || pagination.count !== 0
}
}
}
})
</script>
<style scoped>
......
......@@ -6,7 +6,7 @@
:folder="folder"
:exclude="exclude"
:current-folder-id="currentFolderId"
v-on:selected-folder="f => $emit('selected-folder', f)"
v-on:selected-folder="(f: Folder) => $emit('selectedFolder', f)"
/>
<li>
<button
......@@ -21,16 +21,22 @@
</ul>
</template>
<script>
import { defineAsyncComponent } from 'vue'
import { mapState, mapActions, mapGetters } from 'vuex'
<script lang="ts">
import { defineAsyncComponent, defineComponent, Component } from 'vue'
import { mapState, mapActions } from 'pinia'
import { useFolderPickerStore } from '@/stores'
import { Folder } from '@/stores/folderpicker'
export default {
export default defineComponent({
components: {
// Using a regular import here would cause issues with circular imports, since Folder also imports FolderList
Folder: defineAsyncComponent(async () => await import('./Folder.vue'))
Folder: defineAsyncComponent<Component>(async () => await import('./Folder.vue'))
},
emits: {
selectedFolder (payload: Folder) {
return payload.id !== undefined
}
},
emits: ['selected-folder'],
props: {
corpusId: {
type: String,
......@@ -54,19 +60,18 @@ export default {
loading: false
}),
methods: {
...mapActions('folderpicker', ['nextFolders']),
...mapActions(useFolderPickerStore, ['nextFolders']),
async fetchFolders () {
this.loading = true
try {
await this.nextFolders({ corpus: this.corpusId, folder: this.folderId })
await this.nextFolders(this.corpusId, this.folderId)
} finally {
this.loading = false
}
}
},
computed: {
...mapState('folderpicker', { folders: 'folders', corpusFolders: 'corpus' }),
...mapGetters('folderpicker', ['pagination']),
...mapState(useFolderPickerStore, { folders: 'folders', corpusFolders: 'corpus', pagination: 'pagination' }),
subfolders () {
let folderIds
if (this.folderId) {
......@@ -97,5 +102,5 @@ export default {
immediate: true
}
}
}
})
</script>
......@@ -25,12 +25,14 @@
</div>
</template>
<script>
import { mapState } from 'vuex'
<script lang="ts">
import { mapState } from 'pinia'
import Modal from '@/components/Modal.vue'
import FolderList from './FolderList.vue'
import { useFolderPickerStore } from '@/stores'
import { defineComponent } from 'vue'
export default {
export default defineComponent({
inheritAttrs: false,
components: {
Modal,
......@@ -61,7 +63,7 @@ export default {
opened: false
}),
computed: {
...mapState('folderpicker', ['folders'])
...mapState(useFolderPickerStore, ['folders'])
}
}
})
</script>
import { assign } from 'lodash'
import * as api from '@/api'
import { errorParser } from '@/helpers'
import { ELEMENT_LIST_MAX_AUTO_PAGES } from '@/config'
export const initialState = () => ({
/*
* Folder data with a list of subfolder IDs:
* { [folder.id]: { ...folder, subfolders: [id, id, id…] }
* Using IDs in this way allows avoiding strange recursive code in the store.
*/
folders: {},
// List of top-level folder IDs per corpora: { corpusid: [id, id, id…] }
corpus: {},
/*
* Pagination state by corpus ID or parent folder ID
* to allow loading a portion of the list and load more upon user request
*/
corpusPagination: {},
folderPagination: {}
})
export const mutations = {
addFolders (state, { corpus, folder = null, subfolders }) {
if (!corpus && !folder) throw new Error('Either a corpus ID or a folder ID are required')
// Add the folders to the state
state.folders = {
...state.folders,
...Object.fromEntries(subfolders.map(subfolder => ([subfolder.id, { ...subfolder, subfolders: [] }])))
}
// Add the folders as subfolders of their folder or corpus
const ids = subfolders.map(subfolder => subfolder.id)
if (folder) {
state.folders[folder] = {
...(state.folders[folder] || {}),
subfolders: [
...((state.folders[folder] || {}).subfolders || []),
...ids
]
}
} else {
state.corpus[corpus] = [
...new Set([
...(state.corpus[corpus] || []),
...ids
])
]
}
},
setPagination (state, { corpus, folder = null, ...pagination }) {
if (folder) state.folderPagination[folder] = pagination
else state.corpusPagination[corpus] = pagination
},
reset (state) {
assign(state, initialState())
}
}
export const actions = {
async nextFolders ({ commit, getters }, { corpus, folder = null, max = ELEMENT_LIST_MAX_AUTO_PAGES }) {
if (!corpus && !folder) throw new Error('Either a corpus ID or a folder ID are required')
try {
let count = 0
while (count < max) {
const pagination = getters.pagination(corpus, folder) || { number: 0 }
if (pagination.number > 0 && !pagination.next) {
// No next page
break
}
const endpoint = folder ? api.listElementChildren : api.listElements
const payload = {
page: pagination.number + 1,
folder: true
}
if (folder) payload.id = folder
else {
payload.corpus = corpus
payload.top_level = true
}
const data = await endpoint(payload)
commit('addFolders', { corpus, folder, subfolders: data.results })
/*
* Copy pagination parameters e.g. all returned parameters except the response body
* Do not alter data directly has API endpoints may return the same object in case of duplicated requests
*/
const nextPagination = { ...data }
delete nextPagination.results
commit('setPagination', { corpus, folder, ...nextPagination })
count++
}
} catch (err) {
commit('notifications/notify', { type: 'error', text: errorParser(err) }, { root: true })
throw err
}
}
}
export const getters = {
pagination: state => (corpus = null, folder = null) => (folder ? state.folderPagination[folder] : state.corpusPagination[corpus]) || null
}
export default {
namespaced: true,
state: initialState(),
mutations,
actions,
getters
}
import { createStore } from 'vuex'
import { useDisplayStore, useImageStore, useIngestStore } from '@/stores'
import { useDisplayStore, useFolderPickerStore, useImageStore, useIngestStore } from '@/stores'
/**
* Store module names. Those must match file names in the js/store folder (e.g. auth → ./auth.js)
......@@ -13,7 +13,6 @@ const moduleNames = [
'elements',
'entity',
'files',
'folderpicker',
'jobs',
'model',
'navigation',
......@@ -34,7 +33,8 @@ const moduleNames = [
export const piniaStores = [
useDisplayStore,
useImageStore,
useIngestStore
useIngestStore,
useFolderPickerStore
]
export const actions = {
......
import { defineStore } from 'pinia'
import { listElementChildren, listElements } from '@/api'
import { ELEMENT_LIST_MAX_AUTO_PAGES } from '@/config'
import { errorParser } from '@/helpers'
import { CorpusLight, ElementList, PageNumberPagination, UUID } from '@/types'
import { useNotificationStore } from './notification'
export interface Folder extends ElementList {
corpus: CorpusLight
subfolders: UUID[]
}
type Pagination = Omit<PageNumberPagination<ElementList>, 'results'>
interface State {
/**
* Folder data with a list of subfolder IDs.
* Using IDs in this way allows avoiding strange recursive code in the store.
*/
folders: { [id: UUID]: Folder }
/**
* List of top-level folder IDs per corpora.
*/
corpus: { [corpusId: UUID]: UUID[] }
/**
* Pagination state by corpus ID to allow loading a portion of the list and load more upon user request
*/
corpusPagination: { [corpusId: UUID]: Pagination }
/**
* Pagination state by folder ID to allow loading a portion of the list and load more upon user request
*/
folderPagination: { [id: UUID]: Pagination }
}
export const useFolderPickerStore = defineStore('folderpicker', {
state: (): State => ({
folders: {},
corpus: {},
corpusPagination: {},
folderPagination: {}
}),
actions: {
async nextFolders (corpus: UUID, folder: UUID | null = null, max = ELEMENT_LIST_MAX_AUTO_PAGES) {
if (!corpus && !folder) throw new Error('Either a corpus ID or a folder ID are required')
try {
let count = 0
while (count < max) {
const pagination = this.pagination(corpus, folder) || { previous: null, next: null, count: 0, number: 0 }
if (pagination.number > 0 && !pagination.next) {
// No next page
break
}
let data: PageNumberPagination<ElementList & { corpus: CorpusLight }>
if (folder) {
data = await listElementChildren({
id: folder,
page: pagination.number + 1,
folder: true
})
} else {
data = await listElements({
corpus,
top_level: true,
page: pagination.number + 1,
folder: true
})
}
const { results, ...nextPagination } = data
// Add the folders to the state
this.folders = {
...this.folders,
...Object.fromEntries(results.map(subfolder => ([subfolder.id, { ...subfolder, subfolders: [] }])))
}
// Add the folders as subfolders of their folder or corpus
const ids = results.map(subfolder => subfolder.id)
if (folder) {
this.folders[folder] = {
...(this.folders[folder] || {}),
subfolders: [
...((this.folders[folder] || {}).subfolders || []),
...ids
]
}
} else {
this.corpus[corpus] = [
...new Set([
...(this.corpus[corpus] || []),
...ids
])
]
}
/*
* Copy pagination parameters e.g. all returned parameters except the response body
* Do not alter data directly has API endpoints may return the same object in case of duplicated requests
*/
if (folder) this.folderPagination[folder] = nextPagination
else this.corpusPagination[corpus] = nextPagination
count++
}
} catch (err) {
const notificationStore = useNotificationStore()
notificationStore.notify({ type: 'error', text: errorParser(err) })
throw err
}
}
},
getters: {
/**
* Returns pagination parameters of either a folder or a corpus
*/
pagination () {
return (corpus: UUID | null = null, folder: UUID | null = null): Pagination | null => {
if (folder !== null) {
return this.folderPagination[folder] || null
}
if (corpus !== null) {
return this.corpusPagination[corpus] || null
}
return null
}
}
}
})
......@@ -2,3 +2,4 @@ export { useDisplayStore } from './display'
export { useImageStore } from './image'
export { useIngestStore } from './ingest'
export { useNotificationStore } from './notification'
export { useFolderPickerStore } from './folderpicker'
......@@ -114,7 +114,7 @@ export interface ElementSlim extends ElementTiny {
* URL of the element's thumbnail, on which a PUT request can be made to upload a new thumbnail.
* The URL will only be available if the element's type is a folder type and the user is admin or internal.
*/
thumbnail_put_url: string | null
thumbnail_put_url?: string | null
}
export interface Element extends ElementSlim {
......@@ -132,8 +132,8 @@ export type ElementList = Required<Pick<ElementBase, 'id' | 'type' | 'name' | 'c
export interface PageNumberPagination<T> {
count: number
previous: string
next: string
previous: string | null
next: string | null
number: number
results: T[]
}
......
......@@ -118,7 +118,6 @@ describe('auth', () => {
{ mutation: 'elements/reset' },
{ mutation: 'entity/reset' },
{ mutation: 'files/reset' },
{ mutation: 'folderpicker/reset' },
{ mutation: 'jobs/reset' },
{ mutation: 'model/reset' },
{ mutation: 'navigation/reset' },
......@@ -157,7 +156,6 @@ describe('auth', () => {
{ mutation: 'elements/reset' },
{ mutation: 'entity/reset' },
{ mutation: 'files/reset' },
{ mutation: 'folderpicker/reset' },
{ mutation: 'jobs/reset' },
{ mutation: 'model/reset' },
{ mutation: 'navigation/reset' },
......@@ -194,7 +192,6 @@ describe('auth', () => {
{ mutation: 'elements/reset' },
{ mutation: 'entity/reset' },
{ mutation: 'files/reset' },
{ mutation: 'folderpicker/reset' },
{ mutation: 'jobs/reset' },
{ mutation: 'model/reset' },
{ mutation: 'navigation/reset' },
......@@ -233,7 +230,6 @@ describe('auth', () => {
{ mutation: 'elements/reset' },
{ mutation: 'entity/reset' },
{ mutation: 'files/reset' },
{ mutation: 'folderpicker/reset' },
{ mutation: 'jobs/reset' },
{ mutation: 'model/reset' },
{ mutation: 'navigation/reset' },
......@@ -268,7 +264,6 @@ describe('auth', () => {
{ mutation: 'elements/reset' },
{ mutation: 'entity/reset' },
{ mutation: 'files/reset' },
{ mutation: 'folderpicker/reset' },
{ mutation: 'jobs/reset' },
{ mutation: 'model/reset' },
{ mutation: 'navigation/reset' },
......
......@@ -44,7 +44,6 @@ describe('store', () => {
{ mutation: 'elements/reset' },
{ mutation: 'entity/reset' },
{ mutation: 'files/reset' },
{ mutation: 'folderpicker/reset' },
{ mutation: 'jobs/reset' },
{ mutation: 'model/reset' },
{ mutation: 'navigation/reset' },
......@@ -68,7 +67,7 @@ describe('store', () => {
require('./elements.spec.js')
require('./entity.spec.js')
require('./files.spec.js')
require('./folderpicker.spec.js')
require('../stores/folderpicker.spec.js')
require('./jobs.spec.js')
require('./model.spec.js')
require('./navigation.spec.js')
......
import { assert } from 'chai'
import { createPinia, setActivePinia } from 'pinia'
import axios from 'axios'
import { initialState, mutations, getters } from '@/store/folderpicker.js'
import store from './index.spec.js'
import { useFolderPickerStore } from '@/stores/folderpicker.ts'
import { assertRejects, FakeAxios } from '../testhelpers.js'
describe('folderpicker', () => {
describe('mutations', () => {
describe('addFolders', () => {
it('requires a corpus or a folder', () => {
const state = initialState()
assert.throws(
() => { mutations.addFolders(state, { subfolders: [{ id: 'sub1' }] }) },
Error,
'Either a corpus ID or a folder ID are required'
)
})
it('sets subfolders on a corpus', () => {
const state = initialState()
mutations.addFolders(state, {
corpus: 'corpusid',
subfolders: [{ id: 'sub1', name: 'Subfolder 1' }]
})
assert.deepStrictEqual(state.corpus, {
corpusid: ['sub1']
})
assert.deepStrictEqual(state.folders, {
sub1: {
id: 'sub1',
name: 'Subfolder 1',
subfolders: []
}
})
})
it('sets subfolders on a folder', () => {
const state = initialState()
mutations.addFolders(state, {
corpus: 'corpusid',
folder: 'folderid',
subfolders: [{ id: 'sub1', name: 'Subfolder 1' }]
})
assert.deepStrictEqual(state.corpus, {})
assert.deepStrictEqual(state.folders, {
folderid: { subfolders: ['sub1'] },
sub1: {
id: 'sub1',
name: 'Subfolder 1',
subfolders: []
}
})
})
it('adds subfolders to a corpus', () => {
const state = initialState()
state.corpus.corpusid = ['sub0']
mutations.addFolders(state, {
corpus: 'corpusid',
subfolders: [{ id: 'sub1', name: 'Subfolder 1' }]
})
assert.deepStrictEqual(state.corpus, {
corpusid: ['sub0', 'sub1']
})
assert.deepStrictEqual(state.folders, {
sub1: {
id: 'sub1',
name: 'Subfolder 1',
subfolders: []
}
})
})
it('adds subfolders to a folder', () => {
const state = initialState()
state.folders.folderid = {
id: 'folderid',
name: 'Parent folder',
subfolders: ['sub0']
}
let store
mutations.addFolders(state, {
corpus: 'corpusid',
folder: 'folderid',
subfolders: [{ id: 'sub1', name: 'Subfolder 1' }]
})
assert.deepStrictEqual(state.corpus, {})
assert.deepStrictEqual(state.folders, {
folderid: {
id: 'folderid',
name: 'Parent folder',
subfolders: ['sub0', 'sub1']
},
sub1: {
id: 'sub1',
name: 'Subfolder 1',
subfolders: []
}
})
})
})
describe('setPagination', () => {
it('sets pagination for a corpus', () => {
const state = initialState()
mutations.setPagination(state, {
corpus: 'corpusid',
number: 4,
next: 'url'
})
assert.deepStrictEqual(state.corpusPagination, {
corpusid: {
number: 4,
next: 'url'
}
})
assert.deepStrictEqual(state.folderPagination, {})
})
it('sets pagination for a folder', () => {
const state = initialState()
mutations.setPagination(state, {
corpus: 'corpusid',
folder: 'folderid',
number: 4,
next: 'url'
})
assert.deepStrictEqual(state.corpusPagination, {})
assert.deepStrictEqual(state.folderPagination, {
folderid: {
number: 4,
next: 'url'
}
})
})
})
before(() => {
setActivePinia(createPinia())
store = useFolderPickerStore()
})
it('reset', () => {
const state = {
corpus: { a: ['b', 'c'] },
folders: { a: { id: 'a' } },
folderPagination: { a: { number: 4 } },
corpusPagination: { a: { number: 4 } }
}
mutations.reset(state)
assert.deepStrictEqual(state, initialState())
})
beforeEach(() => {
store.$reset()
})
describe('actions', () => {
......@@ -168,7 +26,6 @@ describe('folderpicker', () => {
afterEach(() => {
// Remove any handlers, but leave mocking in place
mock.reset()
store.reset()
})
after('Removing Axios mock', () => {
......@@ -178,7 +35,7 @@ describe('folderpicker', () => {
describe('nextFolders', () => {
it('requires a corpus or a folder', async () => {
const err = await assertRejects(() => store.dispatch('folderpicker/nextFolders', {}))
const err = await assertRejects(() => store.nextFolders())
assert.strictEqual(err.message, 'Either a corpus ID or a folder ID are required')
})
......@@ -187,7 +44,7 @@ describe('folderpicker', () => {
const folder2 = { id: 'folder2', name: 'Folder 2' }
const folder3 = { id: 'folder3', name: 'Folder 3' }
store.setState('folderpicker.folders', { folder1 })
store.folders = { folder1 }
const pagination1 = {
count: 3,
......@@ -211,52 +68,60 @@ describe('folderpicker', () => {
results: [folder3]
})
await store.dispatch('folderpicker/nextFolders', { corpus: 'corpusid', folder: 'folder1', max: 2 })
await store.nextFolders('corpusid', 'folder1', 2)
assert.deepStrictEqual(store.history, [
{
action: 'folderpicker/nextFolders',
payload: {
corpus: 'corpusid',
folder: 'folder1',
max: 2
}
},
{
mutation: 'folderpicker/addFolders',
payload: {
corpus: 'corpusid',
folder: 'folder1',
subfolders: [folder2]
}
},
{
mutation: 'folderpicker/setPagination',
payload: {
corpus: 'corpusid',
folder: 'folder1',
...pagination1
}
assert.deepStrictEqual(store.folders, {
folder1: {
...folder1,
subfolders: ['folder2', 'folder3']
},
{
mutation: 'folderpicker/addFolders',
payload: {
corpus: 'corpusid',
folder: 'folder1',
subfolders: [folder3]
}
folder2: {
...folder2,
subfolders: []
},
{
mutation: 'folderpicker/setPagination',
payload: {
corpus: 'corpusid',
folder: 'folder1',
...pagination2
}
folder3: {
...folder3,
subfolders: []
}
])
})
assert.deepStrictEqual(store.folderPagination, {
folder1: pagination2
})
})
it('accepts a folder with no corpus', async () => {
const folder1 = { id: 'folder1', name: 'Folder 1' }
const folder2 = { id: 'folder2', name: 'Folder 2' }
const folder3 = { id: 'folder3', name: 'Folder 3' }
store.folders = { folder1 }
const pagination1 = {
count: 3,
previous: null,
next: '/elements/folder1/children/?page=2',
number: 1
}
const pagination2 = {
count: 3,
previous: '/elements/folder1/children/?page=1',
next: '/elements/folder1/children/?page=3',
number: 2
}
mock.onGet('/elements/folder1/children/', { params: { folder: true, page: 1 } }).reply(200, {
...pagination1,
results: [folder2]
})
mock.onGet('/elements/folder1/children/', { params: { folder: true, page: 2 } }).reply(200, {
...pagination2,
results: [folder3]
})
await store.nextFolders(null, 'folder1', 2)
assert.deepStrictEqual(store.state.folderpicker.folders, {
assert.deepStrictEqual(store.folders, {
folder1: {
...folder1,
subfolders: ['folder2', 'folder3']
......@@ -271,7 +136,7 @@ describe('folderpicker', () => {
}
})
assert.deepStrictEqual(store.state.folderpicker.folderPagination, {
assert.deepStrictEqual(store.folderPagination, {
folder1: pagination2
})
})
......@@ -314,51 +179,9 @@ describe('folderpicker', () => {
results: [folder2]
})
await store.dispatch('folderpicker/nextFolders', { corpus: 'corpusid', max: 2 })
await store.nextFolders('corpusid', null, 2)
assert.deepStrictEqual(store.history, [
{
action: 'folderpicker/nextFolders',
payload: {
corpus: 'corpusid',
max: 2
}
},
{
mutation: 'folderpicker/addFolders',
payload: {
corpus: 'corpusid',
folder: null,
subfolders: [folder1]
}
},
{
mutation: 'folderpicker/setPagination',
payload: {
corpus: 'corpusid',
folder: null,
...pagination1
}
},
{
mutation: 'folderpicker/addFolders',
payload: {
corpus: 'corpusid',
folder: null,
subfolders: [folder2]
}
},
{
mutation: 'folderpicker/setPagination',
payload: {
corpus: 'corpusid',
folder: null,
...pagination2
}
}
])
assert.deepStrictEqual(store.state.folderpicker.folders, {
assert.deepStrictEqual(store.folders, {
folder1: {
...folder1,
subfolders: []
......@@ -368,42 +191,38 @@ describe('folderpicker', () => {
subfolders: []
}
})
assert.deepStrictEqual(store.state.folderpicker.corpus, {
assert.deepStrictEqual(store.corpus, {
corpusid: ['folder1', 'folder2']
})
assert.deepStrictEqual(store.state.folderpicker.corpusPagination, {
assert.deepStrictEqual(store.corpusPagination, {
corpusid: pagination2
})
assert.deepStrictEqual(store.state.folderpicker.folderPagination, {})
assert.deepStrictEqual(store.folderPagination, {})
})
it('does nothing when there are no further pages', async () => {
store.setState('folderpicker.folderPagination', {
const initialFolderPagination = {
folder1: {
count: 0,
previous: null,
next: null,
number: 1
}
})
}
store.folderPagination = initialFolderPagination
await store.dispatch('folderpicker/nextFolders', { folder: 'folder1', max: Infinity })
await store.nextFolders(null, 'folder1', Infinity)
assert.strictEqual(mock.history.get.length, 0)
assert.deepStrictEqual(store.history, [
{
action: 'folderpicker/nextFolders',
payload: { folder: 'folder1', max: Infinity }
}
])
assert.deepStrictEqual(store.folderPagination, initialFolderPagination)
})
it('handles errors', async () => {
const folder1 = { id: 'folder1', name: 'Folder 1' }
const folder2 = { id: 'folder2', name: 'Folder 2' }
store.setState('folderpicker.folders', { folder1 })
store.folders = { folder1 }
const pagination1 = {
count: 3,
......@@ -418,43 +237,9 @@ describe('folderpicker', () => {
})
mock.onGet('/elements/folder1/children/', { params: { folder: true, page: 2 } }).reply(500)
await assertRejects(() => store.dispatch('folderpicker/nextFolders', { corpus: 'corpusid', folder: 'folder1', max: 2 }))
assert.deepStrictEqual(store.history, [
{
action: 'folderpicker/nextFolders',
payload: {
corpus: 'corpusid',
folder: 'folder1',
max: 2
}
},
{
mutation: 'folderpicker/addFolders',
payload: {
corpus: 'corpusid',
folder: 'folder1',
subfolders: [folder2]
}
},
{
mutation: 'folderpicker/setPagination',
payload: {
corpus: 'corpusid',
folder: 'folder1',
...pagination1
}
},
{
mutation: 'notifications/notify',
payload: {
type: 'error',
text: 'Request failed with status code 500'
}
}
])
await assertRejects(() => store.nextFolders('corpusid', 'folder1', 2))
assert.deepStrictEqual(store.state.folderpicker.folders, {
assert.deepStrictEqual(store.folders, {
folder1: {
...folder1,
subfolders: ['folder2']
......@@ -465,7 +250,7 @@ describe('folderpicker', () => {
}
})
assert.deepStrictEqual(store.state.folderpicker.folderPagination, {
assert.deepStrictEqual(store.folderPagination, {
folder1: pagination1
})
})
......@@ -475,33 +260,31 @@ describe('folderpicker', () => {
describe('getters', () => {
describe('pagination', () => {
it('retrieves pagination for a corpus', () => {
const state = initialState()
state.corpusPagination.corpusid = {
store.corpusPagination.corpusid = {
number: 4,
next: 'url'
}
assert.deepStrictEqual(getters.pagination(state)('corpusid'), {
assert.deepStrictEqual(store.pagination('corpusid', null), {
number: 4,
next: 'url'
})
assert.strictEqual(getters.pagination(state)('corpus2'), null)
assert.strictEqual(store.pagination('corpus2', null), null)
})
it('retrieves pagination for a folder', () => {
const state = initialState()
state.folderPagination.folderid = {
store.folderPagination.folderid = {
number: 4,
next: 'url'
}
assert.deepStrictEqual(getters.pagination(state)(null, 'folderid'), {
assert.deepStrictEqual(store.pagination(null, 'folderid'), {
number: 4,
next: 'url'
})
assert.deepStrictEqual(getters.pagination(state)('corpusid', 'folderid'), {
assert.deepStrictEqual(store.pagination('corpusid', 'folderid'), {
number: 4,
next: 'url'
})
assert.strictEqual(getters.pagination(state)('corpusid', 'folder2'), null)
assert.strictEqual(store.pagination('corpusid', 'folder2'), null)
})
})
})
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment