diff --git a/src/composables/useTreeConversationTopology.js b/src/composables/useTreeConversationTopology.js index 686ede06c..bdae9df94 100644 --- a/src/composables/useTreeConversationTopology.js +++ b/src/composables/useTreeConversationTopology.js @@ -1,12 +1,12 @@ import { storeToRefs } from 'pinia' -import { computed, ref } from 'vue' +import { computed, ref, toValue } from 'vue' import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useStatusesStore } from 'src/stores/statuses.js' export function useTreeConversationTopology(conversation, replies, current) { const getStatusObject = (id) => useStatusesStore().allStatuses.get(id) - const getReplies = (id) => replies.value.get(id) ?? new Set() + const getReplies = (id) => toValue(replies).get(id) ?? new Set() const { mergedConfig } = storeToRefs(useMergedConfigStore()) @@ -21,12 +21,12 @@ export function useTreeConversationTopology(conversation, replies, current) { const ancestors = computed(() => { // First we fill map with empty sets and add given id's parent // as set's only element (if any) - const parentMap = conversation.value.reduce( + const parentMap = toValue(conversation).reduce( (result, { id, in_reply_to_status_id: irid }) => { if (!result.has(id)) { result.set(id, new Set()) } - if (irid && conversation.value.length !== 1) { + if (irid && toValue(conversation).length !== 1) { // Setting parent for current item result.get(id).add(irid) } @@ -48,26 +48,32 @@ export function useTreeConversationTopology(conversation, replies, current) { }) return parentMap }) - const topLevel = computed(() => + const topLevelIds = computed(() => [...ancestors.value.entries()] .filter(([id, ancestors]) => ancestors.size === 0) - .map(([id]) => getStatusObject(id)), + .map(([id]) => id), + ) + const topLevel = computed(() => + topLevelIds.value.map((id) => getStatusObject(id)), ) const getAncestorIds = (id) => ancestors.value.get(id) ?? new Set() const getAncestors = (id) => [...getAncestorIds(id)].map(getStatusObject).filter(Boolean) - const currentAncestors = computed(() => getAncestors(current.value).reverse()) + const currentAncestors = computed(() => + getAncestors(toValue(current)).reverse(), + ) const currentDepth = computed(() => currentAncestors.value.length) // Thread Display, for collapsing/expanding tree branches // Map of id => 'showing' | 'hidden' const threadDisplayOverride = ref(new Map()) const threadDisplayDefault = computed(() => { - return conversation.value.reduce((map, status) => { + return toValue(conversation).reduce((map, status) => { const { id } = status const depth = ancestors.value.get(id).size const state = (() => { + console.log(toValue(currentAncestors)) if (depth - currentDepth.value <= maxDepthToShowByDefault.value) { return 'showing' } else { @@ -107,5 +113,9 @@ export function useTreeConversationTopology(conversation, replies, current) { threadDisplay, showThreadRecursively, resetThreadDisplay, + + // For testing + topLevelIds, + ancestors, } } diff --git a/test/unit/specs/composables/useTreeConversationTopology.spec.js b/test/unit/specs/composables/useTreeConversationTopology.spec.js new file mode 100644 index 000000000..59528be54 --- /dev/null +++ b/test/unit/specs/composables/useTreeConversationTopology.spec.js @@ -0,0 +1,163 @@ +import { createTestingPinia } from '@pinia/testing' +import { setActivePinia } from 'pinia' + +import { useMergedConfigStore } from 'src/stores/merged_config.js' +import { useStatusesStore } from 'src/stores/statuses.js' + +import { useTreeConversationTopology } from 'src/composables/useTreeConversationTopology.js' + +describe('useTreeConversationTopology', () => { + const conversation = [ + { + id: 1, + }, + { + id: 2, + in_reply_to_status_id: 1, + }, + { + id: 3, + in_reply_to_status_id: 2, + }, + { + id: 4, + in_reply_to_status_id: 1, + }, + { + id: 5, + in_reply_to_status_id: 4, + }, + { + id: 6, + in_reply_to_status_id: 3, + }, + ] + const replies = new Map([ + [1, new Set([{ id: 2 }, { id: 4 }])], + [2, new Set([{ id: 3 }])], + [3, new Set([{ id: 6 }])], + [4, new Set([{ id: 5 }])], + [6, new Set([])], + ]) + + beforeEach(() => { + setActivePinia(createTestingPinia()) + useStatusesStore().allStatuses = new Map( + conversation.map(({ id }) => [id, { id }]), + ) + }) + + it('should form a correct topology', () => { + const { topLevel, ancestors, currentAncestors } = + useTreeConversationTopology(conversation, null, 3) + + expect(ancestors.value).to.eql( + new Map([ + [1, new Set()], + [2, new Set([1])], + [3, new Set([2, 1])], + [4, new Set([1])], + [5, new Set([4, 1])], + [6, new Set([3, 2, 1])], + ]), + ) + expect(currentAncestors.value).to.eql([{ id: 1 }, { id: 2 }]) + expect(topLevel.value.map(({ id }) => id)).to.eql([1]) + }) + + describe('ThreadDisplay', () => { + it('should set default ThreadDisplay with maxDepth 3', () => { + useMergedConfigStore().mergedConfig = { maxDepthInThread: 3 } + + const { threadDisplay } = useTreeConversationTopology(conversation) + + expect(threadDisplay.value).to.eql( + new Map([ + [1, 'showing'], + [2, 'showing'], + [3, 'hidden'], + [4, 'showing'], + [5, 'hidden'], + [6, 'hidden'], + ]), + ) + }) + + it('should set default ThreadDisplay with maxDepth 6', () => { + useMergedConfigStore().mergedConfig = { maxDepthInThread: 6 } + + const { threadDisplay } = useTreeConversationTopology(conversation) + + expect(threadDisplay.value).to.eql( + new Map([ + [1, 'showing'], + [2, 'showing'], + [3, 'showing'], + [4, 'showing'], + [5, 'showing'], + [6, 'showing'], + ]), + ) + }) + + it('should set default ThreadDisplay with maxDepth 3 && current depth being 3', () => { + useMergedConfigStore().mergedConfig = { maxDepthInThread: 3 } + + const { threadDisplay } = useTreeConversationTopology( + conversation, + null, + 4, + ) + + expect(threadDisplay.value).to.eql( + new Map([ + [1, 'showing'], + [2, 'showing'], + [3, 'showing'], + [4, 'showing'], + [5, 'showing'], + [6, 'hidden'], + ]), + ) + }) + + it('should recursively expand thread when calling showThreadRecursively', () => { + useMergedConfigStore().mergedConfig = { maxDepthInThread: 3 } + + const { threadDisplay, showThreadRecursively } = + useTreeConversationTopology(conversation, replies, 1) + + showThreadRecursively(3) + expect(threadDisplay.value).to.eql( + new Map([ + [1, 'showing'], + [2, 'showing'], + [3, 'showing'], + [4, 'showing'], + [5, 'hidden'], + [6, 'showing'], + ]), + ) + }) + + it('should reset thread when calling resetThreadDisplay', () => { + useMergedConfigStore().mergedConfig = { maxDepthInThread: 3 } + + const { threadDisplay, showThreadRecursively, resetThreadDisplay } = + useTreeConversationTopology(conversation, replies, 1) + + showThreadRecursively(3) + resetThreadDisplay() + expect(threadDisplay.value).to.eql( + new Map([ + [1, 'showing'], + [2, 'showing'], + [3, 'hidden'], + [4, 'showing'], + [5, 'hidden'], + [6, 'hidden'], + ]), + ) + }) + }) +})