Commit c128cb00 authored by Phil Hughes's avatar Phil Hughes

Merge branch 'slashmanov/remove-virtual-scroll-diffs-flag' into 'master'

Remove Virtual Scroll flag from Merge Request Diffs

See merge request gitlab-org/gitlab!79750
parents 800ddce8 90d6c654
...@@ -53,6 +53,7 @@ import DiffFile from './diff_file.vue'; ...@@ -53,6 +53,7 @@ import DiffFile from './diff_file.vue';
import HiddenFilesWarning from './hidden_files_warning.vue'; import HiddenFilesWarning from './hidden_files_warning.vue';
import NoChanges from './no_changes.vue'; import NoChanges from './no_changes.vue';
import TreeList from './tree_list.vue'; import TreeList from './tree_list.vue';
import VirtualScrollerScrollSync from './virtual_scroller_scroll_sync';
export default { export default {
name: 'DiffsApp', name: 'DiffsApp',
...@@ -62,8 +63,7 @@ export default { ...@@ -62,8 +63,7 @@ export default {
DynamicScrollerItem: () => DynamicScrollerItem: () =>
import('vendor/vue-virtual-scroller').then(({ DynamicScrollerItem }) => DynamicScrollerItem), import('vendor/vue-virtual-scroller').then(({ DynamicScrollerItem }) => DynamicScrollerItem),
PreRenderer: () => import('./pre_renderer.vue').then((PreRenderer) => PreRenderer), PreRenderer: () => import('./pre_renderer.vue').then((PreRenderer) => PreRenderer),
VirtualScrollerScrollSync: () => VirtualScrollerScrollSync,
import('./virtual_scroller_scroll_sync').then((VSSSync) => VSSSync),
CompareVersions, CompareVersions,
DiffFile, DiffFile,
NoChanges, NoChanges,
...@@ -406,10 +406,8 @@ export default { ...@@ -406,10 +406,8 @@ export default {
this.unsubscribeFromEvents(); this.unsubscribeFromEvents();
this.removeEventListeners(); this.removeEventListeners();
if (window.gon?.features?.diffsVirtualScrolling) { diffsEventHub.$off('scrollToFileHash', this.scrollVirtualScrollerToFileHash);
diffsEventHub.$off('scrollToFileHash', this.scrollVirtualScrollerToFileHash); diffsEventHub.$off('scrollToIndex', this.scrollVirtualScrollerToIndex);
diffsEventHub.$off('scrollToIndex', this.scrollVirtualScrollerToIndex);
}
}, },
methods: { methods: {
...mapActions(['startTaskList']), ...mapActions(['startTaskList']),
...@@ -522,32 +520,27 @@ export default { ...@@ -522,32 +520,27 @@ export default {
); );
} }
if ( let keydownTime;
window.gon?.features?.diffsVirtualScrolling || Mousetrap.bind(['mod+f', 'mod+g'], () => {
window.gon?.features?.usageDataDiffSearches keydownTime = new Date().getTime();
) { });
let keydownTime;
Mousetrap.bind(['mod+f', 'mod+g'], () => {
keydownTime = new Date().getTime();
});
window.addEventListener('blur', () => { window.addEventListener('blur', () => {
if (keydownTime) { if (keydownTime) {
const delta = new Date().getTime() - keydownTime; const delta = new Date().getTime() - keydownTime;
// To make sure the user is using the find function we need to wait for blur // To make sure the user is using the find function we need to wait for blur
// and max 1000ms to be sure it the search box is filtered // and max 1000ms to be sure it the search box is filtered
if (delta >= 0 && delta < 1000) { if (delta >= 0 && delta < 1000) {
this.disableVirtualScroller(); this.disableVirtualScroller();
if (window.gon?.features?.usageDataDiffSearches) { if (window.gon?.features?.usageDataDiffSearches) {
api.trackRedisHllUserEvent('i_code_review_user_searches_diff'); api.trackRedisHllUserEvent('i_code_review_user_searches_diff');
api.trackRedisCounterEvent('diff_searches'); api.trackRedisCounterEvent('diff_searches');
}
} }
} }
}); }
} });
}, },
removeEventListeners() { removeEventListeners() {
Mousetrap.unbind(keysFor(MR_PREVIOUS_FILE_IN_DIFF)); Mousetrap.unbind(keysFor(MR_PREVIOUS_FILE_IN_DIFF));
...@@ -589,8 +582,6 @@ export default { ...@@ -589,8 +582,6 @@ export default {
this.virtualScrollCurrentIndex = -1; this.virtualScrollCurrentIndex = -1;
}, },
scrollVirtualScrollerToDiffNote() { scrollVirtualScrollerToDiffNote() {
if (!window.gon?.features?.diffsVirtualScrolling) return;
const id = window?.location?.hash; const id = window?.location?.hash;
if (id.startsWith('#note_')) { if (id.startsWith('#note_')) {
...@@ -605,11 +596,7 @@ export default { ...@@ -605,11 +596,7 @@ export default {
} }
}, },
subscribeToVirtualScrollingEvents() { subscribeToVirtualScrollingEvents() {
if ( if (this.shouldShow && !this.subscribedToVirtualScrollingEvents) {
window.gon?.features?.diffsVirtualScrolling &&
this.shouldShow &&
!this.subscribedToVirtualScrollingEvents
) {
diffsEventHub.$on('scrollToFileHash', this.scrollVirtualScrollerToFileHash); diffsEventHub.$on('scrollToFileHash', this.scrollVirtualScrollerToFileHash);
diffsEventHub.$on('scrollToIndex', this.scrollVirtualScrollerToIndex); diffsEventHub.$on('scrollToIndex', this.scrollVirtualScrollerToIndex);
......
...@@ -209,7 +209,7 @@ export default { ...@@ -209,7 +209,7 @@ export default {
handler(val) { handler(val) {
const el = this.$el.closest('.vue-recycle-scroller__item-view'); const el = this.$el.closest('.vue-recycle-scroller__item-view');
if (this.glFeatures.diffsVirtualScrolling && el) { if (el) {
// We can't add a style with Vue because of the way the virtual // We can't add a style with Vue because of the way the virtual
// scroller library renders the diff files // scroller library renders the diff files
el.style.zIndex = val ? '1' : null; el.style.zIndex = val ? '1' : null;
......
...@@ -29,8 +29,6 @@ export const UNFOLD_COUNT = 20; ...@@ -29,8 +29,6 @@ export const UNFOLD_COUNT = 20;
export const COUNT_OF_AVATARS_IN_GUTTER = 3; export const COUNT_OF_AVATARS_IN_GUTTER = 3;
export const LENGTH_OF_AVATAR_TOOLTIP = 17; export const LENGTH_OF_AVATAR_TOOLTIP = 17;
export const LINES_TO_BE_RENDERED_DIRECTLY = 100;
export const DIFF_FILE_SYMLINK_MODE = '120000'; export const DIFF_FILE_SYMLINK_MODE = '120000';
export const DIFF_FILE_DELETED_MODE = '0'; export const DIFF_FILE_DELETED_MODE = '0';
......
import Vue from 'vue'; import Vue from 'vue';
import { mapActions, mapState, mapGetters } from 'vuex'; import { mapActions, mapState, mapGetters } from 'vuex';
import { getCookie, setCookie, parseBoolean, removeCookie } from '~/lib/utils/common_utils'; import { getCookie, parseBoolean, removeCookie } from '~/lib/utils/common_utils';
import { getParameterValues } from '~/lib/utils/url_utility';
import eventHub from '../notes/event_hub'; import eventHub from '../notes/event_hub';
import diffsApp from './components/app.vue'; import diffsApp from './components/app.vue';
...@@ -74,11 +73,6 @@ export default function initDiffsApp(store) { ...@@ -74,11 +73,6 @@ export default function initDiffsApp(store) {
trackClick: false, trackClick: false,
}); });
} }
const vScrollingParam = getParameterValues('virtual_scrolling')[0];
if (vScrollingParam === 'false' || vScrollingParam === 'true') {
setCookie('diffs_virtual_scrolling', vScrollingParam);
}
}, },
methods: { methods: {
...mapActions('diffs', ['setRenderTreeList', 'setShowWhitespace']), ...mapActions('diffs', ['setRenderTreeList', 'setShowWhitespace']),
......
...@@ -125,7 +125,7 @@ export const fetchDiffFilesBatch = ({ commit, state, dispatch }) => { ...@@ -125,7 +125,7 @@ export const fetchDiffFilesBatch = ({ commit, state, dispatch }) => {
commit(types.SET_DIFF_DATA_BATCH, { diff_files }); commit(types.SET_DIFF_DATA_BATCH, { diff_files });
commit(types.SET_BATCH_LOADING_STATE, 'loaded'); commit(types.SET_BATCH_LOADING_STATE, 'loaded');
if (window.gon?.features?.diffsVirtualScrolling && !scrolledVirtualScroller) { if (!scrolledVirtualScroller) {
const index = state.diffFiles.findIndex( const index = state.diffFiles.findIndex(
(f) => (f) =>
f.file_hash === hash || f[INLINE_DIFF_LINES_KEY].find((l) => l.line_code === hash), f.file_hash === hash || f[INLINE_DIFF_LINES_KEY].find((l) => l.line_code === hash),
...@@ -195,9 +195,7 @@ export const fetchDiffFilesBatch = ({ commit, state, dispatch }) => { ...@@ -195,9 +195,7 @@ export const fetchDiffFilesBatch = ({ commit, state, dispatch }) => {
commit(types.SET_BATCH_LOADING_STATE, 'error'); commit(types.SET_BATCH_LOADING_STATE, 'error');
}); });
return getBatch().then( return getBatch();
() => !window.gon?.features?.diffsVirtualScrolling && handleLocationHash(),
);
}; };
export const fetchDiffFilesMeta = ({ commit, state }) => { export const fetchDiffFilesMeta = ({ commit, state }) => {
...@@ -529,7 +527,7 @@ export const setCurrentFileHash = ({ commit }, hash) => { ...@@ -529,7 +527,7 @@ export const setCurrentFileHash = ({ commit }, hash) => {
commit(types.SET_CURRENT_DIFF_FILE, hash); commit(types.SET_CURRENT_DIFF_FILE, hash);
}; };
export const scrollToFile = ({ state, commit, getters }, { path, setHash = true }) => { export const scrollToFile = ({ state, commit, getters }, { path }) => {
if (!state.treeEntries[path]) return; if (!state.treeEntries[path]) return;
const { fileHash } = state.treeEntries[path]; const { fileHash } = state.treeEntries[path];
...@@ -539,11 +537,9 @@ export const scrollToFile = ({ state, commit, getters }, { path, setHash = true ...@@ -539,11 +537,9 @@ export const scrollToFile = ({ state, commit, getters }, { path, setHash = true
if (getters.isVirtualScrollingEnabled) { if (getters.isVirtualScrollingEnabled) {
eventHub.$emit('scrollToFileHash', fileHash); eventHub.$emit('scrollToFileHash', fileHash);
if (setHash) { setTimeout(() => {
setTimeout(() => { window.history.replaceState(null, null, `#${fileHash}`);
window.history.replaceState(null, null, `#${fileHash}`); });
});
}
} else { } else {
document.location.hash = fileHash; document.location.hash = fileHash;
......
import { getCookie } from '~/lib/utils/common_utils';
import { getParameterValues } from '~/lib/utils/url_utility';
import { __, n__ } from '~/locale'; import { __, n__ } from '~/locale';
import { getParameterValues } from '~/lib/utils/url_utility';
import { import {
PARALLEL_DIFF_VIEW_TYPE, PARALLEL_DIFF_VIEW_TYPE,
INLINE_DIFF_VIEW_TYPE, INLINE_DIFF_VIEW_TYPE,
...@@ -175,21 +174,11 @@ export function suggestionCommitMessage(state, _, rootState) { ...@@ -175,21 +174,11 @@ export function suggestionCommitMessage(state, _, rootState) {
} }
export const isVirtualScrollingEnabled = (state) => { export const isVirtualScrollingEnabled = (state) => {
const vSrollerCookie = getCookie('diffs_virtual_scrolling'); if (state.disableVirtualScroller || getParameterValues('virtual_scrolling')[0] === 'false') {
if (state.disableVirtualScroller) {
return false; return false;
} }
if (vSrollerCookie) { return !state.viewDiffsFileByFile;
return vSrollerCookie === 'true';
}
return (
!state.viewDiffsFileByFile &&
(window.gon?.features?.diffsVirtualScrolling ||
getParameterValues('virtual_scrolling')[0] === 'true')
);
}; };
export const isBatchLoading = (state) => state.batchLoadingState === 'loading'; export const isBatchLoading = (state) => state.batchLoadingState === 'loading';
......
...@@ -9,7 +9,6 @@ import { ...@@ -9,7 +9,6 @@ import {
NEW_LINE_TYPE, NEW_LINE_TYPE,
OLD_LINE_TYPE, OLD_LINE_TYPE,
MATCH_LINE_TYPE, MATCH_LINE_TYPE,
LINES_TO_BE_RENDERED_DIRECTLY,
INLINE_DIFF_LINES_KEY, INLINE_DIFF_LINES_KEY,
CONFLICT_OUR, CONFLICT_OUR,
CONFLICT_THEIR, CONFLICT_THEIR,
...@@ -380,16 +379,9 @@ function prepareDiffFileLines(file) { ...@@ -380,16 +379,9 @@ function prepareDiffFileLines(file) {
return file; return file;
} }
function finalizeDiffFile(file, index) { function finalizeDiffFile(file) {
let renderIt = Boolean(window.gon?.features?.diffsVirtualScrolling);
if (!window.gon?.features?.diffsVirtualScrolling) {
renderIt =
index < 3 ? file[INLINE_DIFF_LINES_KEY].length < LINES_TO_BE_RENDERED_DIRECTLY : false;
}
Object.assign(file, { Object.assign(file, {
renderIt, renderIt: true,
isShowingFullFile: false, isShowingFullFile: false,
isLoadingFullFile: false, isLoadingFullFile: false,
discussions: [], discussions: [],
...@@ -417,15 +409,13 @@ export function prepareDiffData({ diff, priorFiles = [], meta = false }) { ...@@ -417,15 +409,13 @@ export function prepareDiffData({ diff, priorFiles = [], meta = false }) {
.map((file, index, allFiles) => prepareRawDiffFile({ file, allFiles, meta })) .map((file, index, allFiles) => prepareRawDiffFile({ file, allFiles, meta }))
.map(ensureBasicDiffFileLines) .map(ensureBasicDiffFileLines)
.map(prepareDiffFileLines) .map(prepareDiffFileLines)
.map((file, index) => finalizeDiffFile(file, priorFiles.length + index)); .map((file) => finalizeDiffFile(file));
return deduplicateFilesList([...priorFiles, ...cleanedFiles]); return deduplicateFilesList([...priorFiles, ...cleanedFiles]);
} }
export function getDiffPositionByLineCode(diffFiles) { export function getDiffPositionByLineCode(diffFiles) {
let lines = []; const lines = diffFiles.reduce((acc, diffFile) => {
lines = diffFiles.reduce((acc, diffFile) => {
diffFile[INLINE_DIFF_LINES_KEY].forEach((line) => { diffFile[INLINE_DIFF_LINES_KEY].forEach((line) => {
acc.push({ file: diffFile, line }); acc.push({ file: diffFile, line });
}); });
......
...@@ -3,8 +3,6 @@ import { scrollToElementWithContext, scrollToElement } from '~/lib/utils/common_ ...@@ -3,8 +3,6 @@ import { scrollToElementWithContext, scrollToElement } from '~/lib/utils/common_
import { updateHistory } from '../../lib/utils/url_utility'; import { updateHistory } from '../../lib/utils/url_utility';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
const isDiffsVirtualScrollingEnabled = () => window.gon?.features?.diffsVirtualScrolling;
/** /**
* @param {string} selector * @param {string} selector
* @returns {boolean} * @returns {boolean}
...@@ -15,7 +13,7 @@ function scrollTo(selector, { withoutContext = false } = {}) { ...@@ -15,7 +13,7 @@ function scrollTo(selector, { withoutContext = false } = {}) {
if (el) { if (el) {
scrollFunction(el, { scrollFunction(el, {
behavior: isDiffsVirtualScrollingEnabled() ? 'auto' : 'smooth', behavior: 'auto',
}); });
return true; return true;
} }
...@@ -31,7 +29,7 @@ function updateUrlWithNoteId(noteId) { ...@@ -31,7 +29,7 @@ function updateUrlWithNoteId(noteId) {
replace: true, replace: true,
}; };
if (noteId && isDiffsVirtualScrollingEnabled()) { if (noteId) {
// Temporarily mask the ID to avoid the browser default // Temporarily mask the ID to avoid the browser default
// scrolling taking over which is broken with virtual // scrolling taking over which is broken with virtual
// scrolling enabled. // scrolling enabled.
...@@ -115,17 +113,13 @@ function handleDiscussionJump(self, fn, discussionId = self.currentDiscussionId) ...@@ -115,17 +113,13 @@ function handleDiscussionJump(self, fn, discussionId = self.currentDiscussionId)
const isDiffView = window.mrTabs.currentAction === 'diffs'; const isDiffView = window.mrTabs.currentAction === 'diffs';
const targetId = fn(discussionId, isDiffView); const targetId = fn(discussionId, isDiffView);
const discussion = self.getDiscussion(targetId); const discussion = self.getDiscussion(targetId);
const setHash = !isDiffView && !isDiffsVirtualScrollingEnabled();
const discussionFilePath = discussion?.diff_file?.file_path; const discussionFilePath = discussion?.diff_file?.file_path;
if (isDiffsVirtualScrollingEnabled()) { window.location.hash = '';
window.location.hash = '';
}
if (discussionFilePath) { if (discussionFilePath) {
self.scrollToFile({ self.scrollToFile({
path: discussionFilePath, path: discussionFilePath,
setHash,
}); });
} }
......
...@@ -42,7 +42,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo ...@@ -42,7 +42,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
push_frontend_feature_flag(:paginated_notes, @project, default_enabled: :yaml) push_frontend_feature_flag(:paginated_notes, @project, default_enabled: :yaml)
push_frontend_feature_flag(:confidential_notes, @project, default_enabled: :yaml) push_frontend_feature_flag(:confidential_notes, @project, default_enabled: :yaml)
push_frontend_feature_flag(:improved_emoji_picker, project, default_enabled: :yaml) push_frontend_feature_flag(:improved_emoji_picker, project, default_enabled: :yaml)
push_frontend_feature_flag(:diffs_virtual_scrolling, project, default_enabled: :yaml)
push_frontend_feature_flag(:restructured_mr_widget, project, default_enabled: :yaml) push_frontend_feature_flag(:restructured_mr_widget, project, default_enabled: :yaml)
push_frontend_feature_flag(:refactor_mr_widgets_extensions, @project, default_enabled: :yaml) push_frontend_feature_flag(:refactor_mr_widgets_extensions, @project, default_enabled: :yaml)
push_frontend_feature_flag(:rebase_without_ci_ui, @project, default_enabled: :yaml) push_frontend_feature_flag(:rebase_without_ci_ui, @project, default_enabled: :yaml)
......
---
name: diffs_virtual_scrolling
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60312
rollout_issue_url:
milestone: '13.12'
type: development
group: group::code review
default_enabled: true
...@@ -32,7 +32,7 @@ RSpec.describe 'Merge request > Batch comments', :js do ...@@ -32,7 +32,7 @@ RSpec.describe 'Merge request > Batch comments', :js do
expect(page).to have_selector('[data-testid="review_bar_component"]') expect(page).to have_selector('[data-testid="review_bar_component"]')
expect(find('.review-bar-content .btn-confirm')).to have_content('1') expect(find('[data-testid="review_bar_component"] .btn-confirm')).to have_content('1')
end end
it 'publishes review' do it 'publishes review' do
...@@ -150,10 +150,6 @@ RSpec.describe 'Merge request > Batch comments', :js do ...@@ -150,10 +150,6 @@ RSpec.describe 'Merge request > Batch comments', :js do
it 'adds draft comments to both sides' do it 'adds draft comments to both sides' do
write_parallel_comment('2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9') write_parallel_comment('2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9')
# make sure line 9 is in the view
execute_script("window.scrollBy(0, -200)")
write_parallel_comment('2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9', button_text: 'Add to review', text: 'Another wrong line') write_parallel_comment('2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9', button_text: 'Add to review', text: 'Another wrong line')
expect(find('.new .draft-note-component')).to have_content('Line is wrong') expect(find('.new .draft-note-component')).to have_content('Line is wrong')
...@@ -255,13 +251,15 @@ RSpec.describe 'Merge request > Batch comments', :js do ...@@ -255,13 +251,15 @@ RSpec.describe 'Merge request > Batch comments', :js do
end end
def write_diff_comment(**params) def write_diff_comment(**params)
click_diff_line(find("[id='#{sample_compare.changes[0][:line_code]}']")) click_diff_line(find_by_scrolling("[id='#{sample_compare.changes[0][:line_code]}']"))
write_comment(**params) write_comment(**params)
end end
def write_parallel_comment(line, **params) def write_parallel_comment(line, **params)
find("div[id='#{line}']").hover line_element = find_by_scrolling("[id='#{line}']")
scroll_to_elements_bottom(line_element)
line_element.hover
find(".js-add-diff-note-button").click find(".js-add-diff-note-button").click
write_comment(selector: "form[data-line-code='#{line}']", **params) write_comment(selector: "form[data-line-code='#{line}']", **params)
......
...@@ -25,14 +25,15 @@ RSpec.describe 'User comments on a diff', :js do ...@@ -25,14 +25,15 @@ RSpec.describe 'User comments on a diff', :js do
context 'when toggling inline comments' do context 'when toggling inline comments' do
context 'in a single file' do context 'in a single file' do
it 'hides a comment' do it 'hides a comment' do
click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']")) line_element = find_by_scrolling("[id='#{sample_compare.changes[1][:line_code]}']").find(:xpath, "..")
click_diff_line(line_element)
page.within('.js-discussion-note-form') do page.within('.js-discussion-note-form') do
fill_in('note_note', with: 'Line is wrong') fill_in('note_note', with: 'Line is wrong')
click_button('Add comment now') click_button('Add comment now')
end end
page.within('.diff-files-holder > div:nth-child(6)') do page.within(line_element.ancestor('[data-path]')) do
expect(page).to have_content('Line is wrong') expect(page).to have_content('Line is wrong')
find('.js-diff-more-actions').click find('.js-diff-more-actions').click
...@@ -45,7 +46,9 @@ RSpec.describe 'User comments on a diff', :js do ...@@ -45,7 +46,9 @@ RSpec.describe 'User comments on a diff', :js do
context 'in multiple files' do context 'in multiple files' do
it 'toggles comments' do it 'toggles comments' do
click_diff_line(find("[id='#{sample_compare.changes[0][:line_code]}']")) first_line_element = find_by_scrolling("[id='#{sample_compare.changes[0][:line_code]}']").find(:xpath, "..")
first_root_element = first_line_element.ancestor('[data-path]')
click_diff_line(first_line_element)
page.within('.js-discussion-note-form') do page.within('.js-discussion-note-form') do
fill_in('note_note', with: 'Line is correct') fill_in('note_note', with: 'Line is correct')
...@@ -54,11 +57,14 @@ RSpec.describe 'User comments on a diff', :js do ...@@ -54,11 +57,14 @@ RSpec.describe 'User comments on a diff', :js do
wait_for_requests wait_for_requests
page.within('.diff-files-holder > div:nth-child(5) .note-body > .note-text') do page.within(first_root_element) do
expect(page).to have_content('Line is correct') expect(page).to have_content('Line is correct')
end end
click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']")) second_line_element = find_by_scrolling("[id='#{sample_compare.changes[1][:line_code]}']")
second_root_element = second_line_element.ancestor('[data-path]')
click_diff_line(second_line_element)
page.within('.js-discussion-note-form') do page.within('.js-discussion-note-form') do
fill_in('note_note', with: 'Line is wrong') fill_in('note_note', with: 'Line is wrong')
...@@ -68,7 +74,7 @@ RSpec.describe 'User comments on a diff', :js do ...@@ -68,7 +74,7 @@ RSpec.describe 'User comments on a diff', :js do
wait_for_requests wait_for_requests
# Hide the comment. # Hide the comment.
page.within('.diff-files-holder > div:nth-child(6)') do page.within(second_root_element) do
find('.js-diff-more-actions').click find('.js-diff-more-actions').click
click_button 'Hide comments on this file' click_button 'Hide comments on this file'
...@@ -77,37 +83,36 @@ RSpec.describe 'User comments on a diff', :js do ...@@ -77,37 +83,36 @@ RSpec.describe 'User comments on a diff', :js do
# At this moment a user should see only one comment. # At this moment a user should see only one comment.
# The other one should be hidden. # The other one should be hidden.
page.within('.diff-files-holder > div:nth-child(5) .note-body > .note-text') do page.within(first_root_element) do
expect(page).to have_content('Line is correct') expect(page).to have_content('Line is correct')
end end
# Show the comment. # Show the comment.
page.within('.diff-files-holder > div:nth-child(6)') do page.within(second_root_element) do
find('.js-diff-more-actions').click find('.js-diff-more-actions').click
click_button 'Show comments on this file' click_button 'Show comments on this file'
end end
# Now both the comments should be shown. # Now both the comments should be shown.
page.within('.diff-files-holder > div:nth-child(6) .note-body > .note-text') do page.within(second_root_element) do
expect(page).to have_content('Line is wrong') expect(page).to have_content('Line is wrong')
end end
page.within('.diff-files-holder > div:nth-child(5) .note-body > .note-text') do page.within(first_root_element) do
expect(page).to have_content('Line is correct') expect(page).to have_content('Line is correct')
end end
# Check the same comments in the side-by-side view. # Check the same comments in the side-by-side view.
execute_script("window.scrollTo(0,0);")
find('.js-show-diff-settings').click find('.js-show-diff-settings').click
click_button 'Side-by-side' click_button 'Side-by-side'
wait_for_requests wait_for_requests
page.within('.diff-files-holder > div:nth-child(6) .parallel .note-body > .note-text') do page.within(second_root_element) do
expect(page).to have_content('Line is wrong') expect(page).to have_content('Line is wrong')
end end
page.within('.diff-files-holder > div:nth-child(5) .parallel .note-body > .note-text') do page.within(first_root_element) do
expect(page).to have_content('Line is correct') expect(page).to have_content('Line is correct')
end end
end end
...@@ -121,7 +126,7 @@ RSpec.describe 'User comments on a diff', :js do ...@@ -121,7 +126,7 @@ RSpec.describe 'User comments on a diff', :js do
context 'when adding multiline comments' do context 'when adding multiline comments' do
it 'saves a multiline comment' do it 'saves a multiline comment' do
click_diff_line(find("[id='#{sample_commit.line_code}']")) click_diff_line(find_by_scrolling("[id='#{sample_commit.line_code}']").find(:xpath, '..'))
add_comment('-13', '+14') add_comment('-13', '+14')
end end
...@@ -133,13 +138,13 @@ RSpec.describe 'User comments on a diff', :js do ...@@ -133,13 +138,13 @@ RSpec.describe 'User comments on a diff', :js do
# In `files/ruby/popen.rb` # In `files/ruby/popen.rb`
it 'allows comments for changes involving both sides' do it 'allows comments for changes involving both sides' do
# click +15, select -13 add and verify comment # click +15, select -13 add and verify comment
click_diff_line(find('div[data-path="files/ruby/popen.rb"] .right-side a[data-linenumber="15"]').find(:xpath, '../../..'), 'right') click_diff_line(find_by_scrolling('div[data-path="files/ruby/popen.rb"] .right-side a[data-linenumber="15"]').find(:xpath, '../../..'), 'right')
add_comment('-13', '+15') add_comment('-13', '+15')
end end
it 'allows comments on previously hidden lines at the top of a file', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/285294' do it 'allows comments on previously hidden lines at the top of a file', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/285294' do
# Click -9, expand up, select 1 add and verify comment # Click -9, expand up, select 1 add and verify comment
page.within('[data-path="files/ruby/popen.rb"]') do page.within find_by_scrolling('[data-path="files/ruby/popen.rb"]') do
all('.js-unfold-all')[0].click all('.js-unfold-all')[0].click
end end
click_diff_line(find('div[data-path="files/ruby/popen.rb"] .left-side a[data-linenumber="9"]').find(:xpath, '../..'), 'left') click_diff_line(find('div[data-path="files/ruby/popen.rb"] .left-side a[data-linenumber="9"]').find(:xpath, '../..'), 'left')
...@@ -148,7 +153,7 @@ RSpec.describe 'User comments on a diff', :js do ...@@ -148,7 +153,7 @@ RSpec.describe 'User comments on a diff', :js do
it 'allows comments on previously hidden lines the middle of a file' do it 'allows comments on previously hidden lines the middle of a file' do
# Click 27, expand up, select 18, add and verify comment # Click 27, expand up, select 18, add and verify comment
page.within('[data-path="files/ruby/popen.rb"]') do page.within find_by_scrolling('[data-path="files/ruby/popen.rb"]') do
all('.js-unfold-all')[1].click all('.js-unfold-all')[1].click
end end
click_diff_line(find('div[data-path="files/ruby/popen.rb"] .left-side a[data-linenumber="21"]').find(:xpath, '../..'), 'left') click_diff_line(find('div[data-path="files/ruby/popen.rb"] .left-side a[data-linenumber="21"]').find(:xpath, '../..'), 'left')
...@@ -157,7 +162,7 @@ RSpec.describe 'User comments on a diff', :js do ...@@ -157,7 +162,7 @@ RSpec.describe 'User comments on a diff', :js do
it 'allows comments on previously hidden lines at the bottom of a file' do it 'allows comments on previously hidden lines at the bottom of a file' do
# Click +28, expand down, select 37 add and verify comment # Click +28, expand down, select 37 add and verify comment
page.within('[data-path="files/ruby/popen.rb"]') do page.within find_by_scrolling('[data-path="files/ruby/popen.rb"]') do
all('.js-unfold-down:not([disabled])')[1].click all('.js-unfold-down:not([disabled])')[1].click
end end
click_diff_line(find('div[data-path="files/ruby/popen.rb"] .left-side a[data-linenumber="30"]').find(:xpath, '../..'), 'left') click_diff_line(find('div[data-path="files/ruby/popen.rb"] .left-side a[data-linenumber="30"]').find(:xpath, '../..'), 'left')
...@@ -198,7 +203,7 @@ RSpec.describe 'User comments on a diff', :js do ...@@ -198,7 +203,7 @@ RSpec.describe 'User comments on a diff', :js do
context 'when editing comments' do context 'when editing comments' do
it 'edits a comment' do it 'edits a comment' do
click_diff_line(find("[id='#{sample_commit.line_code}']")) click_diff_line(find_by_scrolling("[id='#{sample_commit.line_code}']"))
page.within('.js-discussion-note-form') do page.within('.js-discussion-note-form') do
fill_in(:note_note, with: 'Line is wrong') fill_in(:note_note, with: 'Line is wrong')
...@@ -224,7 +229,7 @@ RSpec.describe 'User comments on a diff', :js do ...@@ -224,7 +229,7 @@ RSpec.describe 'User comments on a diff', :js do
context 'when deleting comments' do context 'when deleting comments' do
it 'deletes a comment' do it 'deletes a comment' do
click_diff_line(find("[id='#{sample_commit.line_code}']")) click_diff_line(find_by_scrolling("[id='#{sample_commit.line_code}']"))
page.within('.js-discussion-note-form') do page.within('.js-discussion-note-form') do
fill_in(:note_note, with: 'Line is wrong') fill_in(:note_note, with: 'Line is wrong')
......
...@@ -7,7 +7,7 @@ RSpec.describe 'User expands diff', :js do ...@@ -7,7 +7,7 @@ RSpec.describe 'User expands diff', :js do
let(:merge_request) { create(:merge_request, source_branch: 'expand-collapse-files', source_project: project, target_project: project) } let(:merge_request) { create(:merge_request, source_branch: 'expand-collapse-files', source_project: project, target_project: project) }
before do before do
allow(Gitlab::CurrentSettings).to receive(:diff_max_patch_bytes).and_return(100.kilobytes) allow(Gitlab::CurrentSettings).to receive(:diff_max_patch_bytes).and_return(100.bytes)
visit(diffs_project_merge_request_path(project, merge_request)) visit(diffs_project_merge_request_path(project, merge_request))
...@@ -15,7 +15,7 @@ RSpec.describe 'User expands diff', :js do ...@@ -15,7 +15,7 @@ RSpec.describe 'User expands diff', :js do
end end
it 'allows user to expand diff' do it 'allows user to expand diff' do
page.within find('[id="19763941ab80e8c09871c0a425f0560d9053bcb3"]') do page.within find("[id='4c76a1271e41072d7da9fe40bf0f79f7384d472a']") do
find('[data-testid="expand-button"]').click find('[data-testid="expand-button"]').click
wait_for_requests wait_for_requests
......
...@@ -15,16 +15,14 @@ RSpec.describe 'Batch diffs', :js do ...@@ -15,16 +15,14 @@ RSpec.describe 'Batch diffs', :js do
visit diffs_project_merge_request_path(merge_request.project, merge_request) visit diffs_project_merge_request_path(merge_request.project, merge_request)
wait_for_requests wait_for_requests
# Add discussion to first line of first file click_diff_line(get_first_diff.find('[data-testid="left-side"]', match: :first))
click_diff_line(find('.diff-file.file-holder:first-of-type .line_holder .left-side:first-of-type')) page.within get_first_diff.find('.js-discussion-note-form') do
page.within('.js-discussion-note-form') do
fill_in('note_note', with: 'First Line Comment') fill_in('note_note', with: 'First Line Comment')
click_button('Add comment now') click_button('Add comment now')
end end
# Add discussion to first line of last file click_diff_line(get_second_diff.find('[data-testid="left-side"]', match: :first))
click_diff_line(find('.diff-file.file-holder:last-of-type .line_holder .left-side:first-of-type')) page.within get_second_diff.find('.js-discussion-note-form') do
page.within('.js-discussion-note-form') do
fill_in('note_note', with: 'Last Line Comment') fill_in('note_note', with: 'Last Line Comment')
click_button('Add comment now') click_button('Add comment now')
end end
...@@ -36,17 +34,14 @@ RSpec.describe 'Batch diffs', :js do ...@@ -36,17 +34,14 @@ RSpec.describe 'Batch diffs', :js do
# Reload so we know the discussions are persisting across batch loads # Reload so we know the discussions are persisting across batch loads
visit page.current_url visit page.current_url
# Wait for JS to settle
wait_for_requests wait_for_requests
expect(page).to have_selector('.diff-files-holder .file-holder', count: 39)
# Confirm discussions are applied to appropriate files (should be contained in multiple diff pages) # Confirm discussions are applied to appropriate files (should be contained in multiple diff pages)
page.within('.diff-file.file-holder:first-of-type .notes .timeline-entry .note .note-text') do page.within get_first_diff.find('.notes .timeline-entry .note .note-text') do
expect(page).to have_content('First Line Comment') expect(page).to have_content('First Line Comment')
end end
page.within('.diff-file.file-holder:last-of-type .notes .timeline-entry .note .note-text') do page.within get_second_diff.find('.notes .timeline-entry .note .note-text') do
expect(page).to have_content('Last Line Comment') expect(page).to have_content('Last Line Comment')
end end
end end
...@@ -54,7 +49,7 @@ RSpec.describe 'Batch diffs', :js do ...@@ -54,7 +49,7 @@ RSpec.describe 'Batch diffs', :js do
context 'when user visits a URL with a link directly to to a discussion' do context 'when user visits a URL with a link directly to to a discussion' do
context 'which is in the first batched page of diffs' do context 'which is in the first batched page of diffs' do
it 'scrolls to the correct discussion' do it 'scrolls to the correct discussion' do
page.within('.diff-file.file-holder:first-of-type') do page.within get_first_diff do
click_link('just now') click_link('just now')
end end
...@@ -63,15 +58,15 @@ RSpec.describe 'Batch diffs', :js do ...@@ -63,15 +58,15 @@ RSpec.describe 'Batch diffs', :js do
wait_for_requests wait_for_requests
# Confirm scrolled to correct UI element # Confirm scrolled to correct UI element
expect(page.find('.diff-file.file-holder:first-of-type .discussion-notes .timeline-entry li.note[id]').obscured?).to be_falsey expect(get_first_diff.find('.discussion-notes .timeline-entry li.note[id]').obscured?).to be_falsey
expect(page.find('.diff-file.file-holder:last-of-type .discussion-notes .timeline-entry li.note[id]').obscured?).to be_truthy expect(get_second_diff.find('.discussion-notes .timeline-entry li.note[id]').obscured?).to be_truthy
end end
end end
context 'which is in at least page 2 of the batched pages of diffs' do context 'which is in at least page 2 of the batched pages of diffs' do
it 'scrolls to the correct discussion', it 'scrolls to the correct discussion',
quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/293814' } do quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/293814' } do
page.within('.diff-file.file-holder:last-of-type') do page.within get_first_diff do
click_link('just now') click_link('just now')
end end
...@@ -80,8 +75,8 @@ RSpec.describe 'Batch diffs', :js do ...@@ -80,8 +75,8 @@ RSpec.describe 'Batch diffs', :js do
wait_for_requests wait_for_requests
# Confirm scrolled to correct UI element # Confirm scrolled to correct UI element
expect(page.find('.diff-file.file-holder:first-of-type .discussion-notes .timeline-entry li.note[id]').obscured?).to be_truthy expect(get_first_diff.find('.discussion-notes .timeline-entry li.note[id]').obscured?).to be_truthy
expect(page.find('.diff-file.file-holder:last-of-type .discussion-notes .timeline-entry li.note[id]').obscured?).to be_falsey expect(get_second_diff.find('.discussion-notes .timeline-entry li.note[id]').obscured?).to be_falsey
end end
end end
end end
...@@ -95,15 +90,21 @@ RSpec.describe 'Batch diffs', :js do ...@@ -95,15 +90,21 @@ RSpec.describe 'Batch diffs', :js do
end end
it 'has the correct discussions applied to files across batched pages' do it 'has the correct discussions applied to files across batched pages' do
expect(page).to have_selector('.diff-files-holder .file-holder', count: 39) page.within get_first_diff.find('.notes .timeline-entry .note .note-text') do
page.within('.diff-file.file-holder:first-of-type .notes .timeline-entry .note .note-text') do
expect(page).to have_content('First Line Comment') expect(page).to have_content('First Line Comment')
end end
page.within('.diff-file.file-holder:last-of-type .notes .timeline-entry .note .note-text') do page.within get_second_diff.find('.notes .timeline-entry .note .note-text') do
expect(page).to have_content('Last Line Comment') expect(page).to have_content('Last Line Comment')
end end
end end
end end
def get_first_diff
find('#a9b6f940524f646951cc28d954aa41f814f95d4f')
end
def get_second_diff
find('#b285a86891571c7fdbf1f82e840816079de1cc8b')
end
end end
...@@ -6,6 +6,7 @@ include Spec::Support::Helpers::ModalHelpers # rubocop:disable Style/MixinUsage ...@@ -6,6 +6,7 @@ include Spec::Support::Helpers::ModalHelpers # rubocop:disable Style/MixinUsage
RSpec.describe 'Merge request > User sees avatars on diff notes', :js do RSpec.describe 'Merge request > User sees avatars on diff notes', :js do
include NoteInteractionHelpers include NoteInteractionHelpers
include Spec::Support::Helpers::ModalHelpers include Spec::Support::Helpers::ModalHelpers
include MergeRequestDiffHelpers
let(:project) { create(:project, :public, :repository) } let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator } let(:user) { project.creator }
...@@ -135,6 +136,7 @@ RSpec.describe 'Merge request > User sees avatars on diff notes', :js do ...@@ -135,6 +136,7 @@ RSpec.describe 'Merge request > User sees avatars on diff notes', :js do
end end
it 'adds avatar when commenting' do it 'adds avatar when commenting' do
find_by_scrolling('[data-discussion-id]', match: :first)
find_field('Reply…', match: :first).click find_field('Reply…', match: :first).click
page.within '.js-discussion-note-form' do page.within '.js-discussion-note-form' do
...@@ -154,6 +156,7 @@ RSpec.describe 'Merge request > User sees avatars on diff notes', :js do ...@@ -154,6 +156,7 @@ RSpec.describe 'Merge request > User sees avatars on diff notes', :js do
it 'adds multiple comments' do it 'adds multiple comments' do
3.times do 3.times do
find_by_scrolling('[data-discussion-id]', match: :first)
find_field('Reply…', match: :first).click find_field('Reply…', match: :first).click
page.within '.js-discussion-note-form' do page.within '.js-discussion-note-form' do
...@@ -192,7 +195,7 @@ RSpec.describe 'Merge request > User sees avatars on diff notes', :js do ...@@ -192,7 +195,7 @@ RSpec.describe 'Merge request > User sees avatars on diff notes', :js do
end end
def find_line(line_code) def find_line(line_code)
line = find("[id='#{line_code}']") line = find_by_scrolling("[id='#{line_code}']")
line = line.find(:xpath, 'preceding-sibling::*[1][self::td]/preceding-sibling::*[1][self::td]') if line.tag_name == 'td' line = line.find(:xpath, 'preceding-sibling::*[1][self::td]/preceding-sibling::*[1][self::td]') if line.tag_name == 'td'
line line
end end
......
...@@ -5,6 +5,7 @@ require 'spec_helper' ...@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe 'Merge request > User sees diff', :js do RSpec.describe 'Merge request > User sees diff', :js do
include ProjectForksHelper include ProjectForksHelper
include RepoHelpers include RepoHelpers
include MergeRequestDiffHelpers
let(:project) { create(:project, :public, :repository) } let(:project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, source_project: project) } let(:merge_request) { create(:merge_request, source_project: project) }
...@@ -58,12 +59,12 @@ RSpec.describe 'Merge request > User sees diff', :js do ...@@ -58,12 +59,12 @@ RSpec.describe 'Merge request > User sees diff', :js do
let(:changelog_id) { Digest::SHA1.hexdigest("CHANGELOG") } let(:changelog_id) { Digest::SHA1.hexdigest("CHANGELOG") }
context 'as author' do context 'as author' do
it 'shows direct edit link', :sidekiq_might_not_need_inline do it 'contains direct edit link', :sidekiq_might_not_need_inline do
sign_in(author_user) sign_in(author_user)
visit diffs_project_merge_request_path(project, merge_request) visit diffs_project_merge_request_path(project, merge_request)
# Throws `Capybara::Poltergeist::InvalidSelector` if we try to use `#hash` syntax # Throws `Capybara::Poltergeist::InvalidSelector` if we try to use `#hash` syntax
expect(page).to have_selector("[id=\"#{changelog_id}\"] .js-edit-blob", visible: false) expect(page).to have_selector(".js-edit-blob", visible: false)
end end
end end
...@@ -72,6 +73,8 @@ RSpec.describe 'Merge request > User sees diff', :js do ...@@ -72,6 +73,8 @@ RSpec.describe 'Merge request > User sees diff', :js do
sign_in(user) sign_in(user)
visit diffs_project_merge_request_path(project, merge_request) visit diffs_project_merge_request_path(project, merge_request)
find_by_scrolling("[id=\"#{changelog_id}\"]")
# Throws `Capybara::Poltergeist::InvalidSelector` if we try to use `#hash` syntax # Throws `Capybara::Poltergeist::InvalidSelector` if we try to use `#hash` syntax
find("[id=\"#{changelog_id}\"] .js-diff-more-actions").click find("[id=\"#{changelog_id}\"] .js-diff-more-actions").click
find("[id=\"#{changelog_id}\"] .js-edit-blob").click find("[id=\"#{changelog_id}\"] .js-edit-blob").click
...@@ -107,6 +110,7 @@ RSpec.describe 'Merge request > User sees diff', :js do ...@@ -107,6 +110,7 @@ RSpec.describe 'Merge request > User sees diff', :js do
CONTENT CONTENT
file_name = 'xss_file.rs' file_name = 'xss_file.rs'
file_hash = Digest::SHA1.hexdigest(file_name)
create_file('master', file_name, file_content) create_file('master', file_name, file_content)
merge_request = create(:merge_request, source_project: project) merge_request = create(:merge_request, source_project: project)
...@@ -116,6 +120,8 @@ RSpec.describe 'Merge request > User sees diff', :js do ...@@ -116,6 +120,8 @@ RSpec.describe 'Merge request > User sees diff', :js do
visit diffs_project_merge_request_path(project, merge_request) visit diffs_project_merge_request_path(project, merge_request)
find_by_scrolling("[id='#{file_hash}']")
expect(page).to have_text("function foo<input> {") expect(page).to have_text("function foo<input> {")
expect(page).to have_css(".line[lang='rust'] .k") expect(page).to have_css(".line[lang='rust'] .k")
end end
...@@ -136,7 +142,7 @@ RSpec.describe 'Merge request > User sees diff', :js do ...@@ -136,7 +142,7 @@ RSpec.describe 'Merge request > User sees diff', :js do
end end
context 'when file is an image', :js do context 'when file is an image', :js do
let(:file_name) { 'files/lfs/image.png' } let(:file_name) { 'a/image.png' }
it 'shows an error message' do it 'shows an error message' do
expect(page).not_to have_content('could not be displayed because it is stored in LFS') expect(page).not_to have_content('could not be displayed because it is stored in LFS')
...@@ -144,7 +150,7 @@ RSpec.describe 'Merge request > User sees diff', :js do ...@@ -144,7 +150,7 @@ RSpec.describe 'Merge request > User sees diff', :js do
end end
context 'when file is not an image' do context 'when file is not an image' do
let(:file_name) { 'files/lfs/ruby.rb' } let(:file_name) { 'a/ruby.rb' }
it 'shows an error message' do it 'shows an error message' do
expect(page).to have_content('This source diff could not be displayed because it is stored in LFS') expect(page).to have_content('This source diff could not be displayed because it is stored in LFS')
...@@ -153,7 +159,14 @@ RSpec.describe 'Merge request > User sees diff', :js do ...@@ -153,7 +159,14 @@ RSpec.describe 'Merge request > User sees diff', :js do
end end
context 'when LFS is not enabled' do context 'when LFS is not enabled' do
let(:file_name) { 'a/lfs_object.iso' }
before do before do
allow(Gitlab.config.lfs).to receive(:disabled).and_return(true)
project.update_attribute(:lfs_enabled, false)
create_file('master', file_name, project.repository.blob_at('master', 'files/lfs/lfs_object.iso').data)
visit diffs_project_merge_request_path(project, merge_request) visit diffs_project_merge_request_path(project, merge_request)
end end
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Merge request > User sees versions', :js do RSpec.describe 'Merge request > User sees versions', :js do
include MergeRequestDiffHelpers
let(:merge_request) do let(:merge_request) do
create(:merge_request).tap do |mr| create(:merge_request).tap do |mr|
mr.merge_request_diff.destroy! mr.merge_request_diff.destroy!
...@@ -27,8 +29,12 @@ RSpec.describe 'Merge request > User sees versions', :js do ...@@ -27,8 +29,12 @@ RSpec.describe 'Merge request > User sees versions', :js do
diff_file_selector = ".diff-file[id='#{file_id}']" diff_file_selector = ".diff-file[id='#{file_id}']"
line_code = "#{file_id}_#{line_code}" line_code = "#{file_id}_#{line_code}"
page.within(diff_file_selector) do page.within find_by_scrolling(diff_file_selector) do
first("[id='#{line_code}']").hover line_code_element = first("[id='#{line_code}']")
# scrolling to element's bottom is required in order for .hover action to work
# otherwise, the element could be hidden underneath a sticky header
scroll_to_elements_bottom(line_code_element)
line_code_element.hover
first("[id='#{line_code}'] [role='button']").click first("[id='#{line_code}'] [role='button']").click
page.within("form[data-line-code='#{line_code}']") do page.within("form[data-line-code='#{line_code}']") do
......
...@@ -34,7 +34,7 @@ RSpec.describe 'User comments on a diff', :js do ...@@ -34,7 +34,7 @@ RSpec.describe 'User comments on a diff', :js do
context 'single suggestion note' do context 'single suggestion note' do
it 'hides suggestion popover' do it 'hides suggestion popover' do
click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']")) click_diff_line(find_by_scrolling("[id='#{sample_compare.changes[1][:line_code]}']"))
expect(page).to have_selector('.diff-suggest-popover') expect(page).to have_selector('.diff-suggest-popover')
...@@ -46,7 +46,7 @@ RSpec.describe 'User comments on a diff', :js do ...@@ -46,7 +46,7 @@ RSpec.describe 'User comments on a diff', :js do
end end
it 'suggestion is presented' do it 'suggestion is presented' do
click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']")) click_diff_line(find_by_scrolling("[id='#{sample_compare.changes[1][:line_code]}']"))
page.within('.js-discussion-note-form') do page.within('.js-discussion-note-form') do
fill_in('note_note', with: "```suggestion\n# change to a comment\n```") fill_in('note_note', with: "```suggestion\n# change to a comment\n```")
...@@ -74,7 +74,7 @@ RSpec.describe 'User comments on a diff', :js do ...@@ -74,7 +74,7 @@ RSpec.describe 'User comments on a diff', :js do
end end
it 'allows suggestions in replies' do it 'allows suggestions in replies' do
click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']")) click_diff_line(find_by_scrolling("[id='#{sample_compare.changes[1][:line_code]}']"))
page.within('.js-discussion-note-form') do page.within('.js-discussion-note-form') do
fill_in('note_note', with: "```suggestion\n# change to a comment\n```") fill_in('note_note', with: "```suggestion\n# change to a comment\n```")
...@@ -91,7 +91,7 @@ RSpec.describe 'User comments on a diff', :js do ...@@ -91,7 +91,7 @@ RSpec.describe 'User comments on a diff', :js do
end end
it 'suggestion is appliable' do it 'suggestion is appliable' do
click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']")) click_diff_line(find_by_scrolling("[id='#{sample_compare.changes[1][:line_code]}']"))
page.within('.js-discussion-note-form') do page.within('.js-discussion-note-form') do
fill_in('note_note', with: "```suggestion\n# change to a comment\n```") fill_in('note_note', with: "```suggestion\n# change to a comment\n```")
...@@ -273,7 +273,7 @@ RSpec.describe 'User comments on a diff', :js do ...@@ -273,7 +273,7 @@ RSpec.describe 'User comments on a diff', :js do
context 'multiple suggestions in a single note' do context 'multiple suggestions in a single note' do
it 'suggestions are presented', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/258989' do it 'suggestions are presented', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/258989' do
click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']")) click_diff_line(find_by_scrolling("[id='#{sample_compare.changes[1][:line_code]}']"))
page.within('.js-discussion-note-form') do page.within('.js-discussion-note-form') do
fill_in('note_note', with: "```suggestion\n# change to a comment\n```\n```suggestion:-2\n# or that\n# heh\n```") fill_in('note_note', with: "```suggestion\n# change to a comment\n```\n```suggestion:-2\n# or that\n# heh\n```")
...@@ -316,7 +316,7 @@ RSpec.describe 'User comments on a diff', :js do ...@@ -316,7 +316,7 @@ RSpec.describe 'User comments on a diff', :js do
context 'multi-line suggestions' do context 'multi-line suggestions' do
before do before do
click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']")) click_diff_line(find_by_scrolling("[id='#{sample_compare.changes[1][:line_code]}']"))
page.within('.js-discussion-note-form') do page.within('.js-discussion-note-form') do
fill_in('note_note', with: "```suggestion:-3+5\n# change to a\n# comment\n# with\n# broken\n# lines\n```") fill_in('note_note', with: "```suggestion:-3+5\n# change to a\n# comment\n# with\n# broken\n# lines\n```")
......
...@@ -69,6 +69,12 @@ describe('diffs/components/app', () => { ...@@ -69,6 +69,12 @@ describe('diffs/components/app', () => {
}, },
provide, provide,
store, store,
stubs: {
DynamicScroller: {
template: `<div><slot :item="$store.state.diffs.diffFiles[0]"></slot></div>`,
},
DynamicScrollerItem: true,
},
}); });
} }
......
...@@ -202,7 +202,7 @@ describe('DiffsStoreActions', () => { ...@@ -202,7 +202,7 @@ describe('DiffsStoreActions', () => {
testAction( testAction(
fetchDiffFilesBatch, fetchDiffFilesBatch,
{}, {},
{ endpointBatch, diffViewType: 'inline' }, { endpointBatch, diffViewType: 'inline', diffFiles: [] },
[ [
{ type: types.SET_BATCH_LOADING_STATE, payload: 'loading' }, { type: types.SET_BATCH_LOADING_STATE, payload: 'loading' },
{ type: types.SET_RETRIEVING_BATCHES, payload: true }, { type: types.SET_RETRIEVING_BATCHES, payload: true },
......
...@@ -143,7 +143,7 @@ describe('Discussion navigation mixin', () => { ...@@ -143,7 +143,7 @@ describe('Discussion navigation mixin', () => {
it('scrolls to element', () => { it('scrolls to element', () => {
expect(utils.scrollToElement).toHaveBeenCalledWith( expect(utils.scrollToElement).toHaveBeenCalledWith(
findDiscussion('div.discussion', expected), findDiscussion('div.discussion', expected),
{ behavior: 'smooth' }, { behavior: 'auto' },
); );
}); });
}); });
...@@ -171,7 +171,7 @@ describe('Discussion navigation mixin', () => { ...@@ -171,7 +171,7 @@ describe('Discussion navigation mixin', () => {
expect(utils.scrollToElementWithContext).toHaveBeenCalledWith( expect(utils.scrollToElementWithContext).toHaveBeenCalledWith(
findDiscussion('ul.notes', expected), findDiscussion('ul.notes', expected),
{ behavior: 'smooth' }, { behavior: 'auto' },
); );
}); });
}); });
...@@ -212,21 +212,15 @@ describe('Discussion navigation mixin', () => { ...@@ -212,21 +212,15 @@ describe('Discussion navigation mixin', () => {
it('scrolls to discussion', () => { it('scrolls to discussion', () => {
expect(utils.scrollToElement).toHaveBeenCalledWith( expect(utils.scrollToElement).toHaveBeenCalledWith(
findDiscussion('div.discussion', expected), findDiscussion('div.discussion', expected),
{ behavior: 'smooth' }, { behavior: 'auto' },
); );
}); });
}); });
}); });
}); });
describe.each` describe('virtual scrolling feature', () => {
diffsVirtualScrolling
${false}
${true}
`('virtual scrolling feature is $diffsVirtualScrolling', ({ diffsVirtualScrolling }) => {
beforeEach(() => { beforeEach(() => {
window.gon = { features: { diffsVirtualScrolling } };
jest.spyOn(store, 'dispatch'); jest.spyOn(store, 'dispatch');
store.state.notes.currentDiscussionId = 'a'; store.state.notes.currentDiscussionId = 'a';
...@@ -238,22 +232,22 @@ describe('Discussion navigation mixin', () => { ...@@ -238,22 +232,22 @@ describe('Discussion navigation mixin', () => {
window.location.hash = ''; window.location.hash = '';
}); });
it('resets location hash if diffsVirtualScrolling flag is true', async () => { it('resets location hash', async () => {
wrapper.vm.jumpToNextDiscussion(); wrapper.vm.jumpToNextDiscussion();
await nextTick(); await nextTick();
expect(window.location.hash).toBe(diffsVirtualScrolling ? '' : '#test'); expect(window.location.hash).toBe('');
}); });
it.each` it.each`
tabValue | hashValue tabValue
${'diffs'} | ${false} ${'diffs'}
${'show'} | ${!diffsVirtualScrolling} ${'show'}
${'other'} | ${!diffsVirtualScrolling} ${'other'}
`( `(
'calls scrollToFile with setHash as $hashValue when the tab is $tabValue', 'calls scrollToFile with setHash as $hashValue when the tab is $tabValue',
async ({ hashValue, tabValue }) => { async ({ tabValue }) => {
window.mrTabs.currentAction = tabValue; window.mrTabs.currentAction = tabValue;
wrapper.vm.jumpToNextDiscussion(); wrapper.vm.jumpToNextDiscussion();
...@@ -262,7 +256,6 @@ describe('Discussion navigation mixin', () => { ...@@ -262,7 +256,6 @@ describe('Discussion navigation mixin', () => {
expect(store.dispatch).toHaveBeenCalledWith('diffs/scrollToFile', { expect(store.dispatch).toHaveBeenCalledWith('diffs/scrollToFile', {
path: 'test.js', path: 'test.js',
setHash: hashValue,
}); });
}, },
); );
......
...@@ -294,8 +294,6 @@ RSpec.configure do |config| ...@@ -294,8 +294,6 @@ RSpec.configure do |config|
# See https://gitlab.com/gitlab-org/gitlab/-/issues/33867 # See https://gitlab.com/gitlab-org/gitlab/-/issues/33867
stub_feature_flags(file_identifier_hash: false) stub_feature_flags(file_identifier_hash: false)
stub_feature_flags(diffs_virtual_scrolling: false)
# The following `vue_issues_list` stub can be removed # The following `vue_issues_list` stub can be removed
# once the Vue issues page has feature parity with the current Haml page # once the Vue issues page has feature parity with the current Haml page
stub_feature_flags(vue_issues_list: false) stub_feature_flags(vue_issues_list: false)
......
# frozen_string_literal: true # frozen_string_literal: true
module MergeRequestDiffHelpers module MergeRequestDiffHelpers
PageEndReached = Class.new(StandardError)
def click_diff_line(line_holder, diff_side = nil) def click_diff_line(line_holder, diff_side = nil)
line = get_line_components(line_holder, diff_side) line = get_line_components(line_holder, diff_side)
scroll_to_elements_bottom(line_holder)
line_holder.hover line_holder.hover
line[:num].find('.js-add-diff-note-button').click line[:num].find('.js-add-diff-note-button').click
end end
...@@ -27,4 +30,55 @@ module MergeRequestDiffHelpers ...@@ -27,4 +30,55 @@ module MergeRequestDiffHelpers
line_holder.find('.diff-line-num', match: :first) line_holder.find('.diff-line-num', match: :first)
{ content: line_holder.all('.line_content')[side_index], num: line_holder.all('.diff-line-num')[side_index] } { content: line_holder.all('.line_content')[side_index], num: line_holder.all('.diff-line-num')[side_index] }
end end
def has_reached_page_end
evaluate_script("(window.innerHeight + window.scrollY) >= document.body.offsetHeight")
end
def scroll_to_elements_bottom(element)
evaluate_script("(function(el) {
window.scrollBy(0, el.getBoundingClientRect().bottom - window.innerHeight);
})(arguments[0]);", element.native)
end
# we're not using Capybara's .obscured here because it also checks if the element is clickable
def within_viewport?(element)
evaluate_script("(function(el) {
var rect = el.getBoundingClientRect();
return (
rect.bottom >= 0 &&
rect.right >= 0 &&
rect.top <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.left <= (window.innerWidth || document.documentElement.clientWidth)
);
})(arguments[0]);", element.native)
end
def find_within_viewport(selector, **options)
begin
element = find(selector, **options, wait: 2)
rescue Capybara::ElementNotFound
return
end
return element if within_viewport?(element)
nil
end
def find_by_scrolling(selector, **options)
element = find_within_viewport(selector, **options)
return element if element
page.execute_script "window.scrollTo(0,0)"
until element
if has_reached_page_end
raise PageEndReached, "Failed to find any elements matching a selector '#{selector}' by scrolling. Page end reached."
end
page.execute_script "window.scrollBy(0,window.innerHeight/1.5)"
element = find_within_viewport(selector, **options)
end
element
end
end end
# frozen_string_literal: true # frozen_string_literal: true
module NoteInteractionHelpers module NoteInteractionHelpers
include MergeRequestDiffHelpers
def open_more_actions_dropdown(note) def open_more_actions_dropdown(note)
note_element = find("#note_#{note.id}") note_element = find_by_scrolling("#note_#{note.id}")
note_element.find('.more-actions-toggle').click note_element.find('.more-actions-toggle').click
note_element.find('.more-actions .dropdown-menu li', match: :first) note_element.find('.more-actions .dropdown-menu li', match: :first)
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
RSpec.shared_examples 'comment on merge request file' do RSpec.shared_examples 'comment on merge request file' do
it 'adds a comment' do it 'adds a comment' do
click_diff_line(find("[id='#{sample_commit.line_code}']")) click_diff_line(find_by_scrolling("[id='#{sample_commit.line_code}']"))
page.within('.js-discussion-note-form') do page.within('.js-discussion-note-form') do
fill_in(:note_note, with: 'Line is wrong') fill_in(:note_note, with: 'Line is wrong')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment