+
{
const isItemBeingRenamed = (item: TreeItem) => {
if (context.actionInProgress.value?.id !== StudioItemActionId.RenameItem) return false
- return context.actionInProgress.value?.item?.id === item.id
+ return context.actionInProgress.value?.item?.fsPath === item.fsPath
}
const formComponent = computed(() => {
@@ -64,7 +64,7 @@ const formComponent = computed(() => {
{
const { fsPath } = params
const gitkeepFsPath = joinURL(fsPath, '.gitkeep')
const gitKeepMedia: MediaItem = {
- id: generateIdFromFsPath(gitkeepFsPath),
+ id: generateIdFromFsPath(gitkeepFsPath, TreeRootId.Media),
fsPath: gitkeepFsPath,
stem: generateStemFromFsPath(gitkeepFsPath),
extension: '',
@@ -117,19 +117,19 @@ export const useContext = createSharedComposable((
unsetActionInProgress()
- await activeTree.value.selectParentById(gitKeepMedia.id)
+ await activeTree.value.selectParentByFsPath(gitKeepMedia.id)
},
[StudioItemActionId.CreateDocument]: async (params: CreateFileParams) => {
const { fsPath, content } = params
const document = await host.document.create(fsPath, content)
const draftItem = await activeTree.value.draft.create(document as DatabaseItem)
- await activeTree.value.selectItemById(draftItem.id)
+ await activeTree.value.selectItemByFsPath(draftItem.id)
},
[StudioItemActionId.UploadMedia]: async ({ parentFsPath, files }: UploadMediaParams) => {
// Remove .gitkeep draft in folder if exists
const gitkeepFsPath = parentFsPath === '/' ? '.gitkeep' : joinURL(parentFsPath, '.gitkeep')
- const gitkeepId = generateIdFromFsPath(gitkeepFsPath)
- const gitkeepDraft = await activeTree.value.draft.get(generateIdFromFsPath(gitkeepFsPath))
+ const gitkeepId = generateIdFromFsPath(gitkeepFsPath, TreeRootId.Media)
+ const gitkeepDraft = await activeTree.value.draft.get(gitkeepId)
if (gitkeepDraft) {
await activeTree.value.draft.remove([gitkeepId], { rerender: false })
}
@@ -139,28 +139,60 @@ export const useContext = createSharedComposable((
}
},
[StudioItemActionId.RevertItem]: async (item: TreeItem) => {
- await activeTree.value.draft.revert(item.id)
+ // Get collections from document item or use default media collection
+ for (const collection of item.collections) {
+ const id = generateIdFromFsPath(item.fsPath, collection)
+ await activeTree.value.draft.revert(id)
+ }
},
[StudioItemActionId.RenameItem]: async (params: TreeItem | RenameFileParams) => {
- const { id, newFsPath } = params as RenameFileParams
+ const { item, newFsPath } = params as RenameFileParams
+
+ // Revert file
+ if (item.type === 'file') {
+ const id = generateIdFromFsPath(item.fsPath, item.collections[0])
+ await activeTree.value.draft.rename([{ id, newFsPath }])
+ return
+ }
- const descendants = findDescendantsFileItemsFromId(activeTree.value.root.value, id)
+ // Revert folder
+ const descendants = findDescendantsFileItemsFromFsPath(activeTree.value.root.value, item.fsPath)
if (descendants.length > 0) {
- const parent = findItemFromId(activeTree.value.root.value, id)!
- const itemsToRename = descendants.map(item => ({ id: item.id, newFsPath: item.fsPath.replace(parent.fsPath, newFsPath) }))
+ const itemsToRename = descendants.map((descendant) => {
+ return {
+ id: generateIdFromFsPath(descendant.fsPath, descendant.collections[0]),
+ newFsPath: descendant.fsPath.replace(item.fsPath, newFsPath),
+ }
+ })
+
await activeTree.value.draft.rename(itemsToRename)
}
- else {
- await activeTree.value.draft.rename([{ id, newFsPath }])
- }
},
[StudioItemActionId.DeleteItem]: async (item: TreeItem) => {
- const ids: string[] = findDescendantsFileItemsFromId(activeTree.value.root.value, item.id).map(item => item.id)
- await activeTree.value.draft.remove(ids)
+ // Delete file
+ if (item.type === 'file') {
+ const id = generateIdFromFsPath(item.fsPath, item.collections![0])
+ await activeTree.value.draft.remove([id])
+ return
+ }
+
+ // Delete folder
+ const descendants = findDescendantsFileItemsFromFsPath(activeTree.value.root.value, item.fsPath)
+ if (descendants.length > 0) {
+ const ids: string[] = descendants.map((descendant) => {
+ return generateIdFromFsPath(descendant.fsPath, descendant.collections![0])
+ })
+ await activeTree.value.draft.remove(ids)
+ }
},
[StudioItemActionId.DuplicateItem]: async (item: TreeItem) => {
- const draftItem = await activeTree.value.draft.duplicate(item.id)
- await activeTree.value.selectItemById(draftItem!.id)
+ // Duplicate file
+ if (item.type === 'file') {
+ const id = generateIdFromFsPath(item.fsPath, item.collections![0])
+ const draftItem = await activeTree.value.draft.duplicate(id)
+ await activeTree.value.selectItemByFsPath(draftItem!.id)
+ return
+ }
},
[StudioItemActionId.RevertAllItems]: async () => {
await documentTree.draft.revertAll()
diff --git a/src/app/src/composables/useStudio.ts b/src/app/src/composables/useStudio.ts
index 8d2c7b1a..6e4b47a7 100644
--- a/src/app/src/composables/useStudio.ts
+++ b/src/app/src/composables/useStudio.ts
@@ -96,8 +96,9 @@ function initDevelopmentMode(host: StudioHost, draftDocuments: ReturnType {
const manifestId = ref('')
const preferences = useStorage('studio-preferences', { syncEditorAndRoute: true, showTechnicalMode: false })
- const location = useStorage('studio-active', { active: false, feature: StudioFeature.Content, itemId: TreeRootId.Content })
+ const location = useStorage('studio-active', { active: false, feature: StudioFeature.Content, fsPath: '/' })
- function setLocation(feature: StudioFeature, itemId: string) {
- location.value = { active: true, feature, itemId }
+ function setLocation(feature: StudioFeature, fsPath: string) {
+ location.value = { active: true, feature, fsPath }
}
function unsetActiveLocation() {
diff --git a/src/app/src/composables/useTree.ts b/src/app/src/composables/useTree.ts
index 549356b7..fdf493a9 100644
--- a/src/app/src/composables/useTree.ts
+++ b/src/app/src/composables/useTree.ts
@@ -1,11 +1,12 @@
-import { StudioFeature, TreeStatus, type StudioHost, type TreeItem, DraftStatus, TreeRootId } from '../types'
+import { StudioFeature, TreeStatus, type StudioHost, type TreeItem, DraftStatus } from '../types'
import { ref, computed } from 'vue'
import type { useDraftDocuments } from './useDraftDocuments'
import type { useDraftMedias } from './useDraftMedias'
-import { buildTree, findItemFromId, findItemFromRoute, findParentFromId } from '../utils/tree'
+import { buildTree, findItemFromFsPath, findItemFromRoute, findParentFromFsPath, generateIdFromFsPath } from '../utils/tree'
import type { RouteLocationNormalized } from 'vue-router'
import { useHooks } from './useHooks'
import { useStudioState } from './useStudioState'
+import { TreeRootId } from '../types/tree'
export const useTree = (type: StudioFeature, host: StudioHost, draft: ReturnType) => {
const hooks = useHooks()
@@ -16,27 +17,28 @@ export const useTree = (type: StudioFeature, host: StudioHost, draft: ReturnType
const rootItem = computed(() => {
const draftedTreeItems = draft.list.value.filter(draft => draft.status !== DraftStatus.Pristine)
return {
- id: type === StudioFeature.Content ? TreeRootId.Content : TreeRootId.Media,
name: type === StudioFeature.Content ? 'content' : 'public',
type: 'root',
fsPath: '/',
children: tree.value,
status: draftedTreeItems.length > 0 ? TreeStatus.Updated : null,
+ collections: [type === StudioFeature.Content ? TreeRootId.Content : TreeRootId.Media],
+ prefix: null,
} as TreeItem
})
const currentItem = ref(rootItem.value)
const currentTree = computed(() => {
- if (currentItem.value.id === rootItem.value.id) {
+ if (currentItem.value.type === 'root') {
return tree.value
}
let subTree = tree.value
- const idSegments = currentItem.value.id.split('/').filter(Boolean)
- for (let i = 0; i < idSegments.length; i++) {
- const id = idSegments.slice(0, i + 1).join('/')
- const file = subTree.find(item => item.id === id) as TreeItem
+ const fsPathSegments = currentItem.value.fsPath.split('/').filter(Boolean)
+ for (let i = 0; i < fsPathSegments.length; i++) {
+ const fsPath = fsPathSegments.slice(0, i + 1).join('/')
+ const file = subTree.find(item => item.fsPath === fsPath) as TreeItem
if (file) {
subTree = file.children!
}
@@ -48,10 +50,10 @@ export const useTree = (type: StudioFeature, host: StudioHost, draft: ReturnType
async function select(item: TreeItem) {
currentItem.value = item || rootItem.value
- setLocation(type, currentItem.value.id)
+ setLocation(type, currentItem.value.fsPath)
if (item?.type === 'file') {
- await draft.selectById(item.id)
+ await draft.selectById(generateIdFromFsPath(item.fsPath, item.collections![0]))
if (
!preferences.value.syncEditorAndRoute
@@ -71,26 +73,26 @@ export const useTree = (type: StudioFeature, host: StudioHost, draft: ReturnType
async function selectByRoute(route: RouteLocationNormalized) {
const item = findItemFromRoute(tree.value, route)
- if (!item || item.id === currentItem.value.id) return
+ if (!item || item.fsPath === currentItem.value.fsPath) return
await select(item)
}
- async function selectItemById(id: string) {
- const treeItem = findItemFromId(tree.value, id)
+ async function selectItemByFsPath(fsPath: string) {
+ const treeItem = findItemFromFsPath(tree.value, fsPath)
if (!treeItem) {
await select(rootItem.value)
return
}
- if (treeItem.id === currentItem.value.id) return
+ if (treeItem.fsPath === currentItem.value.fsPath) return
await select(treeItem)
}
- async function selectParentById(id: string) {
- const parent = findParentFromId(tree.value, id)
+ async function selectParentByFsPath(fsPath: string) {
+ const parent = findParentFromFsPath(tree.value, fsPath)
await select(parent || rootItem.value)
}
@@ -107,7 +109,7 @@ export const useTree = (type: StudioFeature, host: StudioHost, draft: ReturnType
// Reselect current item to update status
if (selectItem) {
- select(findItemFromId(tree.value, currentItem.value.id)!)
+ select(findItemFromFsPath(tree.value, currentItem.value.fsPath)!)
}
// Rerender host app
@@ -135,8 +137,8 @@ export const useTree = (type: StudioFeature, host: StudioHost, draft: ReturnType
// parentItem,
select,
selectByRoute,
- selectItemById,
- selectParentById,
+ selectItemByFsPath,
+ selectParentByFsPath,
type,
draft,
}
diff --git a/src/app/src/pages/media.vue b/src/app/src/pages/media.vue
index 33aef6f0..a5743fa9 100644
--- a/src/app/src/pages/media.vue
+++ b/src/app/src/pages/media.vue
@@ -7,7 +7,7 @@ const { context } = useStudio()
const isUploading = ref(false)
const folderTree = computed(() => (context.activeTree.value.current.value || []).filter(f => f.type === 'directory'))
-const fileTree = computed(() => (context.activeTree.value.current.value || []).filter(f => f.type === 'file' && !f.id.endsWith('.gitkeep')))
+const fileTree = computed(() => (context.activeTree.value.current.value || []).filter(f => f.type === 'file' && !f.fsPath.endsWith('.gitkeep')))
const currentTreeItem = computed(() => context.activeTree.value.currentItem.value)
const currentDraftItem = computed(() => context.activeTree.value.draft.current.value)
diff --git a/src/app/src/pages/review.vue b/src/app/src/pages/review.vue
index 73584a86..4ed7baf4 100644
--- a/src/app/src/pages/review.vue
+++ b/src/app/src/pages/review.vue
@@ -71,7 +71,7 @@ const statusConfig = {
{{ statusConfig[status].label }}
diff --git a/src/app/src/types/config.ts b/src/app/src/types/config.ts
index e82923f9..28724f9a 100644
--- a/src/app/src/types/config.ts
+++ b/src/app/src/types/config.ts
@@ -8,5 +8,5 @@ export interface StudioConfig {
export interface StudioLocation {
active: boolean
feature: StudioFeature
- itemId: string
+ fsPath: string
}
diff --git a/src/app/src/types/context.ts b/src/app/src/types/context.ts
index 882eff29..0c24e925 100644
--- a/src/app/src/types/context.ts
+++ b/src/app/src/types/context.ts
@@ -44,7 +44,7 @@ export interface CreateFileParams {
}
export interface RenameFileParams {
- id: string
+ item: TreeItem
newFsPath: string
}
diff --git a/src/app/src/types/draft.ts b/src/app/src/types/draft.ts
index 2a5de33c..b03c2b77 100644
--- a/src/app/src/types/draft.ts
+++ b/src/app/src/types/draft.ts
@@ -15,7 +15,7 @@ export interface ContentConflict {
}
export interface DraftItem {
- id: string // nuxt/content id
+ id: string // nuxt/content id (with collection prefix)
fsPath: string // file path in content directory
status: DraftStatus // status
diff --git a/src/app/src/types/tree.ts b/src/app/src/types/tree.ts
index bd2fc77a..aa4a6fd8 100644
--- a/src/app/src/types/tree.ts
+++ b/src/app/src/types/tree.ts
@@ -12,13 +12,13 @@ export enum TreeStatus {
}
export interface TreeItem {
- id: string
name: string
- fsPath: string
+ fsPath: string // can be used as id
type: 'file' | 'directory' | 'root'
+ prefix: number | null
+ collections: string[]
status?: TreeStatus
routePath?: string
children?: TreeItem[]
- prefix?: number | null
hide?: boolean
}
diff --git a/src/app/src/utils/context.ts b/src/app/src/utils/context.ts
index 63c535ba..3f5af2f8 100644
--- a/src/app/src/utils/context.ts
+++ b/src/app/src/utils/context.ts
@@ -68,7 +68,7 @@ export function computeItemActions(itemActions: StudioAction
const forbiddenActions: StudioItemActionId[] = []
- if (item.id.startsWith(TreeRootId.Media)) {
+ if (item.collections.includes(TreeRootId.Media)) {
forbiddenActions.push(StudioItemActionId.DuplicateItem, StudioItemActionId.CreateDocumentFolder, StudioItemActionId.CreateDocument)
}
else {
diff --git a/src/app/src/utils/draft.ts b/src/app/src/utils/draft.ts
index dd240cff..f08d7aa3 100644
--- a/src/app/src/utils/draft.ts
+++ b/src/app/src/utils/draft.ts
@@ -76,15 +76,15 @@ export function findDescendantsFromId(list: DraftItem[], id: string): DraftItem[
const descendants: DraftItem[] = []
for (const item of list) {
- // Only file type are stored in the draft list, if id is exact match, return the file
- if (item.id === id) {
+ const isExactMatch = item.id === id
+ // If exact match it means id refers to a file, there is no need to browse the list further
+ if (isExactMatch) {
return [item]
}
- // Else the parent is a directory, add we need to browse the list and find all descendants
- // Descendants means id without collection prefix starts with the parent id
- const idWithoutCollectionPrefix = item.id.split('/').slice(1).join('/') || item.id
- if (idWithoutCollectionPrefix.startsWith(id + '/')) {
+ // Else it means id refers to a directory, we need to browse the list further to find all descendants
+ const isDescendant = item.id.startsWith(id + '/')
+ if (isDescendant) {
descendants.push(item)
}
}
diff --git a/src/app/src/utils/media.ts b/src/app/src/utils/media.ts
index 4fd7272a..837c7bc1 100644
--- a/src/app/src/utils/media.ts
+++ b/src/app/src/utils/media.ts
@@ -1,10 +1,3 @@
-import { joinURL } from 'ufo'
-import { TreeRootId } from '../types'
-
export function generateStemFromFsPath(fsPath: string) {
return fsPath.split('.').slice(0, -1).join('.')
}
-
-export function generateIdFromFsPath(fsPath: string) {
- return joinURL(TreeRootId.Media, fsPath)
-}
diff --git a/src/app/src/utils/tree.ts b/src/app/src/utils/tree.ts
index e44c0d41..d2ba7fca 100644
--- a/src/app/src/utils/tree.ts
+++ b/src/app/src/utils/tree.ts
@@ -11,6 +11,7 @@ import type { BaseItem } from '../types/item'
import { isEqual } from './database'
import { studioFlags } from '../composables/useStudio'
import { getFileExtension, parseName } from './file'
+import { joinURL } from 'ufo'
export const COLOR_STATUS_MAP: { [key in TreeStatus]?: string } = {
[TreeStatus.Created]: 'green',
@@ -74,13 +75,12 @@ TreeItem[] {
if (directorySegments.length === 0) {
const { name, prefix } = parseName(fileName)
fileName = name === 'index' ? 'home' : name
-
const fileItem: TreeItem = {
- id: dbItem.id,
name: fileName,
fsPath: dbItem.fsPath,
type: 'file',
prefix,
+ collections: [dbItem.id.split('/')[0]],
}
if (dbItem.fsPath.endsWith('.gitkeep')) {
@@ -103,13 +103,6 @@ TreeItem[] {
/*****************
Generate directory
******************/
- // Directory id do not start with collection prefix since files from different collections can be part of the same directory
- function dirIdBuilder(index: number) {
- const idSegments = dbItem.id.split('/').slice(1)
- const stemVsIdGap = idSegments.length - fsPathSegments.length
- return idSegments.slice(0, index + stemVsIdGap + 1).join('/')
- }
-
function dirFsPathBuilder(index: number) {
return directorySegments.slice(0, index + 1).join('/')
}
@@ -117,27 +110,32 @@ TreeItem[] {
let directoryChildren = tree
for (let i = 0; i < directorySegments.length; i++) {
const { name: dirName, prefix: dirPrefix } = parseName(directorySegments[i])
- const dirId = dirIdBuilder(i)
const dirFsPath = dirFsPathBuilder(i)
// Only create directory if it doesn't exist
- let directory = directoryMap.get(dirId)
+ let directory = directoryMap.get(dirFsPath)
if (!directory) {
directory = {
- id: dirId,
name: dirName,
fsPath: dirFsPath,
type: 'directory',
children: [],
prefix: dirPrefix,
+ collections: [dbItem.id.split('/')[0]],
}
- directoryMap.set(dirId, directory)
+ directoryMap.set(dirFsPath, directory)
- if (!directoryChildren.find(child => child.id === dirId)) {
+ if (!directoryChildren.find(child => child.fsPath === dirFsPath)) {
directoryChildren.push(directory)
}
}
+ else {
+ const collection = dbItem.id.split('/')[0]
+ if (!directory.collections.includes(collection)) {
+ directory.collections.push(collection)
+ }
+ }
directoryChildren = directory.children!
}
@@ -147,11 +145,11 @@ TreeItem[] {
******************************************/
const { name, prefix } = parseName(fileName)
const fileItem: TreeItem = {
- id: dbItem.id,
name,
fsPath: dbItem.fsPath,
type: 'file',
prefix,
+ collections: [dbItem.id.split('/')[0]],
}
if (dbItem.fsPath.endsWith('.gitkeep')) {
@@ -175,6 +173,10 @@ TreeItem[] {
return tree
}
+export function generateIdFromFsPath(fsPath: string, collectionName: string): string {
+ return joinURL(collectionName, fsPath)
+}
+
export function getTreeStatus(modified?: BaseItem, original?: BaseItem): TreeStatus {
if (studioFlags.dev) {
return TreeStatus.Opened
@@ -210,14 +212,14 @@ export function getTreeStatus(modified?: BaseItem, original?: BaseItem): TreeSta
return TreeStatus.Opened
}
-export function findItemFromId(tree: TreeItem[], id: string): TreeItem | null {
+export function findItemFromFsPath(tree: TreeItem[], fsPath: string): TreeItem | null {
for (const item of tree) {
- if (item.id === id) {
+ if (item.fsPath === fsPath) {
return item
}
if (item.children) {
- const foundInChildren = findItemFromId(item.children, id)
+ const foundInChildren = findItemFromFsPath(item.children, fsPath)
if (foundInChildren) {
return foundInChildren
}
@@ -227,16 +229,16 @@ export function findItemFromId(tree: TreeItem[], id: string): TreeItem | null {
return null
}
-export function findParentFromId(tree: TreeItem[], id: string): TreeItem | null {
+export function findParentFromFsPath(tree: TreeItem[], fsPath: string): TreeItem | null {
for (const item of tree) {
if (item.children) {
for (const child of item.children) {
- if (child.id === id) {
+ if (child.fsPath === fsPath) {
return item
}
}
- const foundParent = findParentFromId(item.children, id)
+ const foundParent = findParentFromFsPath(item.children, fsPath)
if (foundParent) {
return foundParent
}
@@ -264,17 +266,15 @@ export function findItemFromRoute(tree: TreeItem[], route: RouteLocationNormaliz
return null
}
-export function findDescendantsFileItemsFromId(tree: TreeItem[], id: string): TreeItem[] {
+export function findDescendantsFileItemsFromFsPath(tree: TreeItem[], fsPath: string): TreeItem[] {
const descendants: TreeItem[] = []
function traverse(items: TreeItem[]) {
for (const item of items) {
// File type
if (item.type === 'file') {
- const itemIdWithoutCollectionPrefix = item.id.split('/').slice(1).join('/')
- const isExactItem = item.id === id
- // Descendants means id without collection prefix starts with the parent id
- const isDescendant = itemIdWithoutCollectionPrefix.startsWith(id + '/')
+ const isExactItem = item.fsPath === fsPath
+ const isDescendant = item.fsPath.startsWith(fsPath + '/')
if (isExactItem || isDescendant) {
descendants.push(item)
}
@@ -282,7 +282,7 @@ export function findDescendantsFileItemsFromId(tree: TreeItem[], id: string): Tr
// Directory type
else {
// Directory found, add all children as descendants
- if (item.id === id) {
+ if (item.fsPath === fsPath) {
getAllDescendants(item.children!, descendants)
}
// Keep browsing children
diff --git a/src/app/test/integration/actions.test.ts b/src/app/test/integration/actions.test.ts
index 9535100d..5813870f 100644
--- a/src/app/test/integration/actions.test.ts
+++ b/src/app/test/integration/actions.test.ts
@@ -1,6 +1,6 @@
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { joinURL } from 'ufo'
-import { DraftStatus, StudioItemActionId, TreeRootId, StudioFeature, type StudioHost } from '../../src/types'
+import { DraftStatus, StudioItemActionId, TreeRootId, StudioFeature, type StudioHost, type TreeItem } from '../../src/types'
import { normalizeKey, generateUniqueDocumentId, generateUniqueMediaId, generateUniqueMediaName } from '../utils'
import { createMockHost, clearMockHost } from '../mocks/host'
import { createMockGit } from '../mocks/git'
@@ -8,7 +8,7 @@ import { createMockFile, createMockMedia, setupMediaMocks } from '../mocks/media
import { createMockDocument } from '../mocks/document'
import { createMockStorage } from '../mocks/composables'
import type { useGit } from '../../src/composables/useGit'
-import { findItemFromId } from '../../src/utils/tree'
+import { findItemFromFsPath } from '../../src/utils/tree'
const mockStorageDraft = createMockStorage()
const mockHost = createMockHost()
@@ -72,12 +72,18 @@ const cleanAndSetupContext = async (mockedHost: StudioHost, mockedGit: ReturnTyp
}
describe('Document - Action Chains Integration Tests', () => {
+ let filename: string
let documentId: string
+ let documentFsPath: string
+ let collection: string
let context: Awaited>
beforeEach(async () => {
currentRouteName = 'content'
- documentId = generateUniqueDocumentId()
+ collection = 'docs'
+ filename = 'document'
+ documentId = generateUniqueDocumentId(filename, collection)
+ documentFsPath = mockHost.document.getFileSystemPath(documentId)
context = await cleanAndSetupContext(mockHost, mockGit)
})
@@ -85,9 +91,8 @@ describe('Document - Action Chains Integration Tests', () => {
const consoleInfoSpy = vi.spyOn(console, 'info')
/* STEP 1: CREATE */
- const fsPath = mockHost.document.getFileSystemPath(documentId)
await context.itemActionHandler[StudioItemActionId.CreateDocument]({
- fsPath,
+ fsPath: documentFsPath,
content: 'Test content',
})
@@ -110,10 +115,8 @@ describe('Document - Action Chains Integration Tests', () => {
expect(context.activeTree.value.draft.list.value[0].original).toBeUndefined()
// Tree
- expect(context.activeTree.value.currentItem.value).toHaveProperty('id', documentId)
- expect(context.activeTree.value.currentItem.value).toHaveProperty('status', DraftStatus.Created)
expect(context.activeTree.value.root.value).toHaveLength(1)
- expect(context.activeTree.value.root.value[0]).toHaveProperty('id', documentId)
+ expect(context.activeTree.value.root.value[0]).toHaveProperty('fsPath', documentFsPath)
/* STEP 2: REVERT */
await context.itemActionHandler[StudioItemActionId.RevertItem](context.activeTree.value.currentItem.value)
@@ -125,7 +128,6 @@ describe('Document - Action Chains Integration Tests', () => {
expect(context.activeTree.value.draft.list.value).toHaveLength(0)
// Tree
- expect(context.activeTree.value.currentItem.value).toHaveProperty('type', 'root')
expect(context.activeTree.value.root.value).toHaveLength(0)
// Hooks
@@ -137,19 +139,21 @@ describe('Document - Action Chains Integration Tests', () => {
it('Create > Rename', async () => {
const consoleInfoSpy = vi.spyOn(console, 'info')
/* STEP 1: CREATE */
- const fsPath = mockHost.document.getFileSystemPath(documentId)
await context.itemActionHandler[StudioItemActionId.CreateDocument]({
- fsPath,
+ fsPath: documentFsPath,
content: 'Test content',
})
/* STEP 2: RENAME */
const newId = generateUniqueDocumentId()
const newFsPath = mockHost.document.getFileSystemPath(newId)
- const draftItem = context.activeTree.value.draft.list.value[0]
await context.itemActionHandler[StudioItemActionId.RenameItem]({
- id: draftItem.id,
newFsPath,
+ item: {
+ type: 'file',
+ fsPath: documentFsPath,
+ collections: [collection],
+ } as TreeItem,
})
// Draft in Storage
@@ -169,9 +173,7 @@ describe('Document - Action Chains Integration Tests', () => {
expect(list[0].modified).toHaveProperty('id', newId)
// Tree
- expect(context.activeTree.value.currentItem.value).toHaveProperty('type', 'root')
- expect(context.activeTree.value.root.value).toHaveLength(1)
- expect(context.activeTree.value.root.value[0]).toHaveProperty('id', newId)
+ expect(context.activeTree.value.root.value[0]).toHaveProperty('fsPath', newFsPath)
expect(context.activeTree.value.root.value[0]).toHaveProperty('status', DraftStatus.Created)
// Hooks
@@ -184,9 +186,8 @@ describe('Document - Action Chains Integration Tests', () => {
const consoleInfoSpy = vi.spyOn(console, 'info')
/* STEP 1: CREATE */
- const fsPath = mockHost.document.getFileSystemPath(documentId)
await context.itemActionHandler[StudioItemActionId.CreateDocument]({
- fsPath,
+ fsPath: documentFsPath,
content: 'Test content',
})
@@ -216,9 +217,8 @@ describe('Document - Action Chains Integration Tests', () => {
expect(context.activeTree.value.draft.list.value[0].modified).toHaveProperty('id', updatedDocument.id)
// Tree
- expect(context.activeTree.value.currentItem.value).toHaveProperty('id', documentId)
expect(context.activeTree.value.root.value).toHaveLength(1)
- expect(context.activeTree.value.root.value[0]).toHaveProperty('id', documentId)
+ expect(context.activeTree.value.root.value[0]).toHaveProperty('fsPath', documentFsPath)
/* STEP 3: REVERT */
await context.itemActionHandler[StudioItemActionId.RevertItem](context.activeTree.value.currentItem.value)
@@ -230,7 +230,6 @@ describe('Document - Action Chains Integration Tests', () => {
expect(context.activeTree.value.draft.list.value).toHaveLength(0)
// Tree
- expect(context.activeTree.value.currentItem.value).toHaveProperty('type', 'root')
expect(context.activeTree.value.root.value).toHaveLength(0)
// Hooks
@@ -243,12 +242,11 @@ describe('Document - Action Chains Integration Tests', () => {
const consoleInfoSpy = vi.spyOn(console, 'info')
// Create document in db and load tree
- const documentFsPath = mockHost.document.getFileSystemPath(documentId)
await mockHost.document.create(documentFsPath, 'Test content')
await context.activeTree.value.draft.load()
/* STEP 1: SELECT */
- await context.activeTree.value.selectItemById(documentId)
+ await context.activeTree.value.selectItemByFsPath(documentFsPath)
// Storage
expect(mockStorageDraft.size).toEqual(1)
@@ -266,9 +264,8 @@ describe('Document - Action Chains Integration Tests', () => {
expect(context.activeTree.value.draft.list.value[0].original).toHaveProperty('id', documentId)
// Tree
- expect(context.activeTree.value.currentItem.value).toHaveProperty('id', documentId)
expect(context.activeTree.value.root.value).toHaveLength(1)
- expect(context.activeTree.value.root.value[0]).toHaveProperty('id', documentId)
+ expect(context.activeTree.value.root.value[0]).toHaveProperty('fsPath', documentFsPath)
/* STEP 2: UPDATE */
const updatedDocument = createMockDocument(documentId, {
@@ -294,9 +291,8 @@ describe('Document - Action Chains Integration Tests', () => {
expect(context.activeTree.value.draft.list.value[0]).toHaveProperty('id', documentId)
// Tree
- expect(context.activeTree.value.currentItem.value).toHaveProperty('id', documentId)
expect(context.activeTree.value.root.value).toHaveLength(1)
- expect(context.activeTree.value.root.value[0]).toHaveProperty('id', documentId)
+ expect(context.activeTree.value.root.value[0]).toHaveProperty('fsPath', documentFsPath)
expect(context.activeTree.value.root.value[0].status).toEqual('updated')
/* STEP 3: REVERT */
@@ -316,9 +312,8 @@ describe('Document - Action Chains Integration Tests', () => {
expect(context.activeTree.value.draft.list.value[0]).toHaveProperty('id', documentId)
// Tree
- expect(context.activeTree.value.currentItem.value).toHaveProperty('id', documentId)
expect(context.activeTree.value.root.value).toHaveLength(1)
- expect(context.activeTree.value.root.value[0]).toHaveProperty('id', documentId)
+ expect(context.activeTree.value.root.value[0]).toHaveProperty('fsPath', documentFsPath)
// Hooks
expect(consoleInfoSpy).toHaveBeenCalledTimes(4)
@@ -332,12 +327,11 @@ describe('Document - Action Chains Integration Tests', () => {
const consoleInfoSpy = vi.spyOn(console, 'info')
// Create document in db and load tree
- const documentFsPath = mockHost.document.getFileSystemPath(documentId)
await mockHost.document.create(documentFsPath, 'Test content')
await context.activeTree.value.draft.load()
/* STEP 1: SELECT */
- await context.activeTree.value.selectItemById(documentId)
+ await context.activeTree.value.selectItemByFsPath(documentFsPath)
/* STEP 2: UPDATE */
const updatedDocument = createMockDocument(documentId, {
@@ -351,9 +345,12 @@ describe('Document - Action Chains Integration Tests', () => {
/* STEP 3: RENAME */
const newId = generateUniqueDocumentId()
const newFsPath = mockHost.document.getFileSystemPath(newId)
- const draftItem = context.activeTree.value.draft.list.value.find(d => d.id === documentId)!
await context.itemActionHandler[StudioItemActionId.RenameItem]({
- id: draftItem.id,
+ item: {
+ type: 'file',
+ fsPath: documentFsPath,
+ collections: [collection],
+ } as TreeItem,
newFsPath,
})
@@ -392,7 +389,7 @@ describe('Document - Action Chains Integration Tests', () => {
// Tree
expect(context.activeTree.value.root.value).toHaveLength(1)
- expect(context.activeTree.value.root.value[0]).toHaveProperty('id', newId)
+ expect(context.activeTree.value.root.value[0]).toHaveProperty('fsPath', newFsPath)
// Hooks
expect(consoleInfoSpy).toHaveBeenCalledTimes(4)
@@ -406,19 +403,21 @@ describe('Document - Action Chains Integration Tests', () => {
const consoleInfoSpy = vi.spyOn(console, 'info')
// Create document in db and load tree
- const documentFsPath = mockHost.document.getFileSystemPath(documentId)
await mockHost.document.create(documentFsPath, 'Test content')
await context.activeTree.value.draft.load()
/* STEP 1: SELECT */
- await context.activeTree.value.selectItemById(documentId)
+ await context.activeTree.value.selectItemByFsPath(documentFsPath)
/* STEP 2: RENAME */
const newId = generateUniqueDocumentId()
const newFsPath = mockHost.document.getFileSystemPath(newId)
- const draftItem = context.activeTree.value.draft.list.value[0]
await context.itemActionHandler[StudioItemActionId.RenameItem]({
- id: draftItem.id,
+ item: {
+ type: 'file',
+ fsPath: documentFsPath,
+ collections: [collection],
+ } as TreeItem,
newFsPath,
})
@@ -457,7 +456,7 @@ describe('Document - Action Chains Integration Tests', () => {
// Tree
expect(context.activeTree.value.root.value).toHaveLength(1)
- expect(context.activeTree.value.root.value[0]).toHaveProperty('id', newId)
+ expect(context.activeTree.value.root.value[0]).toHaveProperty('fsPath', newFsPath)
/* STEP 3: UPDATE */
const updatedDocument = createMockDocument(newId, {
@@ -504,7 +503,7 @@ describe('Document - Action Chains Integration Tests', () => {
// Tree
expect(context.activeTree.value.root.value).toHaveLength(1)
- expect(context.activeTree.value.root.value[0]).toHaveProperty('id', newId)
+ expect(context.activeTree.value.root.value[0]).toHaveProperty('fsPath', newFsPath)
// Hooks
expect(consoleInfoSpy).toHaveBeenCalledTimes(3)
@@ -519,25 +518,27 @@ describe('Document - Action Chains Integration Tests', () => {
const consoleInfoSpy = vi.spyOn(console, 'info')
// Create document in db and load tree
- const documentFsPath = mockHost.document.getFileSystemPath(documentId)
await mockHost.document.create(documentFsPath, 'Test content')
await context.activeTree.value.draft.load()
/* STEP 1: SELECT */
- await context.activeTree.value.selectItemById(documentId)
+ await context.activeTree.value.selectItemByFsPath(documentFsPath)
/* STEP 2: RENAME */
const newId = generateUniqueDocumentId()
const newFsPath = mockHost.document.getFileSystemPath(newId)
- const draftItem = context.activeTree.value.draft.list.value[0]
await context.itemActionHandler[StudioItemActionId.RenameItem]({
- id: draftItem.id,
+ item: {
+ type: 'file',
+ fsPath: documentFsPath,
+ collections: [collection],
+ } as TreeItem,
newFsPath,
})
/* STEP 3: REVERT */
const renamedTreeItem = context.activeTree.value.root.value[0]
- expect(renamedTreeItem).toHaveProperty('id', newId)
+ expect(renamedTreeItem).toHaveProperty('fsPath', newFsPath)
await context.itemActionHandler[StudioItemActionId.RevertItem](renamedTreeItem)
@@ -559,7 +560,7 @@ describe('Document - Action Chains Integration Tests', () => {
// Tree
expect(context.activeTree.value.root.value).toHaveLength(1)
- expect(context.activeTree.value.root.value[0]).toHaveProperty('id', documentId)
+ expect(context.activeTree.value.root.value[0]).toHaveProperty('fsPath', documentFsPath)
// Hooks
expect(consoleInfoSpy).toHaveBeenCalledTimes(4)
@@ -573,28 +574,33 @@ describe('Document - Action Chains Integration Tests', () => {
const consoleInfoSpy = vi.spyOn(console, 'info')
// Create document in db and load tree
- const documentFsPath = mockHost.document.getFileSystemPath(documentId)
await mockHost.document.create(documentFsPath, 'Test content')
await context.activeTree.value.draft.load()
/* STEP 1: SELECT */
- await context.activeTree.value.selectItemById(documentId)
+ await context.activeTree.value.selectItemByFsPath(documentFsPath)
/* STEP 2: RENAME */
const newId = generateUniqueDocumentId()
const newFsPath = mockHost.document.getFileSystemPath(newId)
- let draftItem = context.activeTree.value.draft.list.value[0]
await context.itemActionHandler[StudioItemActionId.RenameItem]({
- id: draftItem.id,
+ item: {
+ type: 'file',
+ fsPath: documentFsPath,
+ collections: [collection],
+ } as TreeItem,
newFsPath,
})
/* STEP 3: RENAME */
const newId2 = generateUniqueDocumentId()
const newFsPath2 = mockHost.document.getFileSystemPath(newId2)
- draftItem = context.activeTree.value.draft.list.value.find(d => d.id === newId)!
await context.itemActionHandler[StudioItemActionId.RenameItem]({
- id: draftItem.id,
+ item: {
+ type: 'file',
+ fsPath: newFsPath,
+ collections: [collection],
+ } as TreeItem,
newFsPath: newFsPath2,
})
@@ -634,7 +640,7 @@ describe('Document - Action Chains Integration Tests', () => {
// Tree
expect(context.activeTree.value.root.value).toHaveLength(1)
- expect(context.activeTree.value.root.value[0]).toHaveProperty('id', newId2)
+ expect(context.activeTree.value.root.value[0]).toHaveProperty('fsPath', newFsPath2)
// Hooks
expect(consoleInfoSpy).toHaveBeenCalledTimes(4)
@@ -649,6 +655,7 @@ describe('Media - Action Chains Integration Tests', () => {
let context: Awaited>
let mediaName: string
let mediaId: string
+ let mediaFsPath: string
const parentPath = '/'
beforeEach(async () => {
@@ -657,6 +664,7 @@ describe('Media - Action Chains Integration Tests', () => {
currentRouteName = 'media'
mediaName = generateUniqueMediaName()
mediaId = joinURL(TreeRootId.Media, mediaName)
+ mediaFsPath = mockHost.media.getFileSystemPath(mediaId)
context = await cleanAndSetupContext(mockHost, mockGit)
})
@@ -688,7 +696,7 @@ describe('Media - Action Chains Integration Tests', () => {
// Tree
expect(context.activeTree.value.root.value).toHaveLength(1)
- expect(context.activeTree.value.root.value[0]).toHaveProperty('id', mediaId)
+ expect(context.activeTree.value.root.value[0]).toHaveProperty('fsPath', mediaFsPath)
/* STEP 2: REVERT */
const mediaTreeItem = context.activeTree.value.root.value[0]
@@ -722,9 +730,12 @@ describe('Media - Action Chains Integration Tests', () => {
/* STEP 2: RENAME */
const newId = generateUniqueMediaId()
const newFsPath = mockHost.media.getFileSystemPath(newId)
- const draftItem = context.activeTree.value.draft.list.value[0]
await context.itemActionHandler[StudioItemActionId.RenameItem]({
- id: draftItem.id,
+ item: {
+ type: 'file',
+ fsPath: mediaFsPath,
+ collections: [TreeRootId.Media],
+ } as TreeItem,
newFsPath,
})
@@ -746,7 +757,7 @@ describe('Media - Action Chains Integration Tests', () => {
// Tree
expect(context.activeTree.value.root.value).toHaveLength(1)
- expect(context.activeTree.value.root.value[0]).toHaveProperty('id', newId)
+ expect(context.activeTree.value.root.value[0]).toHaveProperty('fsPath', newFsPath)
expect(context.activeTree.value.root.value[0]).toHaveProperty('status', DraftStatus.Created)
// Hooks
@@ -763,7 +774,7 @@ describe('Media - Action Chains Integration Tests', () => {
await context.activeTree.value.draft.load()
/* STEP 1: SELECT */
- await context.activeTree.value.selectItemById(mediaId)
+ await context.activeTree.value.selectItemByFsPath(mediaFsPath)
// Storage
expect(mockStorageDraft.size).toEqual(1)
@@ -783,10 +794,10 @@ describe('Media - Action Chains Integration Tests', () => {
// Tree
expect(context.activeTree.value.root.value).toHaveLength(1)
- expect(context.activeTree.value.root.value[0]).toHaveProperty('id', mediaId)
+ expect(context.activeTree.value.root.value[0]).toHaveProperty('fsPath', mediaFsPath)
/* STEP 2: DELETE */
- const itemTreeToDelete = findItemFromId(context.activeTree.value.root.value, mediaId)
+ const itemTreeToDelete = findItemFromFsPath(context.activeTree.value.root.value, mediaFsPath)
await context.itemActionHandler[StudioItemActionId.DeleteItem](itemTreeToDelete!)
// Storage
@@ -807,7 +818,7 @@ describe('Media - Action Chains Integration Tests', () => {
// Tree
expect(context.activeTree.value.root.value).toHaveLength(1)
- expect(context.activeTree.value.root.value[0]).toHaveProperty('id', mediaId)
+ expect(context.activeTree.value.root.value[0]).toHaveProperty('fsPath', mediaFsPath)
/* STEP 3: REVERT */
const mediaTreeItem = context.activeTree.value.root.value[0]
@@ -831,7 +842,7 @@ describe('Media - Action Chains Integration Tests', () => {
// Tree
expect(context.activeTree.value.root.value).toHaveLength(1)
- expect(context.activeTree.value.root.value[0]).toHaveProperty('id', mediaId)
+ expect(context.activeTree.value.root.value[0]).toHaveProperty('fsPath', mediaFsPath)
// Hooks
expect(consoleInfoSpy).toHaveBeenCalledTimes(4)
@@ -849,12 +860,16 @@ describe('Media - Action Chains Integration Tests', () => {
await context.activeTree.value.draft.load()
/* STEP 1: RENAME */
- await context.activeTree.value.selectItemById(mediaId)
+ await context.activeTree.value.selectItemByFsPath(mediaFsPath)
const newId = generateUniqueMediaId()
const newFsPath = mockHost.media.getFileSystemPath(newId)
await context.itemActionHandler[StudioItemActionId.RenameItem]({
- id: mediaId,
+ item: {
+ type: 'file',
+ fsPath: mediaFsPath,
+ collections: [TreeRootId.Media],
+ } as TreeItem,
newFsPath,
})
@@ -894,11 +909,11 @@ describe('Media - Action Chains Integration Tests', () => {
// Tree
expect(context.activeTree.value.root.value).toHaveLength(1)
- expect(context.activeTree.value.root.value[0]).toHaveProperty('id', newId)
+ expect(context.activeTree.value.root.value[0]).toHaveProperty('fsPath', newFsPath)
/* STEP 2: REVERT */
const renamedTreeItem = context.activeTree.value.root.value[0]
- expect(renamedTreeItem).toHaveProperty('id', newId)
+ expect(renamedTreeItem).toHaveProperty('fsPath', newFsPath)
await context.itemActionHandler[StudioItemActionId.RevertItem](renamedTreeItem)
// Storage
@@ -919,7 +934,7 @@ describe('Media - Action Chains Integration Tests', () => {
// Tree
expect(context.activeTree.value.root.value).toHaveLength(1)
- expect(context.activeTree.value.root.value[0]).toHaveProperty('id', mediaId)
+ expect(context.activeTree.value.root.value[0]).toHaveProperty('fsPath', mediaFsPath)
// Hooks
expect(consoleInfoSpy).toHaveBeenCalledTimes(4)
@@ -937,21 +952,28 @@ describe('Media - Action Chains Integration Tests', () => {
await context.activeTree.value.draft.load()
/* STEP 1: RENAME */
- await context.activeTree.value.selectItemById(mediaId)
+ await context.activeTree.value.selectItemByFsPath(mediaFsPath)
const newId = generateUniqueMediaId()
const newFsPath = mockHost.media.getFileSystemPath(newId)
await context.itemActionHandler[StudioItemActionId.RenameItem]({
- id: mediaId,
newFsPath,
+ item: {
+ type: 'file',
+ fsPath: mediaFsPath,
+ collections: [TreeRootId.Media],
+ } as TreeItem,
})
/* STEP 2: RENAME */
const newId2 = generateUniqueMediaId()
const newFsPath2 = mockHost.media.getFileSystemPath(newId2)
- const renamedDraftItem = context.activeTree.value.draft.list.value.find(d => d.id === newId)!
await context.itemActionHandler[StudioItemActionId.RenameItem]({
- id: renamedDraftItem.id,
+ item: {
+ type: 'file',
+ fsPath: newFsPath,
+ collections: [TreeRootId.Media],
+ } as TreeItem,
newFsPath: newFsPath2,
})
@@ -992,7 +1014,7 @@ describe('Media - Action Chains Integration Tests', () => {
// Tree
expect(context.activeTree.value.root.value).toHaveLength(1)
- expect(context.activeTree.value.root.value[0]).toHaveProperty('id', newId2)
+ expect(context.activeTree.value.root.value[0]).toHaveProperty('fsPath', newFsPath2)
// Hooks
expect(consoleInfoSpy).toHaveBeenCalledTimes(4)
@@ -1007,6 +1029,7 @@ describe('Media - Action Chains Integration Tests', () => {
const folderName = 'media-folder'
const folderPath = `/${folderName}`
const gitkeepId = joinURL(TreeRootId.Media, folderPath, '.gitkeep')
+ const gitkeepFsPath = mockHost.media.getFileSystemPath(gitkeepId)
/* STEP 1: CREATE FOLDER */
await context.itemActionHandler[StudioItemActionId.CreateMediaFolder]({
@@ -1035,12 +1058,13 @@ describe('Media - Action Chains Integration Tests', () => {
expect(rootTree[0]).toHaveProperty('type', 'directory')
expect(rootTree[0]).toHaveProperty('name', folderName)
expect(rootTree[0].children).toHaveLength(1)
- expect(rootTree[0].children![0]).toHaveProperty('id', gitkeepId)
+ expect(rootTree[0].children![0]).toHaveProperty('fsPath', gitkeepFsPath)
expect(rootTree[0].children![0]).toHaveProperty('hide', true)
/* STEP 2: UPLOAD MEDIA IN FOLDER */
const file = createMockFile(mediaName)
const uploadedMediaId = joinURL(TreeRootId.Media, folderPath, mediaName)
+ const uploadedMediaFsPath = mockHost.media.getFileSystemPath(uploadedMediaId)
await context.itemActionHandler[StudioItemActionId.UploadMedia]({
parentFsPath: folderPath,
files: [file],
@@ -1068,10 +1092,10 @@ describe('Media - Action Chains Integration Tests', () => {
expect(rootTree[0]).toHaveProperty('type', 'directory')
expect(rootTree[0]).toHaveProperty('name', folderName)
expect(rootTree[0].children).toHaveLength(1)
- expect(rootTree[0].children![0]).toHaveProperty('id', uploadedMediaId)
+ expect(rootTree[0].children![0]).toHaveProperty('fsPath', uploadedMediaFsPath)
/* STEP 3: REVERT UPLOADED MEDIA */
- const uploadedMediaTreeItem = context.activeTree.value.root.value[0].children!.find(item => item.id === uploadedMediaId)!
+ const uploadedMediaTreeItem = context.activeTree.value.root.value[0].children!.find(item => item.fsPath === uploadedMediaFsPath)!
await context.itemActionHandler[StudioItemActionId.RevertItem](uploadedMediaTreeItem!)
// Storage
diff --git a/src/app/test/mocks/tree.ts b/src/app/test/mocks/tree.ts
index e8596d72..29c8130f 100644
--- a/src/app/test/mocks/tree.ts
+++ b/src/app/test/mocks/tree.ts
@@ -2,44 +2,50 @@ import type { TreeItem } from '../../src/types/tree'
export const tree: TreeItem[] = [
{
- id: 'landing/index.md',
name: 'home',
fsPath: 'index.md',
type: 'file',
routePath: '/',
+ collections: ['landing'],
+ prefix: null,
},
{
- id: '1.getting-started',
name: 'getting-started',
- fsPath: 'getting-started',
+ fsPath: '1.getting-started',
type: 'directory',
+ collections: ['docs'],
+ prefix: 1,
children: [
{
- id: 'docs/1.getting-started/2.introduction.md',
name: 'introduction',
fsPath: '1.getting-started/2.introduction.md',
type: 'file',
routePath: '/getting-started/introduction',
+ collections: ['docs'],
+ prefix: 2,
},
{
- id: 'docs/1.getting-started/3.installation.md',
name: 'installation',
fsPath: '1.getting-started/3.installation.md',
type: 'file',
routePath: '/getting-started/installation',
+ collections: ['docs'],
+ prefix: 3,
},
{
- id: '1.getting-started/1.advanced',
name: 'advanced',
fsPath: '1.getting-started/1.advanced',
type: 'directory',
+ collections: ['docs'],
+ prefix: 1,
children: [
{
- id: 'docs/1.getting-started/1.advanced/1.studio.md',
name: 'studio',
fsPath: '1.getting-started/1.advanced/1.studio.md',
type: 'file',
routePath: '/getting-started/installation/advanced/studio',
+ collections: ['docs'],
+ prefix: 1,
},
],
},
diff --git a/src/app/test/unit/utils/context.test.ts b/src/app/test/unit/utils/context.test.ts
index 5e1b3444..c937c691 100644
--- a/src/app/test/unit/utils/context.test.ts
+++ b/src/app/test/unit/utils/context.test.ts
@@ -14,10 +14,12 @@ describe('computeItemActions', () => {
**************************************************/
it('should filter out actions for content root items', () => {
const rootItem: TreeItem = {
- id: TreeRootId.Content,
type: 'root',
name: 'content',
- } as TreeItem
+ fsPath: '/',
+ prefix: null,
+ collections: [TreeRootId.Content],
+ }
const result = computeItemActions(STUDIO_ITEM_ACTION_DEFINITIONS, rootItem)
@@ -34,10 +36,12 @@ describe('computeItemActions', () => {
it('should filter out actions for media root items', () => {
const rootItem: TreeItem = {
- id: TreeRootId.Media,
type: 'root',
name: 'media',
- } as TreeItem
+ fsPath: '/',
+ prefix: null,
+ collections: [TreeRootId.Media],
+ }
const result = computeItemActions(STUDIO_ITEM_ACTION_DEFINITIONS, rootItem)
@@ -55,11 +59,13 @@ describe('computeItemActions', () => {
it('should filter out actions for media root items with UPDATED status', () => {
const rootItem: TreeItem = {
- id: TreeRootId.Media,
type: 'root',
name: 'media',
+ fsPath: '/',
+ prefix: null,
status: TreeStatus.Updated,
- } as TreeItem
+ collections: [TreeRootId.Media],
+ }
const result = computeItemActions(STUDIO_ITEM_ACTION_DEFINITIONS, rootItem)
@@ -79,10 +85,12 @@ describe('computeItemActions', () => {
**************************************************/
it('should filter out actions for content file items without draft status', () => {
const fileItem: TreeItem = {
- id: 'docs/test.md',
type: 'file',
name: 'test.md',
- } as TreeItem
+ fsPath: 'test.md',
+ prefix: null,
+ collections: [TreeRootId.Content],
+ }
const result = computeItemActions(STUDIO_ITEM_ACTION_DEFINITIONS, fileItem)
@@ -98,11 +106,13 @@ describe('computeItemActions', () => {
it('should filter out actions for content file items with draft OPENED status', () => {
const fileItem: TreeItem = {
- id: 'docs/test.md',
type: 'file',
name: 'test.md',
+ fsPath: 'test.md',
+ prefix: null,
+ collections: [TreeRootId.Content],
status: TreeStatus.Opened,
- } as TreeItem
+ }
const result = computeItemActions(STUDIO_ITEM_ACTION_DEFINITIONS, fileItem)
@@ -118,11 +128,13 @@ describe('computeItemActions', () => {
it('should filter out actions for content file items with draft UPDATED status', () => {
const fileItem: TreeItem = {
- id: 'docs/test.md',
type: 'file',
name: 'test.md',
+ fsPath: 'test.md',
+ prefix: null,
+ collections: [TreeRootId.Content],
status: TreeStatus.Updated,
- } as TreeItem
+ }
const result = computeItemActions(STUDIO_ITEM_ACTION_DEFINITIONS, fileItem)
@@ -137,11 +149,13 @@ describe('computeItemActions', () => {
it('should filter out actions for content file items with draft CREATED status', () => {
const fileItem: TreeItem = {
- id: 'docs/test.md',
type: 'file',
name: 'test.md',
+ fsPath: 'test.md',
+ prefix: null,
+ collections: [TreeRootId.Content],
status: TreeStatus.Created,
- } as TreeItem
+ }
const result = computeItemActions(STUDIO_ITEM_ACTION_DEFINITIONS, fileItem)
@@ -156,11 +170,13 @@ describe('computeItemActions', () => {
it('should filter out actions for content file items with draft DELETED status', () => {
const fileItem: TreeItem = {
- id: 'docs/test.md',
type: 'file',
name: 'test.md',
+ fsPath: 'test.md',
+ prefix: null,
+ collections: [TreeRootId.Content],
status: TreeStatus.Deleted,
- } as TreeItem
+ }
const result = computeItemActions(STUDIO_ITEM_ACTION_DEFINITIONS, fileItem)
@@ -178,11 +194,13 @@ describe('computeItemActions', () => {
it('should filter out actions for content file items with draft RENAMED status', () => {
const fileItem: TreeItem = {
- id: 'docs/test.md',
type: 'file',
name: 'test.md',
+ fsPath: 'test.md',
+ prefix: null,
+ collections: [TreeRootId.Content],
status: TreeStatus.Renamed,
- } as TreeItem
+ }
const result = computeItemActions(STUDIO_ITEM_ACTION_DEFINITIONS, fileItem)
@@ -201,10 +219,12 @@ describe('computeItemActions', () => {
it('should filter out actions for content directory items without draft status', () => {
const directoryItem: TreeItem = {
- id: 'docs/folder',
type: 'directory',
name: 'folder',
- } as TreeItem
+ fsPath: 'folder',
+ prefix: null,
+ collections: [TreeRootId.Content],
+ }
const result = computeItemActions(STUDIO_ITEM_ACTION_DEFINITIONS, directoryItem)
@@ -220,11 +240,13 @@ describe('computeItemActions', () => {
it('should filter out actions for content directory items with draft OPENED status', () => {
const directoryItem: TreeItem = {
- id: 'docs/folder',
type: 'directory',
name: 'folder',
+ fsPath: 'folder',
+ prefix: null,
+ collections: [TreeRootId.Content],
status: TreeStatus.Opened,
- } as TreeItem
+ }
const result = computeItemActions(STUDIO_ITEM_ACTION_DEFINITIONS, directoryItem)
@@ -239,11 +261,13 @@ describe('computeItemActions', () => {
it('should filter out actions for content directory items with draft UPDATED status', () => {
const directoryItem: TreeItem = {
- id: 'docs/folder',
type: 'directory',
name: 'folder',
+ fsPath: 'folder',
+ prefix: null,
+ collections: [TreeRootId.Content],
status: TreeStatus.Updated,
- } as TreeItem
+ }
const result = computeItemActions(STUDIO_ITEM_ACTION_DEFINITIONS, directoryItem)
@@ -257,11 +281,13 @@ describe('computeItemActions', () => {
it('should filter out actions for content directory items with draft CREATED status', () => {
const directoryItem: TreeItem = {
- id: 'docs/folder',
type: 'directory',
name: 'folder',
+ fsPath: 'folder',
+ prefix: null,
+ collections: [TreeRootId.Content],
status: TreeStatus.Created,
- } as TreeItem
+ }
const result = computeItemActions(STUDIO_ITEM_ACTION_DEFINITIONS, directoryItem)
@@ -275,11 +301,13 @@ describe('computeItemActions', () => {
it('should filter out actions for content directory items with draft DELETED status', () => {
const directoryItem: TreeItem = {
- id: 'docs/folder',
type: 'directory',
name: 'folder',
+ fsPath: 'folder',
+ prefix: null,
+ collections: [TreeRootId.Content],
status: TreeStatus.Deleted,
- } as TreeItem
+ }
const result = computeItemActions(STUDIO_ITEM_ACTION_DEFINITIONS, directoryItem)
@@ -295,9 +323,11 @@ describe('computeItemActions', () => {
it('should filter out actions for content directory items with draft RENAMED status', () => {
const directoryItem: TreeItem = {
- id: 'docs/folder',
type: 'directory',
name: 'folder',
+ fsPath: 'folder',
+ prefix: null,
+ collections: [TreeRootId.Content],
status: TreeStatus.Renamed,
} as TreeItem
@@ -313,9 +343,12 @@ describe('computeItemActions', () => {
it('should filter out actions for media directory items', () => {
const directoryItem: TreeItem = {
- id: `${TreeRootId.Media}/folder`,
type: 'directory',
- } as TreeItem
+ name: 'folder',
+ fsPath: 'folder',
+ prefix: null,
+ collections: [TreeRootId.Media],
+ }
const result = computeItemActions(STUDIO_ITEM_ACTION_DEFINITIONS, directoryItem)
diff --git a/src/app/test/unit/utils/draft.test.ts b/src/app/test/unit/utils/draft.test.ts
index 3c24bdc6..1afd9e29 100644
--- a/src/app/test/unit/utils/draft.test.ts
+++ b/src/app/test/unit/utils/draft.test.ts
@@ -18,7 +18,7 @@ describe('findDescendantsFromId', () => {
})
it('returns all descendants files for a directory path', () => {
- const descendants = findDescendantsFromId(draftItemsList, '1.getting-started')
+ const descendants = findDescendantsFromId(draftItemsList, 'docs/1.getting-started')
expect(descendants).toHaveLength(5)
@@ -30,7 +30,7 @@ describe('findDescendantsFromId', () => {
})
it('returns all descendants for a nested directory path', () => {
- const descendants = findDescendantsFromId(draftItemsList, '1.getting-started/1.advanced')
+ const descendants = findDescendantsFromId(draftItemsList, 'docs/1.getting-started/1.advanced')
expect(descendants).toHaveLength(2)
diff --git a/src/app/test/unit/utils/tree.test.ts b/src/app/test/unit/utils/tree.test.ts
index 021d6433..3a20a5c1 100644
--- a/src/app/test/unit/utils/tree.test.ts
+++ b/src/app/test/unit/utils/tree.test.ts
@@ -1,11 +1,11 @@
import { describe, it, expect } from 'vitest'
-import { buildTree, findParentFromId, findItemFromRoute, findItemFromId, findDescendantsFileItemsFromId, getTreeStatus } from '../../../src/utils/tree'
+import { buildTree, findParentFromFsPath, findItemFromRoute, findItemFromFsPath, findDescendantsFileItemsFromFsPath, getTreeStatus, generateIdFromFsPath } from '../../../src/utils/tree'
import { tree } from '../../../test/mocks/tree'
import type { TreeItem } from '../../../src/types/tree'
import { dbItemsList, languagePrefixedDbItemsList, nestedDbItemsList } from '../../../test/mocks/database'
import type { DraftItem } from '../../../src/types/draft'
import type { MediaItem } from '../../../src/types'
-import { DraftStatus, TreeRootId, TreeStatus } from '../../../src/types'
+import { DraftStatus, TreeStatus, TreeRootId } from '../../../src/types'
import type { RouteLocationNormalized } from 'vue-router'
import type { DatabaseItem } from '../../../src/types/database'
import { joinURL, withLeadingSlash } from 'ufo'
@@ -14,35 +14,35 @@ describe('buildTree of documents with one level of depth', () => {
// Result based on dbItemsList mock
const result: TreeItem[] = [
{
- id: 'landing/index.md',
name: 'home',
fsPath: 'index.md',
type: 'file',
routePath: '/',
prefix: null,
+ collections: ['landing'],
},
{
- id: '1.getting-started',
name: 'getting-started',
fsPath: '1.getting-started',
type: 'directory',
prefix: 1,
+ collections: ['docs'],
children: [
{
- id: 'docs/1.getting-started/2.introduction.md',
name: 'introduction',
fsPath: '1.getting-started/2.introduction.md',
type: 'file',
routePath: '/getting-started/introduction',
prefix: 2,
+ collections: ['docs'],
},
{
- id: 'docs/1.getting-started/3.installation.md',
name: 'installation',
fsPath: '1.getting-started/3.installation.md',
type: 'file',
routePath: '/getting-started/installation',
prefix: 3,
+ collections: ['docs'],
},
],
},
@@ -50,7 +50,7 @@ describe('buildTree of documents with one level of depth', () => {
it('Without draft', () => {
const tree = buildTree(dbItemsList, null)
- expect(tree).toStrictEqual(result)
+ expect(tree).toStrictEqual(result as TreeItem[])
})
it('With draft', () => {
@@ -71,7 +71,7 @@ describe('buildTree of documents with one level of depth', () => {
...result[0],
status: TreeStatus.Created,
},
- ...result.slice(1)])
+ ...result.slice(1)] as TreeItem[])
})
it('With DELETED draft file in existing directory', () => {
@@ -97,17 +97,17 @@ describe('buildTree of documents with one level of depth', () => {
children: [
result[1].children![1],
{
- id: deletedDbItem.id,
name: 'introduction',
fsPath: deletedDbItem.fsPath,
type: 'file',
routePath: deletedDbItem.path,
status: TreeStatus.Deleted,
prefix: 2,
+ collections: ['docs'],
},
],
},
- ])
+ ] as TreeItem[])
})
it('With DELETED draft file in non existing directory', () => {
@@ -133,17 +133,17 @@ describe('buildTree of documents with one level of depth', () => {
children: [
result[1].children![0],
{
- id: deletedDbItem.id,
name: 'installation',
fsPath: deletedDbItem.fsPath,
type: 'file',
routePath: deletedDbItem.path,
status: TreeStatus.Deleted,
prefix: 3,
+ collections: ['docs'],
},
],
},
- ])
+ ] as TreeItem[])
})
it('With UPDATED draft file in existing directory (directory status is set)', () => {
@@ -165,7 +165,7 @@ describe('buildTree of documents with one level of depth', () => {
const tree = buildTree(dbItemsList, draftList)
- const expectedTree: TreeItem[] = [
+ const expectedTree = [
result[0],
{
...result[1],
@@ -180,7 +180,7 @@ describe('buildTree of documents with one level of depth', () => {
},
]
- expect(tree).toStrictEqual(expectedTree)
+ expect(tree).toStrictEqual(expectedTree as TreeItem[])
})
it('With CREATED and OPENED draft files in exsiting directory (directory status is set)', () => {
@@ -203,7 +203,7 @@ describe('buildTree of documents with one level of depth', () => {
const tree = buildTree(dbItemsList, draftList)
- const expectedTree: TreeItem[] = [
+ const expectedTree = [
result[0],
{
...result[1],
@@ -215,7 +215,7 @@ describe('buildTree of documents with one level of depth', () => {
},
]
- expect(tree).toStrictEqual(expectedTree)
+ expect(tree).toStrictEqual(expectedTree as TreeItem[])
})
it('With OPENED draft files in existing directory (directory status is not set)', () => {
@@ -238,7 +238,7 @@ describe('buildTree of documents with one level of depth', () => {
const tree = buildTree(dbItemsList, draftList)
- const expectedTree: TreeItem[] = [
+ const expectedTree = [
result[0],
{
...result[1],
@@ -251,12 +251,12 @@ describe('buildTree of documents with one level of depth', () => {
},
]
- expect(tree).toStrictEqual(expectedTree)
+ expect(tree).toStrictEqual(expectedTree as TreeItem[])
})
it('With same id DELETED and CREATED draft file resulting in RENAMED', () => {
const deletedDbItem: DatabaseItem & { fsPath: string } = dbItemsList[1] // 2.introduction.md
- const createdDbItem: DatabaseItem & { fsPath: string } = {
+ const createdDbItem: DatabaseItem & { fsPath: string } = { // 2.renamed.md
...dbItemsList[1],
id: 'docs/1.getting-started/2.renamed.md',
path: '/getting-started/renamed',
@@ -292,51 +292,51 @@ describe('buildTree of documents with one level of depth', () => {
children: [
...result[1].children!.slice(1),
{
- id: createdDbItem.id,
fsPath: createdDbItem.fsPath,
routePath: createdDbItem.path,
name: createdDbItem.path!.split('/').pop()!,
type: 'file',
status: TreeStatus.Renamed,
prefix: 2,
+ collections: ['docs'],
},
],
},
- ])
+ ] as TreeItem[])
})
})
describe('buildTree of documents with two levels of depth', () => {
const result: TreeItem[] = [
{
- id: '1.essentials',
name: 'essentials',
fsPath: '1.essentials',
type: 'directory',
prefix: 1,
+ collections: ['docs'],
children: [
{
- id: 'docs/1.essentials/2.configuration.md',
name: 'configuration',
fsPath: '1.essentials/2.configuration.md',
type: 'file',
routePath: '/essentials/configuration',
prefix: 2,
+ collections: ['docs'],
},
{
- id: '1.essentials/1.nested',
name: 'nested',
fsPath: '1.essentials/1.nested',
type: 'directory',
prefix: 1,
+ collections: ['docs'],
children: [
{
- id: 'docs/1.essentials/1.nested/2.advanced.md',
name: 'advanced',
fsPath: '1.essentials/1.nested/2.advanced.md',
type: 'file',
routePath: '/essentials/nested/advanced',
prefix: 2,
+ collections: ['docs'],
},
],
},
@@ -375,7 +375,7 @@ describe('buildTree of documents with two levels of depth', () => {
{ ...result[0].children![0], status: TreeStatus.Updated },
result[0].children![1],
],
- }])
+ }] as TreeItem[])
})
it('With nested levels of depth draft files', () => {
@@ -413,7 +413,7 @@ describe('buildTree of documents with two levels of depth', () => {
],
},
],
- }])
+ }] as TreeItem[])
})
it ('With DELETED draft file in nested non existing directory (directory status is set)', () => {
@@ -442,60 +442,60 @@ describe('buildTree of documents with two levels of depth', () => {
status: TreeStatus.Deleted,
children: [
{
- id: deletedDbItem.id,
name: 'advanced',
fsPath: deletedDbItem.fsPath,
routePath: deletedDbItem.path,
type: 'file',
status: TreeStatus.Deleted,
prefix: 2,
+ collections: ['docs'],
},
],
},
],
- }])
+ }] as TreeItem[])
})
})
describe('buildTree of documents with language prefixed', () => {
const result: TreeItem[] = [
{
- id: 'en',
name: 'en',
fsPath: 'en',
type: 'directory',
prefix: null,
+ collections: ['landing_en', 'docs_en'],
children: [
{
- id: 'landing_en/en/index.md',
name: 'index',
fsPath: 'en/index.md',
prefix: null,
type: 'file',
routePath: '/en',
+ collections: ['landing_en'],
},
{
- id: 'en/1.getting-started',
name: 'getting-started',
fsPath: 'en/1.getting-started',
type: 'directory',
prefix: 1,
+ collections: ['docs_en'],
children: [
{
- id: 'docs_en/en/1.getting-started/2.introduction.md',
name: 'introduction',
fsPath: 'en/1.getting-started/2.introduction.md',
type: 'file',
routePath: '/en/getting-started/introduction',
prefix: 2,
+ collections: ['docs_en'],
},
{
- id: 'docs_en/en/1.getting-started/3.installation.md',
name: 'installation',
fsPath: 'en/1.getting-started/3.installation.md',
type: 'file',
routePath: '/en/getting-started/installation',
prefix: 3,
+ collections: ['docs_en'],
},
],
},
@@ -512,23 +512,25 @@ describe('buildTree of documents with language prefixed', () => {
describe('buildTree of medias', () => {
it('With .gitkeep file in directory (file is marked as hidden)', () => {
const mediaFolderName = 'media-folder'
- const gitKeepId = joinURL(TreeRootId.Media, mediaFolderName, '.gitkeep')
- const mediaId = joinURL(TreeRootId.Media, mediaFolderName, 'image.jpg')
+ const gitKeepFsPath = joinURL(mediaFolderName, '.gitkeep')
+ const gitKeepId = generateIdFromFsPath(gitKeepFsPath, TreeRootId.Media)
+ const mediaFsPath = joinURL(mediaFolderName, 'image.jpg')
+ const mediaId = generateIdFromFsPath(mediaFsPath, TreeRootId.Media)
const gitkeepDbItem: MediaItem & { fsPath: string } = {
id: gitKeepId,
- fsPath: joinURL(mediaFolderName, '.gitkeep'),
+ fsPath: gitKeepFsPath,
stem: '.gitkeep',
extension: 'gitkeep',
- path: withLeadingSlash(joinURL(mediaFolderName, '.gitkeep')),
+ path: withLeadingSlash(gitKeepFsPath),
}
const mediaDbItem: MediaItem & { fsPath: string } = {
id: mediaId,
- fsPath: joinURL(mediaFolderName, 'image.jpg'),
+ fsPath: mediaFsPath,
stem: 'image',
extension: 'jpg',
- path: withLeadingSlash(joinURL(mediaFolderName, 'image.jpg')),
+ path: withLeadingSlash(mediaFsPath),
}
const draftList: DraftItem[] = [{
@@ -542,11 +544,11 @@ describe('buildTree of medias', () => {
const tree = buildTree([gitkeepDbItem, mediaDbItem], draftList)
expect(tree).toHaveLength(1)
- expect(tree[0]).toHaveProperty('id', mediaFolderName)
+ expect(tree[0]).toHaveProperty('fsPath', mediaFolderName)
expect(tree[0].children).toHaveLength(2)
- const gitkeepFile = tree[0].children!.find(item => item.id === gitKeepId)
- const imageFile = tree[0].children!.find(item => item.id === mediaId)
+ const gitkeepFile = tree[0].children!.find(item => item.fsPath === gitKeepFsPath)
+ const imageFile = tree[0].children!.find(item => item.fsPath === mediaFsPath)
expect(gitkeepFile).toHaveProperty('hide', true)
expect(imageFile).toBeDefined()
@@ -556,14 +558,14 @@ describe('buildTree of medias', () => {
describe('getTreeStatus', () => {
it('draft is CREATED if originalDatabaseItem is not defined', () => {
- const modified: DatabaseItem = dbItemsList[0] // landing/index.md
+ const modified: DatabaseItem = dbItemsList[0] // index.md
const status = getTreeStatus(modified, undefined)
expect(status).toBe(TreeStatus.Created)
})
it('draft is OPENED if originalDatabaseItem is defined and is the same as draftedDocument', () => {
- const original: DatabaseItem = dbItemsList[0] // landing/index.md
+ const original: DatabaseItem = dbItemsList[0] // index.md
const status = getTreeStatus(original, original)
expect(status).toBe(TreeStatus.Opened)
@@ -592,10 +594,10 @@ describe('getTreeStatus', () => {
})
it('draft is RENAMED if originalDatabaseItem is defined and id is different from draftedDocument', () => {
- const original: DatabaseItem = dbItemsList[0] // landing/index.md
+ const original: DatabaseItem = dbItemsList[0] // index.md
const modified: DatabaseItem = {
...original,
- id: 'landing/renamed.md',
+ id: 'renamed.md',
}
const status = getTreeStatus(modified, original)
@@ -603,7 +605,7 @@ describe('getTreeStatus', () => {
})
it('draft is DELETED if modifiedDatabaseItem is not defined', () => {
- const original: DatabaseItem = dbItemsList[0] // landing/index.md
+ const original: DatabaseItem = dbItemsList[0] // index.md
const modified: DatabaseItem = undefined as never
const status = getTreeStatus(modified, original)
@@ -611,31 +613,31 @@ describe('getTreeStatus', () => {
})
})
-describe('findParentFromId', () => {
+describe('findParentFromFsPath', () => {
it('should find direct parent of a child', () => {
- const parent = findParentFromId(tree, 'docs/1.getting-started/2.introduction.md')
+ const parent = findParentFromFsPath(tree, '1.getting-started/2.introduction.md')
expect(parent).toBeDefined()
- expect(parent?.id).toBe('1.getting-started')
+ expect(parent?.fsPath).toBe('1.getting-started')
})
it('should find nested parent', () => {
- const parent = findParentFromId(tree, 'docs/1.getting-started/1.advanced/1.studio.md')
+ const parent = findParentFromFsPath(tree, '1.getting-started/1.advanced/1.studio.md')
expect(parent).toBeDefined()
- expect(parent?.id).toBe('1.getting-started/1.advanced')
+ expect(parent?.fsPath).toBe('1.getting-started/1.advanced')
})
it('should return null for root level items', () => {
- const parent = findParentFromId(tree, 'landing/index.md')
+ const parent = findParentFromFsPath(tree, 'index.md')
expect(parent).toBeNull()
})
it('should return null for non-existent items', () => {
- const parent = findParentFromId(tree, 'non/existent/item.md')
+ const parent = findParentFromFsPath(tree, 'non/existent/item.md')
expect(parent).toBeNull()
})
it('should return null for empty tree', () => {
- const parent = findParentFromId([], 'any/item.md')
+ const parent = findParentFromFsPath([], 'any/item.md')
expect(parent).toBeNull()
})
})
@@ -647,7 +649,7 @@ describe('findItemFromRoute', () => {
const route = mockRoute('/')
const item = findItemFromRoute(tree, route)
expect(item).toBeDefined()
- expect(item?.id).toBe('landing/index.md')
+ expect(item?.fsPath).toBe('index.md')
expect(item?.name).toBe('home')
})
@@ -655,7 +657,7 @@ describe('findItemFromRoute', () => {
const route = mockRoute('/getting-started/introduction')
const item = findItemFromRoute(tree, route)
expect(item).toBeDefined()
- expect(item?.id).toBe('docs/1.getting-started/2.introduction.md')
+ expect(item?.fsPath).toBe('1.getting-started/2.introduction.md')
expect(item?.name).toBe('introduction')
})
@@ -663,7 +665,7 @@ describe('findItemFromRoute', () => {
const route = mockRoute('/getting-started/installation/advanced/studio')
const item = findItemFromRoute(tree, route)
expect(item).toBeDefined()
- expect(item?.id).toBe('docs/1.getting-started/1.advanced/1.studio.md')
+ expect(item?.fsPath).toBe('1.getting-started/1.advanced/1.studio.md')
expect(item?.name).toBe('studio')
})
@@ -680,110 +682,110 @@ describe('findItemFromRoute', () => {
})
})
-describe('findItemFromId', () => {
- it('should find root level item by id', () => {
- const item = findItemFromId(tree, 'landing/index.md')
+describe('findItemFromFsPath', () => {
+ it('should find root level item by fsPath', () => {
+ const item = findItemFromFsPath(tree, 'index.md')
expect(item).toBeDefined()
- expect(item?.id).toBe('landing/index.md')
+ expect(item?.fsPath).toBe('index.md')
expect(item?.name).toBe('home')
expect(item?.type).toBe('file')
})
- it('should find nested file by id', () => {
- const item = findItemFromId(tree, 'docs/1.getting-started/2.introduction.md')
+ it('should find nested file by fsPath', () => {
+ const item = findItemFromFsPath(tree, '1.getting-started/2.introduction.md')
expect(item).toBeDefined()
- expect(item?.id).toBe('docs/1.getting-started/2.introduction.md')
+ expect(item?.fsPath).toBe('1.getting-started/2.introduction.md')
expect(item?.name).toBe('introduction')
expect(item?.type).toBe('file')
})
- it('should find directory by id', () => {
- const item = findItemFromId(tree, '1.getting-started')
+ it('should find directory by fsPath', () => {
+ const item = findItemFromFsPath(tree, '1.getting-started')
expect(item).toBeDefined()
- expect(item?.id).toBe('1.getting-started')
+ expect(item?.fsPath).toBe('1.getting-started')
expect(item?.name).toBe('getting-started')
expect(item?.type).toBe('directory')
expect(item?.children).toBeDefined()
})
- it('should find deeply nested item by id', () => {
- const item = findItemFromId(tree, 'docs/1.getting-started/1.advanced/1.studio.md')
+ it('should find deeply nested item by fsPath', () => {
+ const item = findItemFromFsPath(tree, '1.getting-started/1.advanced/1.studio.md')
expect(item).toBeDefined()
- expect(item?.id).toBe('docs/1.getting-started/1.advanced/1.studio.md')
+ expect(item?.fsPath).toBe('1.getting-started/1.advanced/1.studio.md')
expect(item?.name).toBe('studio')
expect(item?.type).toBe('file')
})
- it('should find nested directory by id', () => {
- const item = findItemFromId(tree, '1.getting-started/1.advanced')
+ it('should find nested directory by fsPath', () => {
+ const item = findItemFromFsPath(tree, '1.getting-started/1.advanced')
expect(item).toBeDefined()
- expect(item?.id).toBe('1.getting-started/1.advanced')
+ expect(item?.fsPath).toBe('1.getting-started/1.advanced')
expect(item?.name).toBe('advanced')
expect(item?.type).toBe('directory')
})
- it('should return null for non-existent id', () => {
- const item = findItemFromId(tree, 'non/existent/item.md')
+ it('should return null for non-existent fsPath', () => {
+ const item = findItemFromFsPath(tree, 'non/existent/item.md')
expect(item).toBeNull()
})
- it('should return null for partial id match', () => {
- const item = findItemFromId(tree, 'docs/1.getting-started/2.introduction')
+ it('should return null for partial fsPath match', () => {
+ const item = findItemFromFsPath(tree, '1.getting-started/2.introduction')
expect(item).toBeNull()
})
it('should return null for empty tree', () => {
- const item = findItemFromId([], 'any/item.md')
+ const item = findItemFromFsPath([], 'any/item.md')
expect(item).toBeNull()
})
- it('should return null for empty id', () => {
- const item = findItemFromId(tree, '')
+ it('should return null for empty fsPath', () => {
+ const item = findItemFromFsPath(tree, '')
expect(item).toBeNull()
})
})
-describe('findDescendantsFileItemsFromId', () => {
+describe('findDescendantsFileItemsFromFsPath', () => {
it('returns exact match for a root level file', () => {
- const descendants = findDescendantsFileItemsFromId(tree, 'landing/index.md')
+ const descendants = findDescendantsFileItemsFromFsPath(tree, 'index.md')
expect(descendants).toHaveLength(1)
- expect(descendants[0].id).toBe('landing/index.md')
+ expect(descendants[0].fsPath).toBe('index.md')
})
it('returns empty array for non-existent id', () => {
- const descendants = findDescendantsFileItemsFromId(tree, 'non-existent/file.md')
+ const descendants = findDescendantsFileItemsFromFsPath(tree, 'non-existent/file.md')
expect(descendants).toHaveLength(0)
})
it('returns all descendants files for directory id', () => {
- const descendants = findDescendantsFileItemsFromId(tree, '1.getting-started')
+ const descendants = findDescendantsFileItemsFromFsPath(tree, '1.getting-started')
expect(descendants).toHaveLength(3)
- expect(descendants.some(item => item.id === 'docs/1.getting-started/2.introduction.md')).toBe(true)
- expect(descendants.some(item => item.id === 'docs/1.getting-started/3.installation.md')).toBe(true)
- expect(descendants.some(item => item.id === 'docs/1.getting-started/1.advanced/1.studio.md')).toBe(true)
+ expect(descendants.some(item => item.fsPath === '1.getting-started/2.introduction.md')).toBe(true)
+ expect(descendants.some(item => item.fsPath === '1.getting-started/3.installation.md')).toBe(true)
+ expect(descendants.some(item => item.fsPath === '1.getting-started/1.advanced/1.studio.md')).toBe(true)
})
it('returns all descendants files for nested directory id', () => {
- const descendants = findDescendantsFileItemsFromId(tree, '1.getting-started/1.advanced')
+ const descendants = findDescendantsFileItemsFromFsPath(tree, '1.getting-started/1.advanced')
expect(descendants).toHaveLength(1)
- expect(descendants.some(item => item.id === 'docs/1.getting-started/1.advanced/1.studio.md')).toBe(true)
+ expect(descendants.some(item => item.fsPath === '1.getting-started/1.advanced/1.studio.md')).toBe(true)
})
it('returns only the file itself when searching for a specific file', () => {
- const descendants = findDescendantsFileItemsFromId(tree, 'docs/1.getting-started/2.introduction.md')
+ const descendants = findDescendantsFileItemsFromFsPath(tree, '1.getting-started/2.introduction.md')
expect(descendants).toHaveLength(1)
- expect(descendants[0].id).toBe('docs/1.getting-started/2.introduction.md')
+ expect(descendants[0].fsPath).toBe('1.getting-started/2.introduction.md')
})
it('returns deeply nested file when searching by specific file id', () => {
- const descendants = findDescendantsFileItemsFromId(tree, 'docs/1.getting-started/1.advanced/1.studio.md')
+ const descendants = findDescendantsFileItemsFromFsPath(tree, '1.getting-started/1.advanced/1.studio.md')
expect(descendants).toHaveLength(1)
- expect(descendants[0].id).toBe('docs/1.getting-started/1.advanced/1.studio.md')
+ expect(descendants[0].fsPath).toBe('1.getting-started/1.advanced/1.studio.md')
})
})
diff --git a/src/app/test/utils/index.ts b/src/app/test/utils/index.ts
index 3e2dbe97..b19118b0 100644
--- a/src/app/test/utils/index.ts
+++ b/src/app/test/utils/index.ts
@@ -17,10 +17,10 @@ export function normalizeKey(key: string): string {
|| ''
}
-export function generateUniqueDocumentId(filename = 'document'): string {
+export function generateUniqueDocumentId(filename = 'document', collection = 'docs'): string {
const uniqueId = Math.random().toString(36).substr(2, 9)
// Add dummy collection prefix
- return joinURL('docs', `${filename}-${uniqueId}.md`)
+ return joinURL(collection, `${filename}-${uniqueId}.md`)
}
export function generateUniqueMediaId(filename = 'media'): string {
diff --git a/src/module/test/utils/collection.test.ts b/src/module/test/utils/collection.test.ts
index 3806a66e..f7635605 100644
--- a/src/module/test/utils/collection.test.ts
+++ b/src/module/test/utils/collection.test.ts
@@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest'
-import { getCollectionByFilePath, generateIdFromFsPath, generateFsPathFromId } from '../../src/runtime/utils/collection'
+import { getCollectionByFilePath, generateFsPathFromId } from '../../src/runtime/utils/collection'
import type { CollectionInfo, ResolvedCollectionSource } from '@nuxt/content'
import { collections } from '../mocks/collection'
@@ -43,51 +43,6 @@ describe('getCollectionByFilePath', () => {
})
})
-describe('generateIdFromFsPath', () => {
- it('should generate ID using collection name and prefix from landing collection', () => {
- const result = generateIdFromFsPath('index.md', collections.landing!)
- expect(result).toBe('landing/index.md')
- })
-
- it('should generate ID using collection name and prefix from docs collection', () => {
- const result = generateIdFromFsPath('1.getting-started/2.introduction.md', collections.docs!)
- expect(result).toBe('docs/1.getting-started/2.introduction.md')
- })
-
- it('should handle collection with empty prefix', () => {
- const mockCollection: CollectionInfo = {
- ...collections.docs as CollectionInfo,
- name: 'test',
- source: [{
- ...collections.docs!.source[0] as ResolvedCollectionSource,
- prefix: '',
- }],
- }
- const result = generateIdFromFsPath('content/test.md', mockCollection)
- expect(result).toBe('test/content/test.md')
- })
-
- // TODO handle multiple sources
- // it('should use the appropriate source when collection has multiple sources', () => {
- // const mockCollection: CollectionInfo = {
- // ...collections.docs,
- // name: 'multi-source',
- // source: [
- // {
- // ...collections.docs.source[0],
- // prefix: '/first',
- // },
- // {
- // ...collections.docs.source[0],
- // prefix: '/second',
- // },
- // ],
- // }
- // const result = generateIdFromFsPath(mockCollection, 'content/test.md')
- // expect(result).toBe('multi-source/first/content/test.md')
- // })
-})
-
describe('generateFsPathFromId', () => {
it('One file included', () => {
const id = 'landing/index.md'