diff options
29 files changed, 242 insertions, 135 deletions
diff --git a/app/assets/javascripts/diffs/components/diff_content.vue b/app/assets/javascripts/diffs/components/diff_content.vue index 6dc2f5d3f68..cb92093db32 100644 --- a/app/assets/javascripts/diffs/components/diff_content.vue +++ b/app/assets/javascripts/diffs/components/diff_content.vue @@ -1,7 +1,8 @@ <script> import { mapActions, mapGetters, mapState } from 'vuex'; import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue'; -import EmptyFileViewer from '~/vue_shared/components/diff_viewer/viewers/empty_file.vue'; +import NotDiffableViewer from '~/vue_shared/components/diff_viewer/viewers/not_diffable.vue'; +import NoPreviewViewer from '~/vue_shared/components/diff_viewer/viewers/no_preview.vue'; import InlineDiffView from './inline_diff_view.vue'; import ParallelDiffView from './parallel_diff_view.vue'; import NoteForm from '../../notes/components/note_form.vue'; @@ -9,6 +10,7 @@ import ImageDiffOverlay from './image_diff_overlay.vue'; import DiffDiscussions from './diff_discussions.vue'; import { IMAGE_DIFF_POSITION_TYPE } from '../constants'; import { getDiffMode } from '../store/utils'; +import { diffViewerModes } from '~/ide/constants'; export default { components: { @@ -18,7 +20,8 @@ export default { NoteForm, DiffDiscussions, ImageDiffOverlay, - EmptyFileViewer, + NotDiffableViewer, + NoPreviewViewer, }, props: { diffFile: { @@ -42,11 +45,17 @@ export default { diffMode() { return getDiffMode(this.diffFile); }, + diffViewerMode() { + return this.diffFile.viewer.name; + }, isTextFile() { - return this.diffFile.viewer.name === 'text'; + return this.diffViewerMode === diffViewerModes.text; + }, + noPreview() { + return this.diffViewerMode === diffViewerModes.no_preview; }, - errorMessage() { - return this.diffFile.viewer.error; + notDiffable() { + return this.diffViewerMode === diffViewerModes.not_diffable; }, diffFileCommentForm() { return this.getCommentFormForDiffFile(this.diffFile.file_hash); @@ -78,11 +87,10 @@ export default { <template> <div class="diff-content"> - <div v-if="!errorMessage" class="diff-viewer"> + <div class="diff-viewer"> <template v-if="isTextFile"> - <empty-file-viewer v-if="diffFile.empty" /> <inline-diff-view - v-else-if="isInlineView" + v-if="isInlineView" :diff-file="diffFile" :diff-lines="diffFile.highlighted_diff_lines || []" :help-page-path="helpPagePath" @@ -94,9 +102,12 @@ export default { :help-page-path="helpPagePath" /> </template> + <not-diffable-viewer v-else-if="notDiffable" /> + <no-preview-viewer v-else-if="noPreview" /> <diff-viewer v-else :diff-mode="diffMode" + :diff-viewer-mode="diffViewerMode" :new-path="diffFile.new_path" :new-sha="diffFile.diff_refs.head_sha" :old-path="diffFile.old_path" @@ -132,8 +143,5 @@ export default { </div> </diff-viewer> </div> - <div v-else class="diff-viewer"> - <div class="nothing-here-block" v-html="errorMessage"></div> - </div> </div> </template> diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue index 449f7007077..347a35b9c54 100644 --- a/app/assets/javascripts/diffs/components/diff_file.vue +++ b/app/assets/javascripts/diffs/components/diff_file.vue @@ -7,6 +7,7 @@ import { GlLoadingIcon } from '@gitlab/ui'; import eventHub from '../../notes/event_hub'; import DiffFileHeader from './diff_file_header.vue'; import DiffContent from './diff_content.vue'; +import { diffViewerErrors } from '~/ide/constants'; export default { components: { @@ -33,15 +34,14 @@ export default { return { isLoadingCollapsedDiff: false, forkMessageVisible: false, + isCollapsed: this.file.viewer.collapsed || false, + renderIt: this.file.renderIt, }; }, computed: { ...mapState('diffs', ['currentDiffFileId']), ...mapGetters(['isNotesFetched']), ...mapGetters('diffs', ['getDiffFileDiscussions']), - isCollapsed() { - return this.file.collapsed || false; - }, viewBlobLink() { return sprintf( __('You can %{linkStart}view the blob%{linkEnd} instead.'), @@ -52,19 +52,8 @@ export default { false, ); }, - showExpandMessage() { - return ( - this.isCollapsed || - (!this.file.highlighted_diff_lines && - !this.isLoadingCollapsedDiff && - !this.file.too_large && - this.file.text && - !this.file.renamed_file && - !this.file.mode_changed) - ); - }, showLoadingIcon() { - return this.isLoadingCollapsedDiff || (!this.file.renderIt && !this.isCollapsed); + return this.isLoadingCollapsedDiff || (!this.renderIt && !this.isCollapsed); }, hasDiffLines() { return ( @@ -73,9 +62,15 @@ export default { this.file.parallel_diff_lines.length > 0 ); }, + isFileTooLarge() { + return this.file.viewer.error === diffViewerErrors.too_large; + }, + errorMessage() { + return this.file.viewer.error_message; + }, }, watch: { - 'file.collapsed': function fileCollapsedWatch(newVal, oldVal) { + isCollapsed: function fileCollapsedWatch(newVal, oldVal) { if (!newVal && oldVal && !this.hasDiffLines) { this.handleLoadCollapsedDiff(); } @@ -90,8 +85,8 @@ export default { if (!this.hasDiffLines) { this.handleLoadCollapsedDiff(); } else { - this.file.collapsed = !this.file.collapsed; - this.file.renderIt = true; + this.isCollapsed = !this.isCollapsed; + this.renderIt = true; } }, handleLoadCollapsedDiff() { @@ -100,8 +95,8 @@ export default { this.loadCollapsedDiff(this.file) .then(() => { this.isLoadingCollapsedDiff = false; - this.file.collapsed = false; - this.file.renderIt = true; + this.isCollapsed = false; + this.renderIt = true; }) .then(() => { requestIdleCallback( @@ -164,21 +159,25 @@ export default { Cancel </button> </div> - - <diff-content - v-if="!isCollapsed && file.renderIt" - :class="{ hidden: isCollapsed || file.too_large }" - :diff-file="file" - :help-page-path="helpPagePath" - /> <gl-loading-icon v-if="showLoadingIcon" class="diff-content loading" /> - <div v-else-if="showExpandMessage" class="nothing-here-block diff-collapsed"> - {{ __('This diff is collapsed.') }} - <a class="click-to-expand js-click-to-expand" href="#" @click.prevent="handleToggle">{{ - __('Click to expand it.') - }}</a> - </div> - <div v-if="file.too_large" class="nothing-here-block diff-collapsed js-too-large-diff"> + <template v-else> + <div v-if="errorMessage" class="diff-viewer"> + <div class="nothing-here-block" v-html="errorMessage"></div> + </div> + <div v-else-if="isCollapsed" class="nothing-here-block diff-collapsed"> + {{ __('This diff is collapsed.') }} + <a class="click-to-expand js-click-to-expand" href="#" @click.prevent="handleToggle">{{ + __('Click to expand it.') + }}</a> + </div> + <diff-content + v-else + :class="{ hidden: isCollapsed || isFileTooLarge }" + :diff-file="file" + :help-page-path="helpPagePath" + /> + </template> + <div v-if="isFileTooLarge" class="nothing-here-block diff-collapsed js-too-large-diff"> {{ __('This source diff could not be displayed because it is too large.') }} <span v-html="viewBlobLink"></span> </div> diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue index 60586d4a607..2b801898345 100644 --- a/app/assets/javascripts/diffs/components/diff_file_header.vue +++ b/app/assets/javascripts/diffs/components/diff_file_header.vue @@ -8,6 +8,7 @@ import FileIcon from '~/vue_shared/components/file_icon.vue'; import { GlTooltipDirective } from '@gitlab/ui'; import { truncateSha } from '~/lib/utils/text_utility'; import { __, s__, sprintf } from '~/locale'; +import { diffViewerModes } from '~/ide/constants'; import EditButton from './edit_button.vue'; import DiffStats from './diff_stats.vue'; @@ -118,6 +119,12 @@ export default { gfmCopyText() { return `\`${this.diffFile.file_path}\``; }, + isFileRenamed() { + return this.diffFile.viewer.name === diffViewerModes.renamed; + }, + isModeChanged() { + return this.diffFile.viewer.name === diffViewerModes.mode_changed; + }, }, mounted() { polyfillSticky(this.$refs.header); @@ -165,7 +172,7 @@ export default { aria-hidden="true" css-classes="js-file-icon append-right-5" /> - <span v-if="diffFile.renamed_file"> + <span v-if="isFileRenamed"> <strong v-gl-tooltip :title="diffFile.old_path" @@ -193,7 +200,7 @@ export default { css-class="btn-default btn-transparent btn-clipboard" /> - <small v-if="diffFile.mode_changed" ref="fileMode"> + <small v-if="isModeChanged" ref="fileMode"> {{ diffFile.a_mode }} → {{ diffFile.b_mode }} </small> diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js index 7fb66ce433b..feda882e826 100644 --- a/app/assets/javascripts/diffs/store/actions.js +++ b/app/assets/javascripts/diffs/store/actions.js @@ -17,6 +17,7 @@ import { TREE_LIST_STORAGE_KEY, WHITESPACE_STORAGE_KEY, } from '../constants'; +import { diffViewerModes } from '~/ide/constants'; export const setBaseConfig = ({ commit }, options) => { const { endpoint, projectPath } = options; @@ -91,7 +92,7 @@ export const renderFileForDiscussionId = ({ commit, rootState, state }, discussi commit(types.RENDER_FILE, file); } - if (file.collapsed) { + if (file.viewer.collapsed) { eventHub.$emit(`loadCollapsedDiff/${file.file_hash}`); scrollToElement(document.getElementById(file.file_hash)); } else { @@ -105,7 +106,8 @@ export const startRenderDiffsQueue = ({ state, commit }) => { const checkItem = () => new Promise(resolve => { const nextFile = state.diffFiles.find( - file => !file.renderIt && (!file.collapsed || !file.text), + file => + !file.renderIt && (!file.viewer.collapsed || !file.viewer.name === diffViewerModes.text), ); if (nextFile) { diff --git a/app/assets/javascripts/diffs/store/getters.js b/app/assets/javascripts/diffs/store/getters.js index 0e1ad654a2b..4e7e5306995 100644 --- a/app/assets/javascripts/diffs/store/getters.js +++ b/app/assets/javascripts/diffs/store/getters.js @@ -4,7 +4,8 @@ export const isParallelView = state => state.diffViewType === PARALLEL_DIFF_VIEW export const isInlineView = state => state.diffViewType === INLINE_DIFF_VIEW_TYPE; -export const hasCollapsedFile = state => state.diffFiles.some(file => file.collapsed); +export const hasCollapsedFile = state => + state.diffFiles.some(file => file.viewer && file.viewer.collapsed); export const commitId = state => (state.commit && state.commit.id ? state.commit.id : null); diff --git a/app/assets/javascripts/diffs/store/utils.js b/app/assets/javascripts/diffs/store/utils.js index 062024b8cdd..247d1e65fea 100644 --- a/app/assets/javascripts/diffs/store/utils.js +++ b/app/assets/javascripts/diffs/store/utils.js @@ -1,6 +1,6 @@ import _ from 'underscore'; -import { diffModes } from '~/ide/constants'; import { truncatePathMiddleToLength } from '~/lib/utils/text_utility'; +import { diffModes, diffViewerModes } from '~/ide/constants'; import { LINE_POSITION_LEFT, LINE_POSITION_RIGHT, @@ -248,7 +248,8 @@ export function prepareDiffData(diffData) { Object.assign(file, { renderIt: showingLines < LINES_TO_BE_RENDERED_DIRECTLY, - collapsed: file.text && showingLines > MAX_LINES_TO_BE_RENDERED, + collapsed: + file.viewer.name === diffViewerModes.text && showingLines > MAX_LINES_TO_BE_RENDERED, discussions: [], }); } @@ -404,7 +405,9 @@ export const getDiffMode = diffFile => { const diffModeKey = Object.keys(diffModes).find(key => diffFile[`${key}_file`]); return ( diffModes[diffModeKey] || - (diffFile.mode_changed && diffModes.mode_changed) || + (diffFile.viewer && + diffFile.viewer.name === diffViewerModes.mode_changed && + diffViewerModes.mode_changed) || diffModes.replaced ); }; diff --git a/app/assets/javascripts/ide/constants.js b/app/assets/javascripts/ide/constants.js index 804ebae4555..7c560c89695 100644 --- a/app/assets/javascripts/ide/constants.js +++ b/app/assets/javascripts/ide/constants.js @@ -24,6 +24,22 @@ export const diffModes = { mode_changed: 'mode_changed', }; +export const diffViewerModes = Object.freeze({ + not_diffable: 'not_diffable', + no_preview: 'no_preview', + added: 'added', + deleted: 'deleted', + renamed: 'renamed', + mode_changed: 'mode_changed', + text: 'text', + image: 'image', +}); + +export const diffViewerErrors = Object.freeze({ + too_large: 'too_large', + stored_externally: 'server_side_but_stored_externally', +}); + export const rightSidebarViews = { pipelines: { name: 'pipelines-list', keepAlive: true }, jobsDetail: { name: 'jobs-detail', keepAlive: false }, diff --git a/app/assets/javascripts/notes/components/diff_with_note.vue b/app/assets/javascripts/notes/components/diff_with_note.vue index 376d4114efd..d8947e8ca50 100644 --- a/app/assets/javascripts/notes/components/diff_with_note.vue +++ b/app/assets/javascripts/notes/components/diff_with_note.vue @@ -5,6 +5,7 @@ import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue'; import ImageDiffOverlay from '~/diffs/components/image_diff_overlay.vue'; import { GlSkeletonLoading } from '@gitlab/ui'; import { getDiffMode } from '~/diffs/store/utils'; +import { diffViewerModes } from '~/ide/constants'; export default { components: { @@ -31,6 +32,12 @@ export default { diffMode() { return getDiffMode(this.discussion.diff_file); }, + diffViewerMode() { + return this.discussion.diff_file.viewer.name; + }, + isTextFile() { + return this.diffViewerMode === diffViewerModes.text; + }, hasTruncatedDiffLines() { return ( this.discussion.truncated_diff_lines && this.discussion.truncated_diff_lines.length !== 0 @@ -58,18 +65,14 @@ export default { </script> <template> - <div :class="{ 'text-file': discussion.diff_file.text }" class="diff-file file-holder"> + <div :class="{ 'text-file': isTextFile }" class="diff-file file-holder"> <diff-file-header :discussion-path="discussion.discussion_path" :diff-file="discussion.diff_file" :can-current-user-fork="false" - :expanded="!discussion.diff_file.collapsed" + :expanded="!discussion.diff_file.viewer.collapsed" /> - <div - v-if="discussion.diff_file.text" - :class="$options.userColorSchemeClass" - class="diff-content code" - > + <div v-if="isTextFile" :class="$options.userColorSchemeClass" class="diff-content code"> <table> <template v-if="hasTruncatedDiffLines"> <tr @@ -109,6 +112,7 @@ export default { <div v-else> <diff-viewer :diff-mode="diffMode" + :diff-viewer-mode="diffViewerMode" :new-path="discussion.diff_file.new_path" :new-sha="discussion.diff_file.diff_refs.head_sha" :old-path="discussion.diff_file.old_path" diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue index 75c66ed850b..ebb253ff422 100644 --- a/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue @@ -1,6 +1,5 @@ <script> -import { diffModes } from '~/ide/constants'; -import { viewerInformationForPath } from '../content_viewer/lib/viewer_utils'; +import { diffViewerModes, diffModes } from '~/ide/constants'; import ImageDiffViewer from './viewers/image_diff_viewer.vue'; import DownloadDiffViewer from './viewers/download_diff_viewer.vue'; import RenamedFile from './viewers/renamed.vue'; @@ -12,6 +11,10 @@ export default { type: String, required: true, }, + diffViewerMode: { + type: String, + required: true, + }, newPath: { type: String, required: true, @@ -46,7 +49,7 @@ export default { }, computed: { viewer() { - if (this.diffMode === diffModes.renamed) { + if (this.diffViewerMode === diffViewerModes.renamed) { return RenamedFile; } else if (this.diffMode === diffModes.mode_changed) { return ModeChanged; @@ -54,11 +57,8 @@ export default { if (!this.newPath) return null; - const previewInfo = viewerInformationForPath(this.newPath); - if (!previewInfo) return DownloadDiffViewer; - - switch (previewInfo.id) { - case 'image': + switch (this.diffViewerMode) { + case diffViewerModes.image: return ImageDiffViewer; default: return DownloadDiffViewer; diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/no_preview.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/no_preview.vue new file mode 100644 index 00000000000..c5cdddf2f64 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/no_preview.vue @@ -0,0 +1,5 @@ +<template> + <div class="nothing-here-block"> + {{ __('No preview for this file type') }} + </div> +</template> diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/not_diffable.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/not_diffable.vue new file mode 100644 index 00000000000..d4d3038f066 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/not_diffable.vue @@ -0,0 +1,5 @@ +<template> + <div class="nothing-here-block"> + {{ __('This diff was suppressed by a .gitattributes entry.') }} + </div> +</template> diff --git a/app/serializers/diff_file_base_entity.rb b/app/serializers/diff_file_base_entity.rb index 06a8db78476..ede9e04b722 100644 --- a/app/serializers/diff_file_base_entity.rb +++ b/app/serializers/diff_file_base_entity.rb @@ -72,17 +72,20 @@ class DiffFileBaseEntity < Grape::Entity expose :old_path expose :new_path expose :new_file?, as: :new_file - expose :collapsed?, as: :collapsed - expose :text?, as: :text + expose :renamed_file?, as: :renamed_file + expose :deleted_file?, as: :deleted_file + expose :diff_refs + expose :stored_externally?, as: :stored_externally expose :external_storage - expose :renamed_file?, as: :renamed_file - expose :deleted_file?, as: :deleted_file + expose :mode_changed?, as: :mode_changed expose :a_mode expose :b_mode + expose :viewer, using: DiffViewerEntity + private def memoized_submodule_links(diff_file) diff --git a/app/serializers/diff_file_entity.rb b/app/serializers/diff_file_entity.rb index b0aaec3326d..01ee7af37ed 100644 --- a/app/serializers/diff_file_entity.rb +++ b/app/serializers/diff_file_entity.rb @@ -4,12 +4,10 @@ class DiffFileEntity < DiffFileBaseEntity include CommitsHelper include IconsHelper - expose :too_large?, as: :too_large - expose :empty?, as: :empty expose :added_lines expose :removed_lines - expose :load_collapsed_diff_url, if: -> (diff_file, options) { diff_file.text? && options[:merge_request] } do |diff_file| + expose :load_collapsed_diff_url, if: -> (diff_file, options) { diff_file.viewer.collapsed? && options[:merge_request] } do |diff_file| merge_request = options[:merge_request] project = merge_request.target_project @@ -36,10 +34,6 @@ class DiffFileEntity < DiffFileBaseEntity project_blob_path(project, tree_join(diff_file.content_sha, diff_file.new_path)) end - expose :viewer, using: DiffViewerEntity do |diff_file| - diff_file.rich_viewer || diff_file.simple_viewer - end - expose :replaced_view_path, if: -> (_, options) { options[:merge_request] } do |diff_file| image_diff = diff_file.rich_viewer && diff_file.rich_viewer.partial_name == 'image' image_replaced = diff_file.old_content_sha && diff_file.old_content_sha != diff_file.content_sha diff --git a/app/serializers/diff_viewer_entity.rb b/app/serializers/diff_viewer_entity.rb index 587fa2347fd..45faca6cb2f 100644 --- a/app/serializers/diff_viewer_entity.rb +++ b/app/serializers/diff_viewer_entity.rb @@ -1,10 +1,8 @@ # frozen_string_literal: true class DiffViewerEntity < Grape::Entity - # Partial name refers directly to a Rails feature, let's avoid - # using this on the frontend. expose :partial_name, as: :name - expose :error do |diff_viewer| - diff_viewer.render_error_message - end + expose :render_error, as: :error + expose :render_error_message, as: :error_message + expose :collapsed?, as: :collapsed end diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb index e410d5a8333..c9d89d56884 100644 --- a/lib/gitlab/diff/file.rb +++ b/lib/gitlab/diff/file.rb @@ -293,6 +293,10 @@ module Gitlab end end + def viewer + rich_viewer || simple_viewer + end + def simple_viewer @simple_viewer ||= simple_viewer_class.new(self) end diff --git a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb index d19408ee87f..c837a6752f9 100644 --- a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb +++ b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb @@ -222,6 +222,11 @@ describe 'Merge request > User creates image diff notes', :js do end def create_image_diff_note + expand_text = 'Click to expand it.' + page.all('a', text: expand_text).each do |element| + element.click + end + find('.js-add-image-diff-note-button', match: :first).click find('.diff-content .note-textarea').native.send_keys('image diff test comment') click_button 'Comment' diff --git a/spec/fixtures/api/schemas/entities/diff_viewer.json b/spec/fixtures/api/schemas/entities/diff_viewer.json index 81325cd86c6..ae0fb32d3ac 100644 --- a/spec/fixtures/api/schemas/entities/diff_viewer.json +++ b/spec/fixtures/api/schemas/entities/diff_viewer.json @@ -14,6 +14,17 @@ "string", "null" ] + }, + "error_message": { + "type": [ + "string", + "null" + ] + }, + "collapsed": { + "type": [ + "boolean" + ] } }, "additionalProperties": false diff --git a/spec/javascripts/diffs/components/diff_content_spec.js b/spec/javascripts/diffs/components/diff_content_spec.js index 9e158327a77..a1bb51963d6 100644 --- a/spec/javascripts/diffs/components/diff_content_spec.js +++ b/spec/javascripts/diffs/components/diff_content_spec.js @@ -6,6 +6,7 @@ import { GREEN_BOX_IMAGE_URL, RED_BOX_IMAGE_URL } from 'spec/test_constants'; import '~/behaviors/markdown/render_gfm'; import diffFileMockData from '../mock_data/diff_file'; import discussionsMockData from '../mock_data/diff_discussions'; +import { diffViewerModes } from '~/ide/constants'; describe('DiffContent', () => { const Component = Vue.extend(DiffContentComponent); @@ -52,26 +53,39 @@ describe('DiffContent', () => { describe('empty files', () => { beforeEach(() => { - vm.diffFile.empty = true; vm.diffFile.highlighted_diff_lines = []; vm.diffFile.parallel_diff_lines = []; }); - it('should render a message', done => { + it('should render a no preview message if viewer returns no preview', done => { + vm.diffFile.viewer.name = diffViewerModes.no_preview; vm.$nextTick(() => { const block = vm.$el.querySelector('.diff-viewer .nothing-here-block'); expect(block).not.toBe(null); - expect(block.textContent.trim()).toContain('Empty file'); + expect(block.textContent.trim()).toContain('No preview for this file type'); + + done(); + }); + }); + + it('should render a not diffable message if viewer returns not diffable', done => { + vm.diffFile.viewer.name = diffViewerModes.not_diffable; + vm.$nextTick(() => { + const block = vm.$el.querySelector('.diff-viewer .nothing-here-block'); + + expect(block).not.toBe(null); + expect(block.textContent.trim()).toContain( + 'This diff was suppressed by a .gitattributes entry', + ); done(); }); }); it('should not render multiple messages', done => { - vm.diffFile.mode_changed = true; vm.diffFile.b_mode = '100755'; - vm.diffFile.viewer.name = 'mode_changed'; + vm.diffFile.viewer.name = diffViewerModes.mode_changed; vm.$nextTick(() => { expect(vm.$el.querySelectorAll('.nothing-here-block').length).toBe(1); @@ -81,6 +95,7 @@ describe('DiffContent', () => { }); it('should not render diff table', done => { + vm.diffFile.viewer.name = diffViewerModes.no_preview; vm.$nextTick(() => { expect(vm.$el.querySelector('table')).toBe(null); @@ -157,6 +172,7 @@ describe('DiffContent', () => { vm.diffFile.new_sha = 'DEF'; vm.diffFile.old_path = 'test.abc'; vm.diffFile.old_sha = 'ABC'; + vm.diffFile.viewer.name = diffViewerModes.added; vm.$nextTick(() => { expect(el.querySelectorAll('.js-diff-inline-view').length).toEqual(0); diff --git a/spec/javascripts/diffs/components/diff_file_header_spec.js b/spec/javascripts/diffs/components/diff_file_header_spec.js index 787a81fd88f..005a4751ea1 100644 --- a/spec/javascripts/diffs/components/diff_file_header_spec.js +++ b/spec/javascripts/diffs/components/diff_file_header_spec.js @@ -4,15 +4,15 @@ import diffsModule from '~/diffs/store/modules'; import notesModule from '~/notes/stores/modules'; import DiffFileHeader from '~/diffs/components/diff_file_header.vue'; import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import diffDiscussionsMockData from '../mock_data/diff_discussions'; +import { diffViewerModes } from '~/ide/constants'; Vue.use(Vuex); -const discussionFixture = 'merge_requests/diff_discussion.json'; - describe('diff_file_header', () => { let vm; let props; - const diffDiscussionMock = getJSONFixture(discussionFixture)[0]; + const diffDiscussionMock = diffDiscussionsMockData; const Component = Vue.extend(DiffFileHeader); const store = new Vuex.Store({ @@ -303,13 +303,13 @@ describe('diff_file_header', () => { }); it('displays old and new path if the file was renamed', () => { - props.diffFile.renamed_file = true; + props.diffFile.viewer.name = diffViewerModes.renamed; vm = mountComponentWithStore(Component, { props, store }); expect(filePaths()).toHaveLength(2); - expect(filePaths()[0]).toHaveText(props.diffFile.old_path); - expect(filePaths()[1]).toHaveText(props.diffFile.new_path); + expect(filePaths()[0]).toHaveText(props.diffFile.old_path_html); + expect(filePaths()[1]).toHaveText(props.diffFile.new_path_html); }); }); @@ -319,14 +319,12 @@ describe('diff_file_header', () => { const button = vm.$el.querySelector('.btn-clipboard'); expect(button).not.toBe(null); - expect(button.dataset.clipboardText).toBe( - '{"text":"files/ruby/popen.rb","gfm":"`files/ruby/popen.rb`"}', - ); + expect(button.dataset.clipboardText).toBe('{"text":"CHANGELOG.rb","gfm":"`CHANGELOG.rb`"}'); }); describe('file mode', () => { it('it displays old and new file mode if it changed', () => { - props.diffFile.mode_changed = true; + props.diffFile.viewer.name = diffViewerModes.mode_changed; vm = mountComponentWithStore(Component, { props, store }); @@ -338,7 +336,7 @@ describe('diff_file_header', () => { }); it('does not display the file mode if it has not changed', () => { - props.diffFile.mode_changed = false; + props.diffFile.viewer.name = diffViewerModes.text; vm = mountComponentWithStore(Component, { props, store }); diff --git a/spec/javascripts/diffs/components/diff_file_spec.js b/spec/javascripts/diffs/components/diff_file_spec.js index 1af49282c36..d16bc21022f 100644 --- a/spec/javascripts/diffs/components/diff_file_spec.js +++ b/spec/javascripts/diffs/components/diff_file_spec.js @@ -1,5 +1,6 @@ import Vue from 'vue'; import DiffFileComponent from '~/diffs/components/diff_file.vue'; +import { diffViewerModes, diffViewerErrors } from '~/ide/constants'; import store from '~/mr_notes/stores'; import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; import diffFileMockData from '../mock_data/diff_file'; @@ -27,8 +28,8 @@ describe('DiffFile', () => { expect(el.querySelector('.file-title-name').innerText.indexOf(file_path)).toBeGreaterThan(-1); expect(el.querySelector('.js-syntax-highlight')).toBeDefined(); - expect(vm.file.renderIt).toEqual(false); - vm.file.renderIt = true; + expect(vm.renderIt).toEqual(false); + vm.renderIt = true; vm.$nextTick(() => { expect(el.querySelectorAll('.line_content').length).toBeGreaterThan(5); @@ -38,9 +39,9 @@ describe('DiffFile', () => { describe('collapsed', () => { it('should not have file content', done => { expect(vm.$el.querySelectorAll('.diff-content').length).toEqual(1); - expect(vm.file.collapsed).toEqual(false); - vm.file.collapsed = true; - vm.file.renderIt = true; + expect(vm.isCollapsed).toEqual(false); + vm.isCollapsed = true; + vm.renderIt = true; vm.$nextTick(() => { expect(vm.$el.querySelectorAll('.diff-content').length).toEqual(0); @@ -50,9 +51,8 @@ describe('DiffFile', () => { }); it('should have collapsed text and link', done => { - vm.file.renderIt = true; - vm.file.collapsed = false; - vm.file.highlighted_diff_lines = null; + vm.renderIt = true; + vm.isCollapsed = true; vm.$nextTick(() => { expect(vm.$el.innerText).toContain('This diff is collapsed'); @@ -63,8 +63,8 @@ describe('DiffFile', () => { }); it('should have collapsed text and link even before rendered', done => { - vm.file.renderIt = false; - vm.file.collapsed = true; + vm.renderIt = false; + vm.isCollapsed = true; vm.$nextTick(() => { expect(vm.$el.innerText).toContain('This diff is collapsed'); @@ -75,10 +75,10 @@ describe('DiffFile', () => { }); it('should be collapsed for renamed files', done => { - vm.file.renderIt = true; - vm.file.collapsed = false; + vm.renderIt = true; + vm.isCollapsed = false; vm.file.highlighted_diff_lines = null; - vm.file.renamed_file = true; + vm.file.viewer.name = diffViewerModes.renamed; vm.$nextTick(() => { expect(vm.$el.innerText).not.toContain('This diff is collapsed'); @@ -88,10 +88,10 @@ describe('DiffFile', () => { }); it('should be collapsed for mode changed files', done => { - vm.file.renderIt = true; - vm.file.collapsed = false; + vm.renderIt = true; + vm.isCollapsed = false; vm.file.highlighted_diff_lines = null; - vm.file.mode_changed = true; + vm.file.viewer.name = diffViewerModes.mode_changed; vm.$nextTick(() => { expect(vm.$el.innerText).not.toContain('This diff is collapsed'); @@ -101,7 +101,7 @@ describe('DiffFile', () => { }); it('should have loading icon while loading a collapsed diffs', done => { - vm.file.collapsed = true; + vm.isCollapsed = true; vm.isLoadingCollapsedDiff = true; vm.$nextTick(() => { @@ -116,7 +116,7 @@ describe('DiffFile', () => { describe('too large diff', () => { it('should have too large warning and blob link', done => { const BLOB_LINK = '/file/view/path'; - vm.file.too_large = true; + vm.file.viewer.error = diffViewerErrors.too_large; vm.file.view_path = BLOB_LINK; vm.$nextTick(() => { @@ -140,11 +140,11 @@ describe('DiffFile', () => { vm.file.highlighted_diff_lines = undefined; vm.file.parallel_diff_lines = []; - vm.file.collapsed = true; + vm.isCollapsed = true; vm.$nextTick() .then(() => { - vm.file.collapsed = false; + vm.isCollapsed = false; return vm.$nextTick(); }) diff --git a/spec/javascripts/diffs/mock_data/diff_discussions.js b/spec/javascripts/diffs/mock_data/diff_discussions.js index c1e9f791925..4a091b4580b 100644 --- a/spec/javascripts/diffs/mock_data/diff_discussions.js +++ b/spec/javascripts/diffs/mock_data/diff_discussions.js @@ -266,7 +266,7 @@ export default { blob_name: 'CHANGELOG', blob_icon: '<i aria-hidden="true" data-hidden="true" class="fa fa-file-text-o fa-fw"></i>', file_hash: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a', - file_path: 'CHANGELOG', + file_path: 'CHANGELOG.rb', new_file: false, deleted_file: false, renamed_file: false, @@ -286,7 +286,7 @@ export default { content_sha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13', stored_externally: null, external_storage: null, - old_path_html: ['CHANGELOG', 'CHANGELOG'], + old_path_html: 'CHANGELOG_OLD', new_path_html: 'CHANGELOG', context_lines_path: '/gitlab-org/gitlab-test/blob/c48ee0d1bf3b30453f5b32250ce03134beaa6d13/CHANGELOG/diff', @@ -485,6 +485,10 @@ export default { }, }, ], + viewer: { + name: 'text', + error: null, + }, }, diff_discussion: true, truncated_diff_lines: [ diff --git a/spec/javascripts/diffs/mock_data/diff_file.js b/spec/javascripts/diffs/mock_data/diff_file.js index 031c9842f2f..32af9ea8ddd 100644 --- a/spec/javascripts/diffs/mock_data/diff_file.js +++ b/spec/javascripts/diffs/mock_data/diff_file.js @@ -25,6 +25,8 @@ export default { text: true, viewer: { name: 'text', + error: null, + collapsed: false, }, added_lines: 2, removed_lines: 0, diff --git a/spec/javascripts/diffs/store/actions_spec.js b/spec/javascripts/diffs/store/actions_spec.js index b53ae4cecfd..7caa37f45b9 100644 --- a/spec/javascripts/diffs/store/actions_spec.js +++ b/spec/javascripts/diffs/store/actions_spec.js @@ -262,12 +262,16 @@ describe('DiffsStoreActions', () => { { id: 1, renderIt: false, - collapsed: false, + viewer: { + collapsed: false, + }, }, { id: 2, renderIt: false, - collapsed: false, + viewer: { + collapsed: false, + }, }, ], }; @@ -766,7 +770,9 @@ describe('DiffsStoreActions', () => { diffFiles: [ { file_hash: 'HASH', - collapsed, + viewer: { + collapsed, + }, renderIt, }, ], diff --git a/spec/javascripts/diffs/store/getters_spec.js b/spec/javascripts/diffs/store/getters_spec.js index 4f69dc92ab8..0ab88e6b2aa 100644 --- a/spec/javascripts/diffs/store/getters_spec.js +++ b/spec/javascripts/diffs/store/getters_spec.js @@ -51,13 +51,13 @@ describe('Diffs Module Getters', () => { describe('hasCollapsedFile', () => { it('returns true when all files are collapsed', () => { - localState.diffFiles = [{ collapsed: true }, { collapsed: true }]; + localState.diffFiles = [{ viewer: { collapsed: true } }, { viewer: { collapsed: true } }]; expect(getters.hasCollapsedFile(localState)).toEqual(true); }); it('returns true when at least one file is collapsed', () => { - localState.diffFiles = [{ collapsed: false }, { collapsed: true }]; + localState.diffFiles = [{ viewer: { collapsed: false } }, { viewer: { collapsed: true } }]; expect(getters.hasCollapsedFile(localState)).toEqual(true); }); diff --git a/spec/javascripts/diffs/store/mutations_spec.js b/spec/javascripts/diffs/store/mutations_spec.js index a6f3f9b9dc3..09ee691b602 100644 --- a/spec/javascripts/diffs/store/mutations_spec.js +++ b/spec/javascripts/diffs/store/mutations_spec.js @@ -121,8 +121,14 @@ describe('DiffsStoreMutations', () => { describe('ADD_COLLAPSED_DIFFS', () => { it('should update the state with the given data for the given file hash', () => { const fileHash = 123; - const state = { diffFiles: [{}, { file_hash: fileHash, existing_field: 0 }] }; - const data = { diff_files: [{ file_hash: fileHash, extra_field: 1, existing_field: 1 }] }; + const state = { + diffFiles: [{}, { file_hash: fileHash, existing_field: 0 }], + }; + const data = { + diff_files: [ + { file_hash: fileHash, extra_field: 1, existing_field: 1, viewer: { name: 'text' } }, + ], + }; mutations[types.ADD_COLLAPSED_DIFFS](state, { file: state.diffFiles[1], data }); diff --git a/spec/javascripts/diffs/store/utils_spec.js b/spec/javascripts/diffs/store/utils_spec.js index baf6e111f9f..599ea9cd420 100644 --- a/spec/javascripts/diffs/store/utils_spec.js +++ b/spec/javascripts/diffs/store/utils_spec.js @@ -601,7 +601,7 @@ describe('DiffsStoreUtils', () => { it('returns mode_changed if key has no match', () => { expect( utils.getDiffMode({ - mode_changed: true, + viewer: { name: 'mode_changed' }, }), ).toBe('mode_changed'); }); diff --git a/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js b/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js index 6add6cdac4d..660eaddf01f 100644 --- a/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js +++ b/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js @@ -22,6 +22,7 @@ describe('DiffViewer', () => { createComponent({ diffMode: 'replaced', + diffViewerMode: 'image', newPath: GREEN_BOX_IMAGE_URL, newSha: 'ABC', oldPath: RED_BOX_IMAGE_URL, @@ -45,6 +46,7 @@ describe('DiffViewer', () => { it('renders fallback download diff display', done => { createComponent({ diffMode: 'replaced', + diffViewerMode: 'added', newPath: 'test.abc', newSha: 'ABC', oldPath: 'testold.abc', @@ -72,6 +74,7 @@ describe('DiffViewer', () => { it('renders renamed component', () => { createComponent({ diffMode: 'renamed', + diffViewerMode: 'renamed', newPath: 'test.abc', newSha: 'ABC', oldPath: 'testold.abc', @@ -84,6 +87,7 @@ describe('DiffViewer', () => { it('renders mode changed component', () => { createComponent({ diffMode: 'mode_changed', + diffViewerMode: 'image', newPath: 'test.abc', newSha: 'ABC', oldPath: 'testold.abc', diff --git a/spec/serializers/diff_file_entity_spec.rb b/spec/serializers/diff_file_entity_spec.rb index 073c13c2cbb..92b649f5b6c 100644 --- a/spec/serializers/diff_file_entity_spec.rb +++ b/spec/serializers/diff_file_entity_spec.rb @@ -22,7 +22,7 @@ describe DiffFileEntity do let(:request) { EntityRequest.new(project: project, current_user: user) } let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } let(:entity) { described_class.new(diff_file, request: request, merge_request: merge_request) } - let(:exposed_urls) { %i(load_collapsed_diff_url edit_path view_path context_lines_path) } + let(:exposed_urls) { %i(edit_path view_path context_lines_path) } it_behaves_like 'diff file entity' @@ -38,6 +38,12 @@ describe DiffFileEntity do expect(response[attribute]).to include(merge_request.target_project.to_param) end end + + it 'exposes load_collapsed_diff_url if the file viewer is collapsed' do + allow(diff_file.viewer).to receive(:collapsed?) { true } + + expect(subject).to include(:load_collapsed_diff_url) + end end context '#parallel_diff_lines' do diff --git a/spec/support/shared_examples/serializers/diff_file_entity_examples.rb b/spec/support/shared_examples/serializers/diff_file_entity_examples.rb index 1770308f789..96cb71be737 100644 --- a/spec/support/shared_examples/serializers/diff_file_entity_examples.rb +++ b/spec/support/shared_examples/serializers/diff_file_entity_examples.rb @@ -6,9 +6,9 @@ shared_examples 'diff file base entity' do :submodule_tree_url, :old_path_html, :new_path_html, :blob, :can_modify_blob, :file_hash, :file_path, :old_path, :new_path, - :collapsed, :text, :diff_refs, :stored_externally, + :viewer, :diff_refs, :stored_externally, :external_storage, :renamed_file, :deleted_file, - :mode_changed, :a_mode, :b_mode, :new_file) + :a_mode, :b_mode, :new_file) end # Converted diff files from GitHub import does not contain blob file @@ -30,9 +30,9 @@ shared_examples 'diff file entity' do it_behaves_like 'diff file base entity' it 'exposes correct attributes' do - expect(subject).to include(:too_large, :added_lines, :removed_lines, + expect(subject).to include(:added_lines, :removed_lines, :context_lines_path, :highlighted_diff_lines, - :parallel_diff_lines, :empty) + :parallel_diff_lines) end it 'includes viewer' do |