diff --git a/app/assets/javascripts/rapid_diffs/adapters.js b/app/assets/javascripts/rapid_diffs/adapters.js index 1911f55bcc49eb558c7a87500f32ee2f6dd4baeb..4f03a2e46b40c8813d1141400e6a1c5c45ee4d4f 100644 --- a/app/assets/javascripts/rapid_diffs/adapters.js +++ b/app/assets/javascripts/rapid_diffs/adapters.js @@ -3,6 +3,7 @@ import { OptionsMenuAdapter } from '~/rapid_diffs/options_menu/adapter'; import { ToggleFileAdapter } from '~/rapid_diffs/toggle_file/adapter'; import { DisableDiffSideAdapter } from '~/rapid_diffs/disable_diff_side/adapter'; import { ImageAdapter } from '~/rapid_diffs/image_viewer/adapter'; +import { LoadFileAdapter } from '~/rapid_diffs/load_file/adapter'; const HEADER_ADAPTERS = [OptionsMenuAdapter, ToggleFileAdapter]; @@ -10,5 +11,5 @@ export const VIEWER_ADAPTERS = { text_inline: [...HEADER_ADAPTERS, ExpandLinesAdapter], text_parallel: [...HEADER_ADAPTERS, ExpandLinesAdapter, DisableDiffSideAdapter], image: [...HEADER_ADAPTERS, ImageAdapter], - no_preview: HEADER_ADAPTERS, + no_preview: [...HEADER_ADAPTERS, LoadFileAdapter], }; diff --git a/app/assets/javascripts/rapid_diffs/diff_file.js b/app/assets/javascripts/rapid_diffs/diff_file.js index 31314f96ed07834ce5a4f250e5b6076783a0e0c5..0658e7e700fbebf39a6a1ea44e2c4ea9fea20c0d 100644 --- a/app/assets/javascripts/rapid_diffs/diff_file.js +++ b/app/assets/javascripts/rapid_diffs/diff_file.js @@ -43,7 +43,7 @@ export class DiffFile extends HTMLElement { disconnectedCallback() { // app might be missing if the file was destroyed before mounting // for example: changing view settings in the middle of the streaming - if (this.app) this.app.unobserve(this); + if (this.app) this.unobserveVisibility(); this.app = undefined; this.diffElement = undefined; this.sink = undefined; @@ -64,6 +64,10 @@ export class DiffFile extends HTMLElement { this.app.observe(this); } + unobserveVisibility() { + this.app.unobserve(this); + } + // Delegated to Rapid Diffs App onVisible(entry) { this.trigger(events.VISIBLE, entry); @@ -98,6 +102,16 @@ export class DiffFile extends HTMLElement { // TODO: add outline for active file } + focusFirstButton(options) { + this.diffElement.querySelector('button').focus(options); + } + + selfReplace(node) { + // 'mount' is automagically called by the component inside the diff file + this.replaceWith(node); + node.focusFirstButton({ focusVisible: false }); + } + get data() { if (!this[dataCacheKey]) this[dataCacheKey] = camelizeKeys(JSON.parse(this.dataset.fileData)); return this[dataCacheKey]; @@ -109,7 +123,8 @@ export class DiffFile extends HTMLElement { diffElement: this.diffElement, sink: this.sink, data: this.data, - trigger: this.trigger, + trigger: this.trigger.bind(this), + replaceWith: this.selfReplace.bind(this), }; } diff --git a/app/assets/javascripts/rapid_diffs/load_file/adapter.js b/app/assets/javascripts/rapid_diffs/load_file/adapter.js new file mode 100644 index 0000000000000000000000000000000000000000..2737cd7e58e7f2d4757a2878f6783b781d4edf21 --- /dev/null +++ b/app/assets/javascripts/rapid_diffs/load_file/adapter.js @@ -0,0 +1,39 @@ +import axios from '~/lib/utils/axios_utils'; +import { useDiffsView } from '~/rapid_diffs/stores/diffs_view'; +import { pinia } from '~/pinia/instance'; +import { createAlert } from '~/alert'; +import { s__ } from '~/locale'; + +function htmlToElement(html) { + const parser = new DOMParser(); + const doc = parser.parseFromString(html, 'text/html'); + return doc.body.firstChild; +} + +export const LoadFileAdapter = { + clicks: { + async showChanges(event, button) { + const { parallelView, showWhitespace } = useDiffsView(pinia); + const url = new URL(this.appData.diffFileEndpoint, window.location.origin); + const { old_path: oldPath, new_path: newPath } = JSON.parse(button.dataset.paths); + if (oldPath) url.searchParams.set('old_path', oldPath); + if (newPath) url.searchParams.set('new_path', newPath); + url.searchParams.set('ignore_whitespace_changes', !showWhitespace); + if (parallelView) url.searchParams.set('view', 'parallel'); + button.setAttribute('disabled', true); + let response; + try { + response = await axios.get(url.toString()); + } catch (error) { + button.removeAttribute('disabled'); + createAlert({ + message: s__('RapidDiffs|Failed to load changes, please try again.'), + error, + }); + return; + } + const node = htmlToElement(response.data); + this.replaceWith(node); + }, + }, +}; diff --git a/app/components/rapid_diffs/app_component.rb b/app/components/rapid_diffs/app_component.rb index e63e8d2d1b7f8516b051ceee8a91fd37690b752a..52aa42066c7250d00e54c45a51d2ccdada34d079 100644 --- a/app/components/rapid_diffs/app_component.rb +++ b/app/components/rapid_diffs/app_component.rb @@ -13,6 +13,7 @@ def initialize( update_user_endpoint:, diffs_stats_endpoint:, diff_files_endpoint:, + diff_file_endpoint:, should_sort_metadata_files: false, lazy: false ) @@ -25,6 +26,7 @@ def initialize( @diffs_stats_endpoint = diffs_stats_endpoint @diff_files_endpoint = diff_files_endpoint @should_sort_metadata_files = should_sort_metadata_files + @diff_file_endpoint = diff_file_endpoint @lazy = lazy end @@ -37,6 +39,7 @@ def app_data should_sort_metadata_files: @should_sort_metadata_files, show_whitespace: @show_whitespace, diff_view_type: @diff_view, + diff_file_endpoint: @diff_file_endpoint, update_user_endpoint: @update_user_endpoint } end diff --git a/app/components/rapid_diffs/viewers/no_preview_component.html.haml b/app/components/rapid_diffs/viewers/no_preview_component.html.haml index 8390d9e18ceb622982f11601af7683e7f35c7f5c..4c3bb96d93cd39dbc433af68049e7e411df71801 100644 --- a/app/components/rapid_diffs/viewers/no_preview_component.html.haml +++ b/app/components/rapid_diffs/viewers/no_preview_component.html.haml @@ -6,12 +6,11 @@ %p.rd-no-preview-paragraph = no_preview_reason .rd-no-preview-actions - - if @diff_file.collapsed? && expandable? || @diff_file.whitespace_only? - - click = @diff_file.whitespace_only? ? 'showWhitespaceChanges' : 'showChanges' - = action_button(button_options: { data: { click: click } }) do + - if @diff_file.collapsed? && expandable? || @diff_file.whitespace_only? && !@diff_file.too_large? + = action_button(button_options: { data: { click: 'showChanges', paths: paths.to_json } }) do = _('Show changes') - elsif expandable? - = action_button(button_options: { data: { click: 'showFileContents' } }) do + = action_button(button_options: { data: { click: 'showChanges', paths: paths.to_json } }) do = _('Show file contents') - elsif @diff_file.content_changed? = action_button(href: old_blob_path) do diff --git a/app/components/rapid_diffs/viewers/no_preview_component.rb b/app/components/rapid_diffs/viewers/no_preview_component.rb index b7ad2bff798d4670f936f91bade369d55c2e9944..081b1dcd404713392cec392db5fc6bf003bc0b29 100644 --- a/app/components/rapid_diffs/viewers/no_preview_component.rb +++ b/app/components/rapid_diffs/viewers/no_preview_component.rb @@ -45,7 +45,7 @@ def no_preview_reason end def expandable? - @diff_file.diffable_text? + @diff_file.diffable_text? && !@diff_file.too_large? end def important? @@ -72,6 +72,13 @@ def action_button(**args, &) end end + def paths + { + old_path: @diff_file.old_path, + new_path: @diff_file.new_path + } + end + def project @diff_file.repository.project end diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index de8e9b0020c628c80b5c671960864dd82ad38750..a5402f94e12eb2b994810e235d47d982a8fccf3b 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -167,6 +167,7 @@ def rapid_diffs @stream_url = diffs_stream_url(@commit, streaming_offset, diff_view) @diffs_slice = @commit.first_diffs_slice(streaming_offset, commit_diff_options) @diff_files_endpoint = diff_files_metadata_namespace_project_commit_path + @diff_file_endpoint = diff_file_namespace_project_commit_path @diffs_stats_endpoint = diffs_stats_namespace_project_commit_path @update_current_user_path = expose_path(api_v4_user_preferences_path) diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb index adb8615c1c21d81f9c09e998519b45ed1ab67a3c..5f97d59cbd5defcbe9a393b2f8b7a18096049544 100644 --- a/app/controllers/projects/compare_controller.rb +++ b/app/controllers/projects/compare_controller.rb @@ -84,6 +84,7 @@ def rapid_diffs @show_whitespace_default = current_user.nil? || current_user.show_whitespace_in_diffs @reload_stream_url = diffs_stream_namespace_project_compare_index_path(**compare_params) @diff_files_endpoint = diff_files_metadata_namespace_project_compare_index_path(**compare_params) + @diff_file_endpoint = diff_file_namespace_project_compare_index_path(**compare_params) @diffs_stats_endpoint = diffs_stats_namespace_project_compare_index_path(**compare_params) @update_current_user_path = expose_path(api_v4_user_preferences_path) diff --git a/app/controllers/projects/merge_requests/creations_controller.rb b/app/controllers/projects/merge_requests/creations_controller.rb index ec68a14001e75765f9a098ebf11bde78cfbdc84d..68098b19d616d89021f233a7bce76d54048bbc30 100644 --- a/app/controllers/projects/merge_requests/creations_controller.rb +++ b/app/controllers/projects/merge_requests/creations_controller.rb @@ -161,6 +161,7 @@ def define_rapid_diffs_vars @stream_url = project_new_merge_request_diffs_stream_path(@project, merge_request: merge_request) @reload_stream_url = project_new_merge_request_diffs_stream_path(@project, merge_request: merge_request) @diff_files_endpoint = project_new_merge_request_diff_files_metadata_path(@project, merge_request: merge_request) + @diff_file_endpoint = project_new_merge_request_diff_file_path(@project, merge_request: merge_request) @diffs_stats_endpoint = project_new_merge_request_diffs_stats_path(@project, merge_request: merge_request) end diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index b81154e288135cd07bdee02c50b7f24c29882763..d02e4719458f923815f7a774a7e853e511854af1 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -113,6 +113,7 @@ def rapid_diffs @stream_url = diffs_stream_url(@merge_request, streaming_offset, diff_view) @diffs_slice = @merge_request.first_diffs_slice(streaming_offset, diff_options) @diff_files_endpoint = diff_files_metadata_namespace_project_merge_request_path + @diff_file_endpoint = diff_file_namespace_project_merge_request_path @diffs_stats_endpoint = diffs_stats_namespace_project_merge_request_path show_merge_request diff --git a/app/views/projects/commit/rapid_diffs.html.haml b/app/views/projects/commit/rapid_diffs.html.haml index 76b0206c4635421fd81d532c888a2fbbaafa400b..9b8445949271fc71f5a0fd75b320b4e8b6a26ab9 100644 --- a/app/views/projects/commit/rapid_diffs.html.haml +++ b/app/views/projects/commit/rapid_diffs.html.haml @@ -13,5 +13,5 @@ .container-fluid{ class: [container_class] } = render "commit_box" = render "ci_menu" - - args = { diffs_slice: @diffs_slice, reload_stream_url: @reload_stream_url, stream_url: @stream_url, show_whitespace: @show_whitespace_default, diff_view: @diff_view, update_user_endpoint: @update_current_user_path, diffs_stats_endpoint: @diffs_stats_endpoint, diff_files_endpoint: @diff_files_endpoint } + - args = { diffs_slice: @diffs_slice, reload_stream_url: @reload_stream_url, stream_url: @stream_url, show_whitespace: @show_whitespace_default, diff_view: @diff_view, update_user_endpoint: @update_current_user_path, diffs_stats_endpoint: @diffs_stats_endpoint, diff_files_endpoint: @diff_files_endpoint, diff_file_endpoint: @diff_file_endpoint } = render ::RapidDiffs::AppComponent.new(**args) diff --git a/app/views/projects/compare/rapid_diffs.html.haml b/app/views/projects/compare/rapid_diffs.html.haml index f9558da164e1b0ce38e414320015c71356c6ed9f..ebbe1c2f7577da3777275a875311f14a8405be96 100644 --- a/app/views/projects/compare/rapid_diffs.html.haml +++ b/app/views/projects/compare/rapid_diffs.html.haml @@ -17,7 +17,7 @@ .container-fluid{ class: [container_class] } = render "projects/commits/commit_list" unless hide_commit_list .container-fluid - - args = { diffs_slice: nil, reload_stream_url: @reload_stream_url, stream_url: nil, show_whitespace: @show_whitespace_default, diff_view: diff_view, diffs_stats_endpoint: @diffs_stats_endpoint, update_user_endpoint: @update_current_user_path, diff_files_endpoint: @diff_files_endpoint, lazy: true } + - args = { diffs_slice: nil, reload_stream_url: @reload_stream_url, stream_url: nil, show_whitespace: @show_whitespace_default, diff_view: diff_view, diffs_stats_endpoint: @diffs_stats_endpoint, update_user_endpoint: @update_current_user_path, diff_files_endpoint: @diff_files_endpoint, diff_file_endpoint: @diff_file_endpoint, lazy: true } = render ::RapidDiffs::AppComponent.new(**args) - else .container-fluid diff --git a/app/views/projects/merge_requests/creations/rapid_diffs.html.haml b/app/views/projects/merge_requests/creations/rapid_diffs.html.haml index bfe466641f8bd2662fbc02b93e57b59295ae2807..caf82d8feca1d82540544aae1ab105dfaf2f5e44 100644 --- a/app/views/projects/merge_requests/creations/rapid_diffs.html.haml +++ b/app/views/projects/merge_requests/creations/rapid_diffs.html.haml @@ -3,5 +3,5 @@ - add_page_specific_style 'page_bundles/merge_request_creation_rapid_diffs' = render "page" do - - args = { diffs_slice: nil, reload_stream_url: @reload_stream_url, stream_url: @stream_url, show_whitespace: @show_whitespace_default, diff_view: diff_view, update_user_endpoint: expose_path(api_v4_user_preferences_path), diffs_stats_endpoint: @diffs_stats_endpoint, diff_files_endpoint: @diff_files_endpoint, lazy: true } + - args = { diffs_slice: nil, reload_stream_url: @reload_stream_url, stream_url: @stream_url, show_whitespace: @show_whitespace_default, diff_view: diff_view, update_user_endpoint: expose_path(api_v4_user_preferences_path), diffs_stats_endpoint: @diffs_stats_endpoint, diff_files_endpoint: @diff_files_endpoint, diff_file_endpoint: @diff_file_endpoint, lazy: true } = render ::RapidDiffs::AppComponent.new(**args) diff --git a/app/views/projects/merge_requests/rapid_diffs.html.haml b/app/views/projects/merge_requests/rapid_diffs.html.haml index 792252e7a4e5e9be930f35b1f39c8ab50f9814da..e9cdd8f6a2dbc9ce3768a7ab72f6fe540d1a05c1 100644 --- a/app/views/projects/merge_requests/rapid_diffs.html.haml +++ b/app/views/projects/merge_requests/rapid_diffs.html.haml @@ -3,7 +3,7 @@ - @content_class = 'rd-page-container diffs-container-limited' = render 'page' -- args = { diffs_slice: @diffs_slice, reload_stream_url: @reload_stream_url, stream_url: @stream_url, show_whitespace: @show_whitespace_default, diff_view: @diff_view, update_user_endpoint: @update_current_user_path, diffs_stats_endpoint: @diffs_stats_endpoint, diff_files_endpoint: @diff_files_endpoint, should_sort_metadata_files: true } +- args = { diffs_slice: @diffs_slice, reload_stream_url: @reload_stream_url, stream_url: @stream_url, show_whitespace: @show_whitespace_default, diff_view: @diff_view, update_user_endpoint: @update_current_user_path, diffs_stats_endpoint: @diffs_stats_endpoint, diff_files_endpoint: @diff_files_endpoint, should_sort_metadata_files: true, diff_file_endpoint: @diff_file_endpoint } = render ::RapidDiffs::AppComponent.new(**args) do |c| - c.with_diffs_list do = render RapidDiffs::MergeRequestDiffFileComponent.with_collection(@diffs_slice, merge_request: @merge_request, parallel_view: @diff_view == :parallel) diff --git a/locale/gitlab.pot b/locale/gitlab.pot index ea98e2cfb056bbb12c203f0ec95abeb53beee262..70e96e5961292fe30fb26d7099cde4a13056c07a 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -50271,6 +50271,9 @@ msgstr "" msgid "RapidDiffs|Failed to expand lines, please try again." msgstr "" +msgid "RapidDiffs|Failed to load changes, please try again." +msgstr "" + msgid "RapidDiffs|File moved from %{old} to %{new}" msgstr "" diff --git a/spec/components/rapid_diffs/app_component_spec.rb b/spec/components/rapid_diffs/app_component_spec.rb index 21868651e2dfed34b935c3da8d94df741c6a1123..1c3f80d6ef4542a9a6ac4bdc1c1f5773ad0dfba4 100644 --- a/spec/components/rapid_diffs/app_component_spec.rb +++ b/spec/components/rapid_diffs/app_component_spec.rb @@ -11,6 +11,7 @@ let(:update_user_endpoint) { '/update_user' } let(:diffs_stats_endpoint) { '/diffs_stats' } let(:diff_files_endpoint) { '/diff_files_metadata' } + let(:diff_file_endpoint) { '/diff_file' } let(:should_sort_metadata_files) { false } let(:lazy) { false } @@ -35,6 +36,7 @@ expect(data['diff_view_type']).to eq(diff_view) expect(data['update_user_endpoint']).to eq(update_user_endpoint) expect(data['diffs_stream_url']).to eq(stream_url) + expect(data['diff_file_endpoint']).to eq(diff_file_endpoint) end context "with should_sort_metadata_files set to true" do @@ -150,6 +152,7 @@ def create_instance update_user_endpoint:, diffs_stats_endpoint:, diff_files_endpoint:, + diff_file_endpoint:, should_sort_metadata_files:, lazy: ) diff --git a/spec/components/rapid_diffs/viewers/no_preview_component_spec.rb b/spec/components/rapid_diffs/viewers/no_preview_component_spec.rb index 43cb22f05fd974eabfc35a0378bb67a9e6cbb3c3..74f355e334a653903c50a8f5a7ae883336cfd655 100644 --- a/spec/components/rapid_diffs/viewers/no_preview_component_spec.rb +++ b/spec/components/rapid_diffs/viewers/no_preview_component_spec.rb @@ -167,15 +167,27 @@ expect(page).to have_button("Show file contents") end - context 'when diff is too large' do + context 'when diff is collapsed' do before do - allow(diff_file).to receive(:collapsed?).and_return(true) + allow(diff_file).to receive_messages(collapsed?: true, too_large?: false) end it 'shows preview button' do render_component expect(page).to have_button("Show changes") end + + context 'when diff is too large' do + before do + allow(diff_file).to receive_messages(too_large?: true) + end + + it 'shows preview button' do + render_component + expect(page).to have_link("View original file") + expect(page).to have_link("View changed file") + end + end end context 'when diff is whitespace only' do diff --git a/spec/frontend/rapid_diffs/diff_file_spec.js b/spec/frontend/rapid_diffs/diff_file_spec.js index 0afb5eefbfb225b939ce131a2f9b4e2a5f86fb8f..51eb549d15b4a712ccb5445f31114c7981bb7c5f 100644 --- a/spec/frontend/rapid_diffs/diff_file_spec.js +++ b/spec/frontend/rapid_diffs/diff_file_spec.js @@ -11,7 +11,7 @@ describe('DiffFile Web Component', () => { `; let app; - let defaultAdapter; + let adapter; const getDiffElement = () => document.querySelector('[id=foo]'); const getWebComponentElement = () => document.querySelector('diff-file'); @@ -22,13 +22,20 @@ describe('DiffFile Web Component', () => { isIntersecting ? target.onVisible({}) : target.onInvisible({}); }; - const createDefaultAdapter = (customAdapter) => { - defaultAdapter = customAdapter; - }; + const createDefaultAdapter = () => ({ + click: jest.fn(), + clicks: { + foo: jest.fn(), + }, + visible: jest.fn(), + invisible: jest.fn(), + mounted: jest.fn(), + }); - const initRapidDiffsApp = (adapterConfig = { current: [defaultAdapter] }, appData = {}) => { + const initRapidDiffsApp = (currentAdapter = createDefaultAdapter(), appData = {}) => { + adapter = currentAdapter; app = { - adapterConfig, + adapterConfig: { current: [currentAdapter] }, appData, observe: jest.fn(), unobserve: jest.fn(), @@ -48,7 +55,8 @@ describe('DiffFile Web Component', () => { getWebComponentElement().onClick(event); }; - const mount = () => { + const mount = (customAdapter) => { + initRapidDiffsApp(customAdapter); document.body.innerHTML = html; getWebComponentElement().mount(app); }; @@ -61,26 +69,14 @@ describe('DiffFile Web Component', () => { viewer: 'current', }, sink: {}, - trigger: getWebComponentElement().trigger, + trigger: expect.any(Function), + replaceWith: expect.any(Function), }); beforeAll(() => { customElements.define('diff-file', DiffFile); }); - beforeEach(() => { - createDefaultAdapter({ - click: jest.fn(), - clicks: { - foo: jest.fn(), - }, - visible: jest.fn(), - invisible: jest.fn(), - mounted: jest.fn(), - }); - initRapidDiffsApp(); - }); - it('observes diff element', () => { mount(); expect(app.observe).toHaveBeenCalledWith(getWebComponentElement()); @@ -92,8 +88,8 @@ describe('DiffFile Web Component', () => { emitted = true; }); mount(); - expect(defaultAdapter.mounted).toHaveBeenCalled(); - expect(defaultAdapter.mounted.mock.instances[0]).toStrictEqual(getContext()); + expect(adapter.mounted).toHaveBeenCalled(); + expect(adapter.mounted.mock.instances[0]).toStrictEqual(getContext()); expect(emitted).toBe(true); }); @@ -104,6 +100,17 @@ describe('DiffFile Web Component', () => { expect(app.unobserve).toHaveBeenCalledWith(element); }); + it('can self replace', () => { + const focusFirstButton = jest.fn(); + const mockNode = { focusFirstButton }; + mount({ + mounted() { + this.replaceWith(mockNode); + }, + }); + expect(focusFirstButton).toHaveBeenCalled(); + }); + it('#selectFile', () => { mount(); const spy = jest.spyOn(getWebComponentElement(), 'scrollIntoView'); @@ -119,28 +126,28 @@ describe('DiffFile Web Component', () => { it('handles all clicks', () => { triggerVisibility(true); delegatedClick(getDiffElement()); - expect(defaultAdapter.click).toHaveBeenCalledWith(expect.any(MouseEvent)); - expect(defaultAdapter.click.mock.instances[0]).toStrictEqual(getContext()); + expect(adapter.click).toHaveBeenCalledWith(expect.any(MouseEvent)); + expect(adapter.click.mock.instances[0]).toStrictEqual(getContext()); }); it('handles specific clicks', () => { triggerVisibility(true); const clickTarget = getDiffElement().querySelector('[data-click=foo]'); delegatedClick(clickTarget); - expect(defaultAdapter.clicks.foo).toHaveBeenCalledWith(expect.any(MouseEvent), clickTarget); - expect(defaultAdapter.clicks.foo.mock.instances[0]).toStrictEqual(getContext()); + expect(adapter.clicks.foo).toHaveBeenCalledWith(expect.any(MouseEvent), clickTarget); + expect(adapter.clicks.foo.mock.instances[0]).toStrictEqual(getContext()); }); it('handles visible event', () => { triggerVisibility(true); - expect(defaultAdapter.visible).toHaveBeenCalled(); - expect(defaultAdapter.visible.mock.instances[0]).toStrictEqual(getContext()); + expect(adapter.visible).toHaveBeenCalled(); + expect(adapter.visible.mock.instances[0]).toStrictEqual(getContext()); }); it('handles invisible event', () => { triggerVisibility(false); - expect(defaultAdapter.invisible).toHaveBeenCalled(); - expect(defaultAdapter.invisible.mock.instances[0]).toStrictEqual(getContext()); + expect(adapter.invisible).toHaveBeenCalled(); + expect(adapter.invisible.mock.instances[0]).toStrictEqual(getContext()); }); }); diff --git a/spec/frontend/rapid_diffs/load_file/adapter_spec.js b/spec/frontend/rapid_diffs/load_file/adapter_spec.js new file mode 100644 index 0000000000000000000000000000000000000000..9112975643f8255d25be91e1d559b5b6b163b1cd --- /dev/null +++ b/spec/frontend/rapid_diffs/load_file/adapter_spec.js @@ -0,0 +1,108 @@ +import MockAxiosAdapter from 'axios-mock-adapter'; +import axios from '~/lib/utils/axios_utils'; +import { DiffFile } from '~/rapid_diffs/diff_file'; +import { setHTMLFixture } from 'helpers/fixtures'; +import { LoadFileAdapter } from '~/rapid_diffs/load_file/adapter'; +import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status'; +import { TEST_HOST } from 'spec/test_constants'; +import { useDiffsView } from '~/rapid_diffs/stores/diffs_view'; +import { pinia } from '~/pinia/instance'; +import waitForPromises from 'helpers/wait_for_promises'; +import { createAlert } from '~/alert'; + +jest.mock('~/alert'); + +describe('LoadFileAdapter', () => { + const viewer = 'any'; + const diffFileEndpoint = '/diff-file'; + + let mockAdapter; + + const getDiffFile = () => document.querySelector('diff-file'); + const getButton = () => document.querySelector('button[data-click="showChanges"]'); + const getExpandedContent = () => document.querySelector('#expanded'); + const getRequestUrl = () => + `${TEST_HOST}${diffFileEndpoint}?old_path=foo&new_path=bar&ignore_whitespace_changes=${!useDiffsView(pinia).showWhitespace}`; + + const mountComponent = (component = getDiffFile()) => { + component.mount({ + adapterConfig: { [viewer]: [LoadFileAdapter] }, + appData: { diffFileEndpoint }, + unobserve: jest.fn(), + }); + }; + + const createComponentHtml = (name, content) => ` + <${name} data-file-data='${JSON.stringify({ viewer })}'> + ${content} + + `; + + const mount = () => { + setHTMLFixture( + createComponentHtml( + 'diff-file', + ``, + ), + ); + mountComponent(); + }; + + const delegatedClick = (element) => { + let event; + element.addEventListener( + 'click', + (e) => { + event = e; + }, + { once: true }, + ); + element.click(); + getDiffFile().onClick(event); + }; + + beforeAll(() => { + customElements.define('diff-file', DiffFile); + customElements.define( + 'new-diff-file', + class extends DiffFile { + constructor(...args) { + super(...args); + mountComponent(this); + } + }, + ); + }); + + beforeEach(() => { + mockAdapter = new MockAxiosAdapter(axios); + }); + + it.each([true, false])('expands file with hide whitespace %s', async (whitespace) => { + useDiffsView(pinia).showWhitespace = whitespace; + mockAdapter + .onGet(getRequestUrl()) + .reply( + HTTP_STATUS_OK, + createComponentHtml( + 'new-diff-file', + '
Expanded Content
', + ), + ); + mount(); + delegatedClick(getButton()); + expect(getButton().disabled).toBe(true); + await waitForPromises(); + expect(getExpandedContent()).not.toBeFalsy(); + }); + + it('handles error', async () => { + mockAdapter.onGet(getRequestUrl()).reply(HTTP_STATUS_INTERNAL_SERVER_ERROR); + mount(); + delegatedClick(getButton()); + expect(getButton().disabled).toBe(true); + await waitForPromises(); + expect(getButton().disabled).toBe(false); + expect(createAlert).toHaveBeenCalled(); + }); +});