diff --git a/src/custom-sort/custom-sort.ts b/src/custom-sort/custom-sort.ts index 945621b..3476cfc 100644 --- a/src/custom-sort/custom-sort.ts +++ b/src/custom-sort/custom-sort.ts @@ -618,6 +618,22 @@ export const sortOrderNeedsBookmarksOrder = (...orders: Array any) => { + for (let itemsToIterate: TAbstractFile[] = [root]; itemsToIterate.length > 0;) { + let firstItem = itemsToIterate.pop(); + if (firstItem) { + cb(firstItem) + if (isFolder(firstItem)) { + let childrenOfFolder = (firstItem as TFolder).children; + itemsToIterate = itemsToIterate.concat(childrenOfFolder) + } + } + } +}) + export const determineDatesForFolder = (folder: TFolder, recursive?: boolean): [ModifiedTime, CreatedTime] => { let mtimeOfFolder: ModifiedTime = DEFAULT_FOLDER_MTIME let ctimeOfFolder: CreatedTime = DEFAULT_FOLDER_CTIME @@ -635,7 +651,7 @@ export const determineDatesForFolder = (folder: TFolder, recursive?: boolean): [ } if (recursive) { - Vault.recurseChildren(folder, checkFile) + (Vault?.recurseChildren ?? recurseChildrenForUnitTests)(folder, checkFile) } else { folder.children.forEach((item) => checkFile(item)) } diff --git a/src/test/int/folder-dates.int.test.ts b/src/test/int/folder-dates.int.test.ts new file mode 100644 index 0000000..54d7330 --- /dev/null +++ b/src/test/int/folder-dates.int.test.ts @@ -0,0 +1,112 @@ +import { + TAbstractFile, + TFolder, + Vault +} from "obsidian"; +import { + DEFAULT_FOLDER_CTIME, + determineFolderDatesIfNeeded, + determineSortingGroup, + FolderItemForSorting +} from "../../custom-sort/custom-sort"; +import { + CustomSortGroupType, + CustomSortOrder, + CustomSortSpec +} from "../../custom-sort/custom-sort-types"; +import { + TIMESTAMP_OLDEST, + TIMESTAMP_NEWEST, + mockTFolderWithChildren, TIMESTAMP_DEEP_NEWEST, TIMESTAMP_DEEP_OLDEST +} from "../mocks"; + +describe('determineFolderDatesIfNeeded', () => { + it('should not be triggered if not needed - sorting method does not require it', () => { + // given + const folder: TFolder = mockTFolderWithChildren('Test folder 1') + const OUTSIDERS_GROUP_IDX = 0 + const sortSpec: CustomSortSpec = { + targetFoldersPaths: ['/'], + groups: [{ + type: CustomSortGroupType.Outsiders, + order: CustomSortOrder.alphabetical + }], + outsidersGroupIdx: OUTSIDERS_GROUP_IDX + } + + // when + const result: FolderItemForSorting = determineSortingGroup(folder, sortSpec) + determineFolderDatesIfNeeded([result], sortSpec) + + // then + expect(result.ctime).toEqual(DEFAULT_FOLDER_CTIME) + expect(result.mtime).toEqual(DEFAULT_FOLDER_CTIME) + }) + it.each( + [ + [CustomSortOrder.byCreatedTimeReverseAdvanced, undefined], + [CustomSortOrder.byCreatedTimeAdvanced, undefined], + [CustomSortOrder.byModifiedTimeAdvanced, undefined], + [CustomSortOrder.byModifiedTimeReverseAdvanced, undefined], + [CustomSortOrder.alphabetical, CustomSortOrder.byCreatedTimeReverseAdvanced], + [CustomSortOrder.alphabetical, CustomSortOrder.byCreatedTimeAdvanced], + [CustomSortOrder.alphabetical, CustomSortOrder.byModifiedTimeAdvanced], + [CustomSortOrder.alphabetical, CustomSortOrder.byModifiedTimeReverseAdvanced], + ])('should correctly determine dates, if triggered by %s under default %s (no deep orders requested)', (order: CustomSortOrder, folderOrder: CustomSortOrder | undefined) => { + // given + const folder: TFolder = mockTFolderWithChildren('Test folder 1') + const OUTSIDERS_GROUP_IDX = 0 + const sortSpec: CustomSortSpec = { + targetFoldersPaths: ['/'], + defaultOrder: folderOrder, + groups: [{ + type: CustomSortGroupType.Outsiders, + order: order + }], + outsidersGroupIdx: OUTSIDERS_GROUP_IDX + } + + // when + const result: FolderItemForSorting = determineSortingGroup(folder, sortSpec) + determineFolderDatesIfNeeded([result], sortSpec) + + // then + expect(result.ctime).toEqual(TIMESTAMP_OLDEST) + expect(result.mtime).toEqual(TIMESTAMP_NEWEST) + }) + it.each( + [ + [CustomSortOrder.alphabetical, CustomSortOrder.byCreatedTimeReverseAdvancedRecursive], + [CustomSortOrder.alphabetical, CustomSortOrder.byCreatedTimeAdvancedRecursive], + [CustomSortOrder.alphabetical, CustomSortOrder.byModifiedTimeAdvancedRecursive], + [CustomSortOrder.alphabetical, CustomSortOrder.byModifiedTimeReverseAdvancedRecursive], + [CustomSortOrder.byCreatedTimeReverseAdvancedRecursive, CustomSortOrder.byCreatedTimeReverseAdvanced], + [CustomSortOrder.byCreatedTimeAdvancedRecursive, CustomSortOrder.byCreatedTimeAdvanced], + [CustomSortOrder.byModifiedTimeAdvancedRecursive, CustomSortOrder.byModifiedTimeAdvanced], + [CustomSortOrder.byModifiedTimeReverseAdvancedRecursive, CustomSortOrder.byModifiedTimeReverseAdvanced], + ])('should correctly determine dates, if triggered by %s under default %s (deep orders)', (order: CustomSortOrder, folderOrder: CustomSortOrder | undefined) => { + // given + const folder: TFolder = mockTFolderWithChildren('Test folder 1') + const OUTSIDERS_GROUP_IDX = 0 + const sortSpec: CustomSortSpec = { + targetFoldersPaths: ['/'], + defaultOrder: folderOrder, + groups: [{ + type: CustomSortGroupType.Outsiders, + order: order + }], + outsidersGroupIdx: OUTSIDERS_GROUP_IDX + } + + // when + const result: FolderItemForSorting = determineSortingGroup(folder, sortSpec) + determineFolderDatesIfNeeded([result], sortSpec) + + // then + expect(result.ctime).toEqual(TIMESTAMP_DEEP_OLDEST) + expect(result.mtime).toEqual(TIMESTAMP_DEEP_NEWEST) + }) +}) + + + diff --git a/src/test/mocks.ts b/src/test/mocks.ts new file mode 100644 index 0000000..4b86251 --- /dev/null +++ b/src/test/mocks.ts @@ -0,0 +1,55 @@ +import { + TFile, + TFolder, + Vault +} from "obsidian"; + +export const mockTFile = (basename: string, ext: string, size?: number, ctime?: number, mtime?: number): TFile => { + return { + stat: { + ctime: ctime ?? 0, + mtime: mtime ?? 0, + size: size ?? 0 + }, + basename: basename, + extension: ext, + vault: {} as Vault, // To satisfy TS typechecking + path: `Some parent folder/${basename}.${ext}`, + name: `${basename}.${ext}`, + parent: {} as TFolder // To satisfy TS typechecking + } +} + +export const mockTFolder = (name: string, children?: Array, parent?: TFolder): TFolder => { + return { + isRoot(): boolean { return name === '/' }, + vault: {} as Vault, // To satisfy TS typechecking + path: `${name}`, + name: name, + parent: parent ?? ({} as TFolder), // To satisfy TS typechecking + children: children ?? [] + } +} + +export const MOCK_TIMESTAMP: number = 1656417542418 +export const TIMESTAMP_OLDEST: number = MOCK_TIMESTAMP +export const TIMESTAMP_DEEP_OLDEST: number = TIMESTAMP_OLDEST - 1000 +export const TIMESTAMP_NEWEST: number = MOCK_TIMESTAMP + 1000 +export const TIMESTAMP_DEEP_NEWEST: number = TIMESTAMP_NEWEST + 1000 +export const TIMESTAMP_INBETWEEN: number = MOCK_TIMESTAMP + 500 + +export const mockTFolderWithChildren = (name: string): TFolder => { + const subchild1: TFile = mockTFile('Sub-child file 1 created as deep oldest, modified recently', 'md', 100, TIMESTAMP_DEEP_OLDEST, TIMESTAMP_NEWEST) + const subfolder1: TFolder = mockTFolder('Subfolder with deep-oldest child file', [subchild1]) + + const subchild2: TFile = mockTFile('Sub-child file 1 created as deep newest, modified recently', 'md', 100, TIMESTAMP_OLDEST, TIMESTAMP_DEEP_NEWEST) + const subfolder2: TFolder = mockTFolder('Subfolder with deep-newest child file', [subchild2]) + + const child1: TFolder = mockTFolder('Section A') + const child2: TFolder = mockTFolder('Section B') + const child3: TFile = mockTFile('Child file 1 created as oldest, modified recently', 'md', 100, TIMESTAMP_OLDEST, TIMESTAMP_NEWEST) + const child4: TFile = mockTFile('Child file 2 created as newest, not modified at all', 'md', 100, TIMESTAMP_NEWEST, TIMESTAMP_NEWEST) + const child5: TFile = mockTFile('Child file 3 created inbetween, modified inbetween', 'md', 100, TIMESTAMP_INBETWEEN, TIMESTAMP_INBETWEEN) + + return mockTFolder(name, [child1, child2, child3, child4, child5, subfolder1, subfolder2]) +} diff --git a/src/test/unit/custom-sort.spec.ts b/src/test/unit/custom-sort.spec.ts index 87c89b4..abdc780 100644 --- a/src/test/unit/custom-sort.spec.ts +++ b/src/test/unit/custom-sort.spec.ts @@ -43,48 +43,12 @@ import { ObsidianIconFolder_PluginInstance, ObsidianIconFolderPlugin_Data } from "../../utils/ObsidianIconFolderPluginSignature"; - -const mockTFile = (basename: string, ext: string, size?: number, ctime?: number, mtime?: number): TFile => { - return { - stat: { - ctime: ctime ?? 0, - mtime: mtime ?? 0, - size: size ?? 0 - }, - basename: basename, - extension: ext, - vault: {} as Vault, // To satisfy TS typechecking - path: `Some parent folder/${basename}.${ext}`, - name: `${basename}.${ext}`, - parent: {} as TFolder // To satisfy TS typechecking - } -} - -const mockTFolder = (name: string, children?: Array, parent?: TFolder): TFolder => { - return { - isRoot(): boolean { return name === '/' }, - vault: {} as Vault, // To satisfy TS typechecking - path: `${name}`, - name: name, - parent: parent ?? ({} as TFolder), // To satisfy TS typechecking - children: children ?? [] - } -} - -const MOCK_TIMESTAMP: number = 1656417542418 -const TIMESTAMP_OLDEST: number = MOCK_TIMESTAMP -const TIMESTAMP_NEWEST: number = MOCK_TIMESTAMP + 1000 -const TIMESTAMP_INBETWEEN: number = MOCK_TIMESTAMP + 500 - -const mockTFolderWithChildren = (name: string): TFolder => { - const child1: TFolder = mockTFolder('Section A') - const child2: TFolder = mockTFolder('Section B') - const child3: TFile = mockTFile('Child file 1 created as oldest, modified recently', 'md', 100, TIMESTAMP_OLDEST, TIMESTAMP_NEWEST) - const child4: TFile = mockTFile('Child file 2 created as newest, not modified at all', 'md', 100, TIMESTAMP_NEWEST, TIMESTAMP_NEWEST) - const child5: TFile = mockTFile('Child file 3 created inbetween, modified inbetween', 'md', 100, TIMESTAMP_INBETWEEN, TIMESTAMP_INBETWEEN) - - return mockTFolder(name, [child1, child2, child3, child4, child5]) -} +import { + MOCK_TIMESTAMP, + mockTFile, + mockTFolder, + mockTFolderWithChildren +} from "../mocks"; const MockedLoc: Pos = { start: {col:0,offset:0,line:0}, @@ -2520,62 +2484,6 @@ describe('determineSortingGroup', () => { }) }) -describe('determineFolderDatesIfNeeded', () => { - it('should not be triggered if not needed - sorting method does not require it', () => { - // given - const folder: TFolder = mockTFolderWithChildren('Test folder 1') - const OUTSIDERS_GROUP_IDX = 0 - const sortSpec: CustomSortSpec = { - targetFoldersPaths: ['/'], - groups: [{ - type: CustomSortGroupType.Outsiders, - order: CustomSortOrder.alphabetical - }], - outsidersGroupIdx: OUTSIDERS_GROUP_IDX - } - - // when - const result: FolderItemForSorting = determineSortingGroup(folder, sortSpec) - determineFolderDatesIfNeeded([result], sortSpec) - - // then - expect(result.ctime).toEqual(DEFAULT_FOLDER_CTIME) - expect(result.mtime).toEqual(DEFAULT_FOLDER_CTIME) - }) - it.each( - [ - [CustomSortOrder.byCreatedTimeReverseAdvanced, undefined], - [CustomSortOrder.byCreatedTimeAdvanced, undefined], - [CustomSortOrder.byModifiedTimeAdvanced, undefined], - [CustomSortOrder.byModifiedTimeReverseAdvanced, undefined], - [CustomSortOrder.alphabetical, CustomSortOrder.byCreatedTimeReverseAdvanced], - [CustomSortOrder.alphabetical, CustomSortOrder.byCreatedTimeAdvanced], - [CustomSortOrder.alphabetical, CustomSortOrder.byModifiedTimeAdvanced], - [CustomSortOrder.alphabetical, CustomSortOrder.byModifiedTimeReverseAdvanced] - ])('should correctly determine dates, if triggered by %s under default %s', (order: CustomSortOrder, folderOrder: CustomSortOrder | undefined) => { - // given - const folder: TFolder = mockTFolderWithChildren('Test folder 1') - const OUTSIDERS_GROUP_IDX = 0 - const sortSpec: CustomSortSpec = { - targetFoldersPaths: ['/'], - defaultOrder: folderOrder, - groups: [{ - type: CustomSortGroupType.Outsiders, - order: order - }], - outsidersGroupIdx: OUTSIDERS_GROUP_IDX - } - - // when - const result: FolderItemForSorting = determineSortingGroup(folder, sortSpec) - determineFolderDatesIfNeeded([result], sortSpec) - - // then - expect(result.ctime).toEqual(TIMESTAMP_OLDEST) - expect(result.mtime).toEqual(TIMESTAMP_NEWEST) - }) -}) - describe('matchGroupRegex', () => { it( 'should correctly handle no match', () => { // given