diff --git a/src/components/DocumentationLayout.vue b/src/components/DocumentationLayout.vue index 65d4da948..ae4383f8f 100644 --- a/src/components/DocumentationLayout.vue +++ b/src/components/DocumentationLayout.vue @@ -30,46 +30,44 @@ > @@ -83,13 +81,10 @@ import QuickNavigationModal from 'docc-render/components/Navigator/QuickNavigati import AdjustableSidebarWidth from 'docc-render/components/AdjustableSidebarWidth.vue'; import Navigator from 'docc-render/components/Navigator.vue'; import onPageLoadScrollToFragment from 'docc-render/mixins/onPageLoadScrollToFragment'; -import Language from 'docc-render/constants/Language'; import { BreakpointName } from 'docc-render/utils/breakpoints'; import { storage } from 'docc-render/utils/storage'; import { getSetting } from 'docc-render/utils/theme-settings'; - -import IndexStore from 'docc-render/stores/IndexStore'; -import NavigatorDataProvider from 'theme/components/Navigator/NavigatorDataProvider.vue'; +import indexGetter from 'docc-render/mixins/indexGetter'; import DocumentationNav from 'theme/components/DocumentationTopic/DocumentationNav.vue'; const NAVIGATOR_HIDDEN_ON_LARGE_KEY = 'navigator-hidden-large'; @@ -100,13 +95,12 @@ export default { components: { Navigator, AdjustableSidebarWidth, - NavigatorDataProvider, Nav: DocumentationNav, QuickNavigationButton, QuickNavigationModal, PortalTarget, }, - mixins: [onPageLoadScrollToFragment], + mixins: [onPageLoadScrollToFragment, indexGetter], props: { enableNavigator: Boolean, diffAvailability: { @@ -152,16 +146,12 @@ export default { sidenavHiddenOnLarge: storage.get(NAVIGATOR_HIDDEN_ON_LARGE_KEY, false), showQuickNavigationModal: false, BreakpointName, - indexState: IndexStore.state, }; }, computed: { enableQuickNavigation: ({ isTargetIDE }) => ( !isTargetIDE && getSetting(['features', 'docs', 'quickNavigation', 'enable'], true) ), - quickNavNodes({ indexState: { flatChildren = {} }, interfaceLanguage }) { - return flatChildren[interfaceLanguage] ?? (flatChildren[Language.swift.key.url] || []); - }, sidebarProps: ({ sidenavVisibleOnMobile, enableNavigator, sidenavHiddenOnLarge, navigatorFixedWidth, }) => ( diff --git a/src/components/Navigator.vue b/src/components/Navigator.vue index bbc069faf..de3de1c8e 100644 --- a/src/components/Navigator.vue +++ b/src/components/Navigator.vue @@ -91,7 +91,7 @@ export default { type: Array, required: true, }, - technology: { + technologyProps: { type: Object, required: false, }, @@ -153,13 +153,6 @@ export default { * The root item is always a module */ type: () => TopicTypes.module, - technologyProps: ({ technology }) => ( - !technology ? null : { - technology: technology.title, - technologyPath: technology.path || technology.url, - isTechnologyBeta: technology.beta, - } - ), }, }; diff --git a/src/components/Navigator/NavigatorDataProvider.vue b/src/components/Navigator/NavigatorDataProvider.vue deleted file mode 100644 index 26fc9c76a..000000000 --- a/src/components/Navigator/NavigatorDataProvider.vue +++ /dev/null @@ -1,125 +0,0 @@ - - - diff --git a/src/mixins/indexGetter.js b/src/mixins/indexGetter.js new file mode 100644 index 000000000..11eefa7b4 --- /dev/null +++ b/src/mixins/indexGetter.js @@ -0,0 +1,52 @@ +/** + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2024 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors +*/ +import IndexStore from 'docc-render/stores/IndexStore'; +import Language from 'docc-render/constants/Language'; + +export default { + computed: { + indexNodes({ indexState: { flatChildren }, interfaceLanguage }) { + if (!flatChildren) return []; + return flatChildren[interfaceLanguage] ?? (flatChildren[Language.swift.key.url] || []); + }, + technologyProps({ indexState: { technologyProps }, interfaceLanguage, technology }) { + // Select technology props from fetched index data for the current language, fallback to swift + // If none available, fallback to technology data of the curr page or null + return technologyProps[interfaceLanguage] ?? technologyProps[Language.swift.key.url] + ?? (technology ? { + technology: technology.title, + technologyPath: technology.path || technology.url, + isTechnologyBeta: technology.beta, + } : null); + }, + navigatorProps: ({ + indexNodes, + indexState: { + flatChildren, + references, + apiChanges, + errorFetching, + }, + technologyProps, + }) => ({ + flatChildren: indexNodes, + navigatorReferences: references, + apiChanges, + isFetching: !flatChildren && !errorFetching, + errorFetching, + technologyProps, + }), + }, + data() { + return { + indexState: IndexStore.state, + }; + }, +}; diff --git a/src/stores/IndexStore.js b/src/stores/IndexStore.js index 1b2d2ee45..36e2aeac3 100644 --- a/src/stores/IndexStore.js +++ b/src/stores/IndexStore.js @@ -10,7 +10,7 @@ export default { state: { - flatChildren: {}, + flatChildren: null, references: {}, apiChanges: null, includedArchiveIdentifiers: [], @@ -18,7 +18,7 @@ export default { technologyProps: {}, }, reset() { - this.state.flatChildren = {}; + this.state.flatChildren = null; this.state.references = {}; this.state.apiChanges = null; this.state.includedArchiveIdentifiers = []; diff --git a/src/views/DocumentationTopic.vue b/src/views/DocumentationTopic.vue index 47f16f48a..0ecb5be6f 100644 --- a/src/views/DocumentationTopic.vue +++ b/src/views/DocumentationTopic.vue @@ -52,7 +52,7 @@ import { shouldFetchDataForRouteUpdate, } from 'docc-render/utils/data'; import DocumentationTopic from 'theme/components/DocumentationTopic.vue'; -import DocumentationLayout from 'docc-render/components/DocumentationLayout.vue'; +import DocumentationLayout from 'theme/components/DocumentationLayout.vue'; import DocumentationTopicStore from 'docc-render/stores/DocumentationTopicStore'; import indexProvider from 'theme/mixins/indexProvider'; import Language from 'docc-render/constants/Language'; diff --git a/tests/unit/components/DocumentationLayout.spec.js b/tests/unit/components/DocumentationLayout.spec.js index 30763b2b1..f58791621 100644 --- a/tests/unit/components/DocumentationLayout.spec.js +++ b/tests/unit/components/DocumentationLayout.spec.js @@ -8,14 +8,12 @@ * See https://swift.org/CONTRIBUTORS.txt for Swift project authors */ -import * as dataUtils from 'docc-render/utils/data'; import { shallowMount } from '@vue/test-utils'; import DocumentationTopicStore from 'docc-render/stores/DocumentationTopicStore'; import onPageLoadScrollToFragment from 'docc-render/mixins/onPageLoadScrollToFragment'; import DocumentationNav from 'docc-render/components/DocumentationTopic/DocumentationNav.vue'; import NavBase from 'docc-render/components/NavBase.vue'; import AdjustableSidebarWidth from '@/components/AdjustableSidebarWidth.vue'; -import NavigatorDataProvider from '@/components/Navigator/NavigatorDataProvider.vue'; import Language from '@/constants/Language'; import Navigator from '@/components/Navigator.vue'; import { storage } from '@/utils/storage'; @@ -30,36 +28,8 @@ jest.mock('docc-render/utils/scroll-lock'); jest.mock('docc-render/utils/storage'); jest.mock('docc-render/utils/theme-settings'); -const swiftChildren = [ - 'swiftChildrenMock', -]; -const objcChildren = [ - 'objcChildrenMock', -]; -jest.mock('docc-render/stores/IndexStore', () => ({ - state: { - flatChildren: { - swift: swiftChildren, - }, - }, -})); - storage.get.mockImplementation((key, value) => value); - -const TechnologyWithChildren = { - path: '/documentation/foo', - children: [], -}; - -const navigatorReferences = { foo: {} }; - -jest.spyOn(dataUtils, 'fetchIndexPathsData').mockResolvedValue({ - interfaceLanguages: { - [Language.swift.key.url]: [TechnologyWithChildren], - }, - references: navigatorReferences, -}); -getSetting.mockReturnValue(false); +getSetting.mockReturnValue(true); const { Nav, @@ -120,12 +90,24 @@ const AdjustableSidebarWidthSmallStub = { const stubs = { AdjustableSidebarWidth, - NavigatorDataProvider, DocumentationLayout, }; const provide = { isTargetIDE: false, store: DocumentationTopicStore }; +const navigatorReferences = { foo: {} }; +const technologyProps = { + technology: references['topic://foo'].title, + technologyPath: references['topic://foo'].url, + isTechnologyBeta: references['topic://foo'].beta, +}; +const swiftChildren = [ + 'swiftChildrenMock', +]; +const objcChildren = [ + 'objcChildrenMock', +]; + const createWrapper = props => shallowMount(DocumentationLayout, { propsData, stubs, @@ -136,6 +118,17 @@ const createWrapper = props => shallowMount(DocumentationLayout, { content: '
Content
', title: '
Title
', }, + data: () => ({ + indexState: { + flatChildren: { + swift: swiftChildren, + }, + errorFetching: false, + references: navigatorReferences, + technologyProps, + apiChanges: null, + }, + }), }); describe('DocumentationLayout', () => { @@ -174,45 +167,23 @@ describe('DocumentationLayout', () => { enableNavigator: true, fixedWidth: propsData.navigatorFixedWidth, }); - const { - technology, - parentTopicIdentifiers, - } = propsData; - expect(wrapper.find(NavigatorDataProvider).props()).toEqual({ - interfaceLanguage: Language.swift.key.url, - technologyUrl: technology.url, - apiChangesVersion: '', - }); + const { parentTopicIdentifiers } = propsData; + // its rendered by default const navigator = wrapper.find(Navigator); expect(navigator.exists()).toBe(true); expect(navigator.props()).toEqual({ errorFetching: false, - isFetching: true, + isFetching: false, // assert we are passing the first set of paths always parentTopicIdentifiers, references, scrollLockID: AdjustableSidebarWidth.constants.SCROLL_LOCK_ID, - // assert we are passing the default technology, if we dont have the children yet - technology, - apiChanges: null, - flatChildren: [], - navigatorReferences: {}, - renderFilterOnTop: false, - }); - expect(dataUtils.fetchIndexPathsData).toHaveBeenCalledTimes(1); - await flushPromises(); - expect(navigator.props()).toEqual({ - errorFetching: false, - isFetching: false, - scrollLockID: AdjustableSidebarWidth.constants.SCROLL_LOCK_ID, - renderFilterOnTop: false, - parentTopicIdentifiers, - references, - technology: TechnologyWithChildren, + technologyProps, apiChanges: null, - flatChildren: [], + flatChildren: swiftChildren, navigatorReferences, + renderFilterOnTop: false, }); // assert the nav is in wide format const nav = wrapper.find(Nav); @@ -220,7 +191,6 @@ describe('DocumentationLayout', () => { }); it('renders QuickNavigation if enableQuickNavigation is true', () => { - getSetting.mockReturnValueOnce(true); wrapper = createWrapper({ stubs: { ...stubs, @@ -238,6 +208,7 @@ describe('DocumentationLayout', () => { }); it('does not render QuickNavigation if enableQuickNavigation is false', () => { + getSetting.mockReturnValueOnce(false); wrapper = createWrapper({ stubs: { ...stubs, @@ -255,7 +226,6 @@ describe('DocumentationLayout', () => { }); it('does not render QuickNavigation and MagnifierIcon if enableNavigation is false', () => { - getSetting.mockReturnValueOnce(true); wrapper = createWrapper({ stubs: { ...stubs, @@ -269,7 +239,6 @@ describe('DocumentationLayout', () => { }); it('does not render QuickNavigation if enableQuickNavigation is true but IDE is being targeted', () => { - getSetting.mockReturnValueOnce(true); wrapper = createWrapper({ provide: { ...provide, isTargetIDE: true }, stubs: { @@ -288,7 +257,6 @@ describe('DocumentationLayout', () => { }); it('QuickNavigation renders Swift items', async () => { - getSetting.mockReturnValueOnce(true); wrapper = createWrapper({ stubs: { ...stubs, @@ -304,7 +272,6 @@ describe('DocumentationLayout', () => { }); it('QuickNavigation falls back to swift items, if no objc items', async () => { - getSetting.mockReturnValueOnce(true); wrapper = createWrapper({ stubs: { ...stubs, @@ -321,7 +288,6 @@ describe('DocumentationLayout', () => { }); it('QuickNavigation renders objc items', async () => { - getSetting.mockReturnValueOnce(true); wrapper = createWrapper({ stubs: { ...stubs, @@ -350,7 +316,6 @@ describe('DocumentationLayout', () => { wrapper = createWrapper({ stubs: { AdjustableSidebarWidth: AdjustableSidebarWidthSmallStub, - NavigatorDataProvider, }, }); }); @@ -389,15 +354,6 @@ describe('DocumentationLayout', () => { }); }); - it('provides the selected api changes, to the NavigatorDataProvider', () => { - wrapper.setProps({ - enableNavigator: true, - selectedAPIChangesVersion: 'latest_major', - }); - const dataProvider = wrapper.find(NavigatorDataProvider); - expect(dataProvider.props('apiChangesVersion')).toEqual('latest_major'); - }); - it('renders the Navigator with data when no reference is found for a top-level item', () => { const technologies = { id: 'topic://not-existing', @@ -417,9 +373,10 @@ describe('DocumentationLayout', () => { const navigator = wrapper.find(Navigator); expect(navigator.exists()).toBe(true); // assert the technology is the last fallback - expect(navigator.props('technology')).toEqual({ - title: 'FooTechnology', - url: '/documentation/foo', + expect(navigator.props('technologyProps')).toEqual({ + technology: 'FooTechnology', + technologyPath: '/documentation/foo', + isTechnologyBeta: undefined, }); }); @@ -444,9 +401,10 @@ describe('DocumentationLayout', () => { const navigator = wrapper.find(Navigator); expect(navigator.exists()).toBe(true); // assert the technology is the last fallback - expect(navigator.props('technology')).toEqual({ - title: 'FooTechnology', - url: '/documentation/foo', + expect(navigator.props('technologyProps')).toEqual({ + technology: 'FooTechnology', + technologyPath: '/documentation/foo', + isTechnologyBeta: undefined, }); }); @@ -460,7 +418,11 @@ describe('DocumentationLayout', () => { const navigator = wrapper.find(Navigator); expect(navigator.exists()).toBe(true); // assert the technology is the last fallback - expect(navigator.props('technology')).toEqual(propsData.technology); + expect(navigator.props('technologyProps')).toEqual({ + technology: propsData.technology.title, + technologyPath: propsData.technology.url, + isTechnologyBeta: propsData.technology.beta, + }); }); it('renders without a sidebar', () => { @@ -490,10 +452,6 @@ describe('DocumentationLayout', () => { .toEqual(expect.arrayContaining(['topic-wrapper', 'full-width-container'])); }); - it('renders without NavigatorDataProvider', async () => { - expect(wrapper.find(NavigatorDataProvider).exists()).toBe(false); - }); - it('handles the `@close`, on Navigator, for Mobile breakpoints', async () => { wrapper.setProps({ enableNavigator: true, diff --git a/tests/unit/components/Navigator.spec.js b/tests/unit/components/Navigator.spec.js index fbfc42f94..34ec65097 100644 --- a/tests/unit/components/Navigator.spec.js +++ b/tests/unit/components/Navigator.spec.js @@ -19,56 +19,9 @@ jest.mock('docc-render/utils/throttle', () => jest.fn(v => v)); const { LoadingNavigatorCard } = Navigator.components; -const technology = { - title: 'FooTechnology', - children: [ - { - title: 'Group Marker', - type: TopicTypes.groupMarker, - }, - { - title: 'Child0', - path: '/foo/child0', - type: 'article', - children: [ - { - title: 'Group Marker, Child 0', - type: TopicTypes.groupMarker, - }, - { - title: 'Child0_GrandChild0', - path: '/foo/child0/grandchild0', - type: 'tutorial', - }, - { - title: 'Child0_GrandChild1', - path: '/foo/child0/grandchild1', - type: 'tutorial', - children: [{ - title: 'Child0_GrandChild0_GreatGrandChild0', - path: '/foo/child0/grandchild0/greatgrandchild0', - type: 'tutorial', - }], - }, - { - title: 'Child0_GrandChild2', - path: '/foo/child0/grandchild2', - type: 'tutorial', - }, - ], - }, - { - title: 'Child1', - path: '/foo/child1/', - type: 'tutorial', - children: [{ - title: 'Child1_GrandChild0', - path: '/foo/child1/grandchild0', - type: 'method', - }], - }, - ], - path: '/path/to/technology', +const technologyProps = { + technology: 'FooTechnology', + technologyPath: '/path/to/technology', }; const references = { @@ -100,7 +53,7 @@ const navigatorReferences = { const defaultProps = { parentTopicIdentifiers, - technology, + technologyProps, references, scrollLockID: 'foo', renderFilterOnTop: false, @@ -141,8 +94,8 @@ describe('Navigator', () => { // will assert in another test children: [], type: TopicTypes.module, - technology: technology.title, - technologyPath: technology.path, + technology: technologyProps.technology, + technologyPath: technologyProps.technologyPath, isTechnologyBeta: false, scrollLockID: defaultProps.scrollLockID, renderFilterOnTop: defaultProps.renderFilterOnTop, @@ -153,16 +106,6 @@ describe('Navigator', () => { }); }); - it('renders an aria live that tells VO users when navigator is loading', () => { - const wrapper = createWrapper({ - propsData: { - isFetching: true, - }, - }); - expect(wrapper.find('[aria-live="polite"]').exists()).toBe(true); - expect(wrapper.find('[aria-live="polite"]').text()).toBe('navigator.navigator-is navigator.state.loading'); - }); - it('renders a LoadingNavigatorCard when navigator is loading', () => { const wrapper = createWrapper({ propsData: { @@ -186,6 +129,16 @@ describe('Navigator', () => { expect(wrapper.find(NavigatorCard).attributes('style')).toContain('display: none'); }); + it('renders an aria live that tells VO users when navigator is loading', () => { + const wrapper = createWrapper({ + propsData: { + isFetching: true, + }, + }); + expect(wrapper.find('[aria-live="polite"]').exists()).toBe(true); + expect(wrapper.find('[aria-live="polite"]').text()).toBe('navigator.navigator-is navigator.state.loading'); + }); + it('renders an aria live that tells VO users when navigator is ready', () => { const wrapper = createWrapper(); expect(wrapper.find('[aria-live="polite"]').exists()).toBe(true); @@ -193,9 +146,14 @@ describe('Navigator', () => { }); it('falls back to using the `technology.url` for the `technology-path`', () => { + const fallbackProps = { + technology: fallbackTechnology.title, + technologyPath: fallbackTechnology.url, + isTechnologyBeta: false, + }; const wrapper = createWrapper({ propsData: { - technology: fallbackTechnology, + technologyProps: fallbackProps, }, }); expect(wrapper.find(NavigatorCard).props()).toEqual({ @@ -203,9 +161,9 @@ describe('Navigator', () => { // will assert in another test children: [], type: TopicTypes.module, - technology: fallbackTechnology.title, - technologyPath: fallbackTechnology.url, - isTechnologyBeta: false, + technology: fallbackProps.technology, + technologyPath: fallbackProps.technologyPath, + isTechnologyBeta: fallbackProps.isTechnologyBeta, scrollLockID: defaultProps.scrollLockID, renderFilterOnTop: defaultProps.renderFilterOnTop, errorFetching: false, diff --git a/tests/unit/components/Navigator/NavigatorDataProvider.spec.js b/tests/unit/components/Navigator/NavigatorDataProvider.spec.js deleted file mode 100644 index f17394249..000000000 --- a/tests/unit/components/Navigator/NavigatorDataProvider.spec.js +++ /dev/null @@ -1,557 +0,0 @@ -/** - * This source file is part of the Swift.org open source project - * - * Copyright (c) 2022 Apple Inc. and the Swift project authors - * Licensed under Apache License v2.0 with Runtime Library Exception - * - * See https://swift.org/LICENSE.txt for license information - * See https://swift.org/CONTRIBUTORS.txt for Swift project authors -*/ - -import NavigatorDataProvider from '@/components/Navigator/NavigatorDataProvider.vue'; -import { shallowMount } from '@vue/test-utils'; -import Language from 'docc-render/constants/Language'; -import { TopicTypes } from '@/constants/TopicTypes'; -import { fetchIndexPathsData } from '@/utils/data'; -import { INDEX_ROOT_KEY } from '@/constants/sidebar'; -import { flushPromises } from '../../../../test-utils'; - -jest.mock('docc-render/utils/data'); - -const technologyUrl = '/documentation/foo'; - -const extendedTechnologies = { - path: '/documentation/foo', - children: [ - { - id: 'Group Marker', - title: 'Group Marker', - type: TopicTypes.groupMarker, - }, - { - id: 'Child0', - title: 'Child0', - path: '/documentation/foo/child0', - type: 'article', - children: [ - { - id: 'Group Marker, Child 0', - title: 'Group Marker, Child 0', - type: TopicTypes.groupMarker, - }, - { - id: 'Child0_GrandChild0', - title: 'Child0_GrandChild0', - path: '/documentation/foo/child0/grandchild0', - type: 'tutorial', - }, - { - id: 'Child0_GrandChild1', - title: 'Child0_GrandChild1', - path: '/documentation/foo/child0/grandchild1', - type: 'tutorial', - children: [{ - id: 'Child0_GrandChild0_GreatGrandChild0', - title: 'Child0_GrandChild0_GreatGrandChild0', - path: '/documentation/foo/child0/grandchild0/greatgrandchild0', - type: 'tutorial', - }], - }, - { - id: 'Child0_GrandChild2', - title: 'Child0_GrandChild2', - path: '/documentation/foo/child0/grandchild2', - type: 'tutorial', - }, - ], - }, - { - id: 'Child1', - title: 'Child1', - path: '/documentation/foo/child1/', - type: 'tutorial', - children: [{ - id: 'Child1_GrandChild0', - title: 'Child1_GrandChild0', - path: '/documentation/foo/child1/grandchild0', - type: 'method', - }], - }, - ], -}; - -const flatChildren = [ - { - uid: -196255993, - parent: '', - index: 0, - siblingsCount: 3, - depth: 0, - childUIDs: [], - }, - { - uid: -196255992, - parent: '', - index: 1, - siblingsCount: 3, - depth: 0, - childUIDs: [], - }, - { - uid: -196255991, - parent: '', - index: 2, - siblingsCount: 3, - depth: 0, - childUIDs: [], - }, -]; - -const swiftIndexOne = { - id: 'foo', - path: technologyUrl, - children: [1, 2, 3], -}; -const objectiveCIndexOne = { - id: 'foo-objc', - path: technologyUrl, - children: [1], -}; - -const references = { - foo: { bar: 'bar' }, -}; - -const includedArchiveIdentifiers = [ - 'foo', - 'bar', -]; - -const response = { - includedArchiveIdentifiers, - interfaceLanguages: { - [Language.swift.key.url]: [ - swiftIndexOne, - ], - [Language.objectiveC.key.url]: [ - objectiveCIndexOne, - ], - }, - references, -}; - -fetchIndexPathsData.mockResolvedValue(response); - -const defaultProps = { - technologyUrl, -}; - -let props = {}; - -const createWrapper = ({ propsData, ...others } = {}) => shallowMount(NavigatorDataProvider, { - propsData: { - ...defaultProps, - ...propsData, - }, - scopedSlots: { - default: (p) => { - props = p; - return 'Text'; - }, - }, - mocks: { - $route: { - params: { - locale: 'en-US', - }, - }, - }, - ...others, -}); - -describe('NavigatorDataProvider', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('fetches data when mounting NavigatorDataProvider', async () => { - expect(fetchIndexPathsData).toHaveBeenCalledTimes(0); - createWrapper(); - expect(fetchIndexPathsData).toHaveBeenCalledTimes(1); - expect(props).toHaveProperty('isFetching', true); - await flushPromises(); - expect(props).toEqual({ - apiChanges: null, - isFetchingAPIChanges: false, - isFetching: false, - technology: swiftIndexOne, - errorFetching: false, - flatChildren, - references, - }); - }); - - it('fetches data, even if being passed a none-root technology url', async () => { - expect(fetchIndexPathsData).toHaveBeenCalledTimes(0); - createWrapper({ - propsData: { - technologyUrl: `${technologyUrl}/bar/baz`, - }, - }); - expect(fetchIndexPathsData).toHaveBeenCalledTimes(1); - expect(props).toHaveProperty('isFetching', true); - await flushPromises(); - expect(props).toEqual({ - apiChanges: null, - isFetchingAPIChanges: false, - isFetching: false, - technology: swiftIndexOne, - errorFetching: false, - flatChildren, - references, - }); - }); - - it('sets errorFetching to true, when request errored', async () => { - expect(fetchIndexPathsData).toHaveBeenCalledTimes(0); - fetchIndexPathsData.mockRejectedValueOnce('Error'); - createWrapper(); - expect(fetchIndexPathsData).toHaveBeenCalledTimes(1); - expect(props).toHaveProperty('isFetching', true); - await flushPromises(); - expect(props).toEqual({ - apiChanges: null, - isFetchingAPIChanges: false, - isFetching: false, - technology: undefined, - errorFetching: true, - flatChildren: [], - references: {}, - }); - }); - - it('returns objc data', async () => { - expect(fetchIndexPathsData).toHaveBeenCalledTimes(0); - createWrapper({ - propsData: { - interfaceLanguage: Language.objectiveC.key.url, - }, - }); - expect(fetchIndexPathsData).toHaveBeenCalledTimes(1); - expect(props).toHaveProperty('isFetching', true); - await flushPromises(); - expect(props).toEqual({ - apiChanges: null, - isFetchingAPIChanges: false, - errorFetching: false, - isFetching: false, - technology: objectiveCIndexOne, - flatChildren: [{ - uid: -196255993, - parent: '', - index: 0, - siblingsCount: 1, - depth: 0, - childUIDs: [], - }], - references, - }); - }); - - it('falls back to swift items, if no objc items', async () => { - expect(fetchIndexPathsData).toHaveBeenCalledTimes(0); - fetchIndexPathsData.mockResolvedValueOnce({ - interfaceLanguages: { - [Language.swift.key.url]: response.interfaceLanguages[Language.swift.key.url], - }, - references, - }); - createWrapper({ - propsData: { - interfaceLanguage: Language.objectiveC.key.url, - }, - }); - expect(fetchIndexPathsData).toHaveBeenCalledTimes(1); - expect(props).toHaveProperty('isFetching', true); - await flushPromises(); - expect(props).toEqual({ - apiChanges: null, - isFetchingAPIChanges: false, - errorFetching: false, - isFetching: false, - technology: swiftIndexOne, - flatChildren, - references, - }); - }); - - it('returns the first technology, if none matches', async () => { - createWrapper({ - propsData: { - technologyUrl: '/documentation/bar', - }, - }); - await flushPromises(); - expect(props).toEqual({ - apiChanges: null, - isFetchingAPIChanges: false, - errorFetching: false, - isFetching: false, - technology: swiftIndexOne, - flatChildren, - references, - }); - }); - - it('returns undefined technology when index response is empty', async () => { - const emptyResponse = { interfaceLanguages: {} }; - fetchIndexPathsData.mockResolvedValue(emptyResponse); - createWrapper(); - await flushPromises(); - expect(props).toEqual({ - apiChanges: null, - isFetchingAPIChanges: false, - isFetching: false, - technology: undefined, - errorFetching: false, - flatChildren: [], - references: undefined, - }); - }); - - it('counts the amount of deprecated items a groupMarker has', async () => { - const technologyClone = JSON.parse(JSON.stringify(extendedTechnologies)); - technologyClone.children[1].deprecated = true; - technologyClone.children[2].deprecated = true; - technologyClone.children[1].children[1].deprecated = true; - fetchIndexPathsData.mockResolvedValue({ - interfaceLanguages: { - [Language.swift.key.url]: [ - technologyClone, - ], - }, - }); - createWrapper(); - await flushPromises(); - expect(props.flatChildren[0]).toHaveProperty('deprecatedChildrenCount', 2); - expect(props.flatChildren).toMatchSnapshot(); - }); - - it('removes the `beta` flag from children, if the parent is a `beta`', async () => { - const technologyClone = JSON.parse(JSON.stringify(extendedTechnologies)); - technologyClone.beta = true; - technologyClone.children[1].beta = true; - technologyClone.children[1].children[0].beta = true; - fetchIndexPathsData.mockResolvedValue({ - interfaceLanguages: { - [Language.swift.key.url]: [ - technologyClone, - ], - }, - }); - createWrapper(); - await flushPromises(); - expect(props).toMatchSnapshot(); - }); - - it('removes the `beta` flag from children, if the parent is a `beta`', async () => { - const technologyClone = JSON.parse(JSON.stringify(extendedTechnologies)); - technologyClone.children[1].beta = true; - technologyClone.children[1].children[1].beta = true; - // case where the direct parent is NOT `Beta`, but an ancestor is - technologyClone.children[1].children[2].children[0].beta = true; - // set an end node as beta - technologyClone.children[2].children[0].beta = true; - fetchIndexPathsData.mockResolvedValue({ - interfaceLanguages: { - [Language.swift.key.url]: [ - technologyClone, - ], - }, - }); - createWrapper(); - await flushPromises(); - expect(props).toMatchSnapshot(); - }); - - it('flattens deeply nested children and provides them to the NavigatorCard', async () => { - fetchIndexPathsData.mockResolvedValue({ - interfaceLanguages: { - [Language.swift.key.url]: [{ - path: '/documentation/foo', - children: [ - { - title: 'Group Marker', - type: TopicTypes.groupMarker, - }, - { - title: 'Child0', - path: '/foo/child0', - type: 'article', - children: [ - { - title: 'Group Marker, Child 0', - type: TopicTypes.groupMarker, - }, - { - title: 'Child0_GrandChild0', - path: '/foo/child0/grandchild0', - type: 'tutorial', - }, - { - title: 'Child0_GrandChild1', - path: '/foo/child0/grandchild1', - type: 'tutorial', - children: [{ - title: 'Child0_GrandChild0_GreatGrandChild0', - path: '/foo/child0/grandchild0/greatgrandchild0', - type: 'tutorial', - }], - }, - { - title: 'Child0_GrandChild2', - path: '/foo/child0/grandchild2', - type: 'tutorial', - }, - ], - }, - { - title: 'Child1', - path: '/foo/child1/', - type: 'tutorial', - children: [{ - title: 'Child1_GrandChild0', - path: '/foo/child1/grandchild0', - type: 'method', - }], - }, - ], - }], - }, - }); - createWrapper(); - await flushPromises(); - expect(props.flatChildren).toEqual([ - { - childUIDs: [ - 551503844, - -97593391, - ], - deprecatedChildrenCount: 0, - depth: 0, - index: 0, - parent: INDEX_ROOT_KEY, - siblingsCount: 3, - title: 'Group Marker', - type: 'groupMarker', - uid: -196255993, - }, - { - childUIDs: [ - -361407047, - 1438225895, - 1439149417, - 1440072939, - ], - depth: 0, - groupMarkerUID: -196255993, - index: 1, - parent: INDEX_ROOT_KEY, - path: '/foo/child0', - siblingsCount: 3, - title: 'Child0', - type: 'article', - uid: 551503844, - }, - { - childUIDs: [ - 1438225895, - 1439149417, - 1440072939, - ], - deprecatedChildrenCount: 0, - depth: 1, - index: 0, - parent: 551503844, - siblingsCount: 4, - title: 'Group Marker, Child 0', - type: 'groupMarker', - uid: -361407047, - }, - { - childUIDs: [], - depth: 1, - groupMarkerUID: -361407047, - index: 1, - parent: 551503844, - path: '/foo/child0/grandchild0', - siblingsCount: 4, - title: 'Child0_GrandChild0', - type: 'tutorial', - uid: 1438225895, - }, - { - childUIDs: [ - 305326087, - ], - depth: 1, - groupMarkerUID: -361407047, - index: 2, - parent: 551503844, - path: '/foo/child0/grandchild1', - siblingsCount: 4, - title: 'Child0_GrandChild1', - type: 'tutorial', - uid: 1439149417, - }, - { - childUIDs: [], - depth: 2, - index: 0, - parent: 1439149417, - path: '/foo/child0/grandchild0/greatgrandchild0', - siblingsCount: 1, - title: 'Child0_GrandChild0_GreatGrandChild0', - type: 'tutorial', - uid: 305326087, - }, - { - childUIDs: [], - depth: 1, - groupMarkerUID: -361407047, - index: 3, - parent: 551503844, - path: '/foo/child0/grandchild2', - siblingsCount: 4, - title: 'Child0_GrandChild2', - type: 'tutorial', - uid: 1440072939, - }, - { - childUIDs: [ - -827353283, - ], - depth: 0, - groupMarkerUID: -196255993, - index: 2, - parent: INDEX_ROOT_KEY, - path: '/foo/child1/', - siblingsCount: 3, - title: 'Child1', - type: 'tutorial', - uid: -97593391, - }, - { - childUIDs: [], - depth: 1, - index: 0, - parent: -97593391, - path: '/foo/child1/grandchild0', - siblingsCount: 1, - title: 'Child1_GrandChild0', - type: 'method', - uid: -827353283, - }, - ]); - }); -}); diff --git a/tests/unit/components/Navigator/__snapshots__/NavigatorDataProvider.spec.js.snap b/tests/unit/components/Navigator/__snapshots__/NavigatorDataProvider.spec.js.snap deleted file mode 100644 index f65dfb359..000000000 --- a/tests/unit/components/Navigator/__snapshots__/NavigatorDataProvider.spec.js.snap +++ /dev/null @@ -1,559 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`NavigatorDataProvider counts the amount of deprecated items a groupMarker has 1`] = ` -Array [ - Object { - "childUIDs": Array [ - 365745369, - -1561138820, - ], - "deprecatedChildrenCount": 2, - "depth": 0, - "id": "Group Marker", - "index": 0, - "parent": , - "siblingsCount": 3, - "title": "Group Marker", - "type": "groupMarker", - "uid": -196255993, - }, - Object { - "childUIDs": Array [ - -1893192488, - -2046366661, - -2045443139, - -2044519617, - ], - "deprecated": true, - "depth": 0, - "groupMarkerUID": -196255993, - "id": "Child0", - "index": 1, - "parent": , - "path": "/documentation/foo/child0", - "siblingsCount": 3, - "title": "Child0", - "type": "article", - "uid": 365745369, - }, - Object { - "childUIDs": Array [ - -2046366661, - -2045443139, - -2044519617, - ], - "deprecatedChildrenCount": 1, - "depth": 1, - "id": "Group Marker, Child 0", - "index": 0, - "parent": 365745369, - "siblingsCount": 4, - "title": "Group Marker, Child 0", - "type": "groupMarker", - "uid": -1893192488, - }, - Object { - "childUIDs": Array [], - "deprecated": true, - "depth": 1, - "groupMarkerUID": -1893192488, - "id": "Child0_GrandChild0", - "index": 1, - "parent": 365745369, - "path": "/documentation/foo/child0/grandchild0", - "siblingsCount": 4, - "title": "Child0_GrandChild0", - "type": "tutorial", - "uid": -2046366661, - }, - Object { - "childUIDs": Array [ - 366459535, - ], - "depth": 1, - "groupMarkerUID": -1893192488, - "id": "Child0_GrandChild1", - "index": 2, - "parent": 365745369, - "path": "/documentation/foo/child0/grandchild1", - "siblingsCount": 4, - "title": "Child0_GrandChild1", - "type": "tutorial", - "uid": -2045443139, - }, - Object { - "childUIDs": Array [], - "depth": 2, - "id": "Child0_GrandChild0_GreatGrandChild0", - "index": 0, - "parent": -2045443139, - "path": "/documentation/foo/child0/grandchild0/greatgrandchild0", - "siblingsCount": 1, - "title": "Child0_GrandChild0_GreatGrandChild0", - "type": "tutorial", - "uid": 366459535, - }, - Object { - "childUIDs": Array [], - "depth": 1, - "groupMarkerUID": -1893192488, - "id": "Child0_GrandChild2", - "index": 3, - "parent": 365745369, - "path": "/documentation/foo/child0/grandchild2", - "siblingsCount": 4, - "title": "Child0_GrandChild2", - "type": "tutorial", - "uid": -2044519617, - }, - Object { - "childUIDs": Array [ - 1069111095, - ], - "deprecated": true, - "depth": 0, - "groupMarkerUID": -196255993, - "id": "Child1", - "index": 2, - "parent": , - "path": "/documentation/foo/child1/", - "siblingsCount": 3, - "title": "Child1", - "type": "tutorial", - "uid": -1561138820, - }, - Object { - "childUIDs": Array [], - "depth": 1, - "id": "Child1_GrandChild0", - "index": 0, - "parent": -1561138820, - "path": "/documentation/foo/child1/grandchild0", - "siblingsCount": 1, - "title": "Child1_GrandChild0", - "type": "method", - "uid": 1069111095, - }, -] -`; - -exports[`NavigatorDataProvider removes the \`beta\` flag from children, if the parent is a \`beta\` 1`] = ` -Object { - "apiChanges": null, - "errorFetching": false, - "flatChildren": Array [ - Object { - "childUIDs": Array [ - 365745369, - -1561138820, - ], - "deprecatedChildrenCount": 0, - "depth": 0, - "id": "Group Marker", - "index": 0, - "parent": , - "siblingsCount": 3, - "title": "Group Marker", - "type": "groupMarker", - "uid": -196255993, - }, - Object { - "beta": false, - "childUIDs": Array [ - -1893192488, - -2046366661, - -2045443139, - -2044519617, - ], - "depth": 0, - "groupMarkerUID": -196255993, - "id": "Child0", - "index": 1, - "parent": , - "path": "/documentation/foo/child0", - "siblingsCount": 3, - "title": "Child0", - "type": "article", - "uid": 365745369, - }, - Object { - "beta": false, - "childUIDs": Array [ - -2046366661, - -2045443139, - -2044519617, - ], - "deprecatedChildrenCount": 0, - "depth": 1, - "id": "Group Marker, Child 0", - "index": 0, - "parent": 365745369, - "siblingsCount": 4, - "title": "Group Marker, Child 0", - "type": "groupMarker", - "uid": -1893192488, - }, - Object { - "childUIDs": Array [], - "depth": 1, - "groupMarkerUID": -1893192488, - "id": "Child0_GrandChild0", - "index": 1, - "parent": 365745369, - "path": "/documentation/foo/child0/grandchild0", - "siblingsCount": 4, - "title": "Child0_GrandChild0", - "type": "tutorial", - "uid": -2046366661, - }, - Object { - "childUIDs": Array [ - 366459535, - ], - "depth": 1, - "groupMarkerUID": -1893192488, - "id": "Child0_GrandChild1", - "index": 2, - "parent": 365745369, - "path": "/documentation/foo/child0/grandchild1", - "siblingsCount": 4, - "title": "Child0_GrandChild1", - "type": "tutorial", - "uid": -2045443139, - }, - Object { - "childUIDs": Array [], - "depth": 2, - "id": "Child0_GrandChild0_GreatGrandChild0", - "index": 0, - "parent": -2045443139, - "path": "/documentation/foo/child0/grandchild0/greatgrandchild0", - "siblingsCount": 1, - "title": "Child0_GrandChild0_GreatGrandChild0", - "type": "tutorial", - "uid": 366459535, - }, - Object { - "childUIDs": Array [], - "depth": 1, - "groupMarkerUID": -1893192488, - "id": "Child0_GrandChild2", - "index": 3, - "parent": 365745369, - "path": "/documentation/foo/child0/grandchild2", - "siblingsCount": 4, - "title": "Child0_GrandChild2", - "type": "tutorial", - "uid": -2044519617, - }, - Object { - "childUIDs": Array [ - 1069111095, - ], - "depth": 0, - "groupMarkerUID": -196255993, - "id": "Child1", - "index": 2, - "parent": , - "path": "/documentation/foo/child1/", - "siblingsCount": 3, - "title": "Child1", - "type": "tutorial", - "uid": -1561138820, - }, - Object { - "childUIDs": Array [], - "depth": 1, - "id": "Child1_GrandChild0", - "index": 0, - "parent": -1561138820, - "path": "/documentation/foo/child1/grandchild0", - "siblingsCount": 1, - "title": "Child1_GrandChild0", - "type": "method", - "uid": 1069111095, - }, - ], - "isFetching": false, - "isFetchingAPIChanges": false, - "references": undefined, - "technology": Object { - "beta": true, - "children": Array [ - Object { - "id": "Group Marker", - "title": "Group Marker", - "type": "groupMarker", - }, - Object { - "beta": true, - "children": Array [ - Object { - "beta": true, - "id": "Group Marker, Child 0", - "title": "Group Marker, Child 0", - "type": "groupMarker", - }, - Object { - "id": "Child0_GrandChild0", - "path": "/documentation/foo/child0/grandchild0", - "title": "Child0_GrandChild0", - "type": "tutorial", - }, - Object { - "children": Array [ - Object { - "id": "Child0_GrandChild0_GreatGrandChild0", - "path": "/documentation/foo/child0/grandchild0/greatgrandchild0", - "title": "Child0_GrandChild0_GreatGrandChild0", - "type": "tutorial", - }, - ], - "id": "Child0_GrandChild1", - "path": "/documentation/foo/child0/grandchild1", - "title": "Child0_GrandChild1", - "type": "tutorial", - }, - Object { - "id": "Child0_GrandChild2", - "path": "/documentation/foo/child0/grandchild2", - "title": "Child0_GrandChild2", - "type": "tutorial", - }, - ], - "id": "Child0", - "path": "/documentation/foo/child0", - "title": "Child0", - "type": "article", - }, - Object { - "children": Array [ - Object { - "id": "Child1_GrandChild0", - "path": "/documentation/foo/child1/grandchild0", - "title": "Child1_GrandChild0", - "type": "method", - }, - ], - "id": "Child1", - "path": "/documentation/foo/child1/", - "title": "Child1", - "type": "tutorial", - }, - ], - "path": "/documentation/foo", - }, -} -`; - -exports[`NavigatorDataProvider removes the \`beta\` flag from children, if the parent is a \`beta\` 2`] = ` -Object { - "apiChanges": null, - "errorFetching": false, - "flatChildren": Array [ - Object { - "childUIDs": Array [ - 365745369, - -1561138820, - ], - "deprecatedChildrenCount": 0, - "depth": 0, - "id": "Group Marker", - "index": 0, - "parent": , - "siblingsCount": 3, - "title": "Group Marker", - "type": "groupMarker", - "uid": -196255993, - }, - Object { - "beta": true, - "childUIDs": Array [ - -1893192488, - -2046366661, - -2045443139, - -2044519617, - ], - "depth": 0, - "groupMarkerUID": -196255993, - "id": "Child0", - "index": 1, - "parent": , - "path": "/documentation/foo/child0", - "siblingsCount": 3, - "title": "Child0", - "type": "article", - "uid": 365745369, - }, - Object { - "childUIDs": Array [ - -2046366661, - -2045443139, - -2044519617, - ], - "deprecatedChildrenCount": 0, - "depth": 1, - "id": "Group Marker, Child 0", - "index": 0, - "parent": 365745369, - "siblingsCount": 4, - "title": "Group Marker, Child 0", - "type": "groupMarker", - "uid": -1893192488, - }, - Object { - "beta": false, - "childUIDs": Array [], - "depth": 1, - "groupMarkerUID": -1893192488, - "id": "Child0_GrandChild0", - "index": 1, - "parent": 365745369, - "path": "/documentation/foo/child0/grandchild0", - "siblingsCount": 4, - "title": "Child0_GrandChild0", - "type": "tutorial", - "uid": -2046366661, - }, - Object { - "childUIDs": Array [ - 366459535, - ], - "depth": 1, - "groupMarkerUID": -1893192488, - "id": "Child0_GrandChild1", - "index": 2, - "parent": 365745369, - "path": "/documentation/foo/child0/grandchild1", - "siblingsCount": 4, - "title": "Child0_GrandChild1", - "type": "tutorial", - "uid": -2045443139, - }, - Object { - "beta": false, - "childUIDs": Array [], - "depth": 2, - "id": "Child0_GrandChild0_GreatGrandChild0", - "index": 0, - "parent": -2045443139, - "path": "/documentation/foo/child0/grandchild0/greatgrandchild0", - "siblingsCount": 1, - "title": "Child0_GrandChild0_GreatGrandChild0", - "type": "tutorial", - "uid": 366459535, - }, - Object { - "childUIDs": Array [], - "depth": 1, - "groupMarkerUID": -1893192488, - "id": "Child0_GrandChild2", - "index": 3, - "parent": 365745369, - "path": "/documentation/foo/child0/grandchild2", - "siblingsCount": 4, - "title": "Child0_GrandChild2", - "type": "tutorial", - "uid": -2044519617, - }, - Object { - "childUIDs": Array [ - 1069111095, - ], - "depth": 0, - "groupMarkerUID": -196255993, - "id": "Child1", - "index": 2, - "parent": , - "path": "/documentation/foo/child1/", - "siblingsCount": 3, - "title": "Child1", - "type": "tutorial", - "uid": -1561138820, - }, - Object { - "beta": true, - "childUIDs": Array [], - "depth": 1, - "id": "Child1_GrandChild0", - "index": 0, - "parent": -1561138820, - "path": "/documentation/foo/child1/grandchild0", - "siblingsCount": 1, - "title": "Child1_GrandChild0", - "type": "method", - "uid": 1069111095, - }, - ], - "isFetching": false, - "isFetchingAPIChanges": false, - "references": undefined, - "technology": Object { - "children": Array [ - Object { - "id": "Group Marker", - "title": "Group Marker", - "type": "groupMarker", - }, - Object { - "beta": true, - "children": Array [ - Object { - "id": "Group Marker, Child 0", - "title": "Group Marker, Child 0", - "type": "groupMarker", - }, - Object { - "beta": true, - "id": "Child0_GrandChild0", - "path": "/documentation/foo/child0/grandchild0", - "title": "Child0_GrandChild0", - "type": "tutorial", - }, - Object { - "children": Array [ - Object { - "beta": true, - "id": "Child0_GrandChild0_GreatGrandChild0", - "path": "/documentation/foo/child0/grandchild0/greatgrandchild0", - "title": "Child0_GrandChild0_GreatGrandChild0", - "type": "tutorial", - }, - ], - "id": "Child0_GrandChild1", - "path": "/documentation/foo/child0/grandchild1", - "title": "Child0_GrandChild1", - "type": "tutorial", - }, - Object { - "id": "Child0_GrandChild2", - "path": "/documentation/foo/child0/grandchild2", - "title": "Child0_GrandChild2", - "type": "tutorial", - }, - ], - "id": "Child0", - "path": "/documentation/foo/child0", - "title": "Child0", - "type": "article", - }, - Object { - "children": Array [ - Object { - "beta": true, - "id": "Child1_GrandChild0", - "path": "/documentation/foo/child1/grandchild0", - "title": "Child1_GrandChild0", - "type": "method", - }, - ], - "id": "Child1", - "path": "/documentation/foo/child1/", - "title": "Child1", - "type": "tutorial", - }, - ], - "path": "/documentation/foo", - }, -} -`; diff --git a/tests/unit/mixins/indexProvider.spec.js b/tests/unit/mixins/indexProvider.spec.js index 6a8c30453..052db8abd 100644 --- a/tests/unit/mixins/indexProvider.spec.js +++ b/tests/unit/mixins/indexProvider.spec.js @@ -230,7 +230,7 @@ describe('indexProvider', () => { expect(fetchData).toHaveBeenCalledTimes(1); await flushPromises(); expect(IndexStore.state).toEqual({ - flatChildren: {}, + flatChildren: null, references: {}, apiChanges: null, includedArchiveIdentifiers: [], diff --git a/tests/unit/stores/IndexStore.spec.js b/tests/unit/stores/IndexStore.spec.js index 28679ad98..365af0f2e 100644 --- a/tests/unit/stores/IndexStore.spec.js +++ b/tests/unit/stores/IndexStore.spec.js @@ -11,14 +11,18 @@ import IndexStore from 'docc-render/stores/IndexStore'; import Language from 'docc-render/constants/Language'; -const flatChildren = [ - { - title: 'item 1', - }, - { - title: 'item 2', - }, -]; +const flatChildren = { + [Language.swift.key.url]: [ + { + title: 'item 1', + }, + ], + [Language.objectiveC.key.url]: [ + { + title: 'item 2', + }, + ], +}; const references = { foo: { identifier: 'foo' }, @@ -43,7 +47,7 @@ const technologyProps = { describe('IndexStore', () => { const defaultState = { - flatChildren: {}, + flatChildren: null, references: {}, apiChanges: null, includedArchiveIdentifiers: [],