diff --git a/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue b/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue index 4e580c88ef3760c0f5e2cd180a9064389407ee90..5581863591c257e8c7927b2715321241acc0afb6 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue @@ -130,11 +130,16 @@ export default { }, async fetchExpandedContent() { this.isLoadingExpandedContent = true; + this.error = null; try { await this.fetch(this.fetchExpandedData, FETCH_TYPE_EXPANDED); } catch { this.error = this.errorText; + + // Reset these values so that we allow refetching + this.isExpandedForTheFirstTime = true; + this.isCollapsed = true; } this.isLoadingExpandedContent = false; diff --git a/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js b/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js index 049e5bcce92f9621e652249b411c5032a3801d25..b1ed61faf66fbb6d36039b3925e9d163ec4cbec7 100644 --- a/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js @@ -236,16 +236,17 @@ describe('MR Widget', () => { data: { vulnerabilities: [{ vuln: 2 }] }, }; + const fetchExpandedData = jest.fn().mockResolvedValue(mockDataExpanded); + createComponent({ propsData: { isCollapsible: true, fetchCollapsedData: () => Promise.resolve(mockDataCollapsed), - fetchExpandedData: () => Promise.resolve(mockDataExpanded), + fetchExpandedData, }, }); findToggleButton().vm.$emit('click'); - await waitForPromises(); // First fetches the collapsed data @@ -259,6 +260,62 @@ describe('MR Widget', () => { collapsed: null, expanded: mockDataExpanded.data, }); + + // Triggering a click does not call the expanded data again + findToggleButton().vm.$emit('click'); + await waitForPromises(); + expect(fetchExpandedData).toHaveBeenCalledTimes(1); + }); + + it('allows refetching when fetch expanded data returns an error', async () => { + const fetchExpandedData = jest.fn().mockRejectedValue({ error: true }); + + createComponent({ + propsData: { + isCollapsible: true, + fetchCollapsedData: () => Promise.resolve([]), + fetchExpandedData, + }, + }); + + findToggleButton().vm.$emit('click'); + await waitForPromises(); + + // First fetches the collapsed data + expect(wrapper.emitted('input')[0][0]).toEqual({ + collapsed: undefined, + expanded: null, + }); + + expect(fetchExpandedData).toHaveBeenCalledTimes(1); + expect(wrapper.emitted('input')).toHaveLength(1); // Should not an emit an input call because request failed + + findToggleButton().vm.$emit('click'); + await waitForPromises(); + expect(fetchExpandedData).toHaveBeenCalledTimes(2); + }); + + it('resets the error message when another request is fetched', async () => { + const fetchExpandedData = jest.fn().mockRejectedValue({ error: true }); + + createComponent({ + propsData: { + isCollapsible: true, + fetchCollapsedData: () => Promise.resolve([]), + fetchExpandedData, + }, + }); + + findToggleButton().vm.$emit('click'); + await waitForPromises(); + + expect(wrapper.findByText('Failed to load').exists()).toBe(true); + fetchExpandedData.mockImplementation(() => new Promise(() => {})); + + findToggleButton().vm.$emit('click'); + await nextTick(); + + expect(wrapper.findByText('Failed to load').exists()).toBe(false); }); }); });