diff options
107 files changed, 434 insertions, 208 deletions
diff --git a/.eslintrc.yml b/.eslintrc.yml index b9c5973d7ac..77b1b72fe68 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -69,5 +69,3 @@ rules: FunctionExpression: parameters: 1 body: 1 - ## Destructuring: https://eslint.org/docs/rules/prefer-destructuring - prefer-destructuring: off diff --git a/app/assets/javascripts/ajax_loading_spinner.js b/app/assets/javascripts/ajax_loading_spinner.js index bd08308904c..54e86f329e4 100644 --- a/app/assets/javascripts/ajax_loading_spinner.js +++ b/app/assets/javascripts/ajax_loading_spinner.js @@ -26,7 +26,7 @@ export default class AjaxLoadingSpinner { } static toggleLoadingIcon(iconElement) { - const classList = iconElement.classList; + const { classList } = iconElement; classList.toggle(iconElement.dataset.icon); classList.toggle('fa-spinner'); classList.toggle('fa-spin'); diff --git a/app/assets/javascripts/behaviors/copy_to_clipboard.js b/app/assets/javascripts/behaviors/copy_to_clipboard.js index 75834ba351d..00419e80cbb 100644 --- a/app/assets/javascripts/behaviors/copy_to_clipboard.js +++ b/app/assets/javascripts/behaviors/copy_to_clipboard.js @@ -52,7 +52,7 @@ export default function initCopyToClipboard() { * data types to the intended values. */ $(document).on('copy', 'body > textarea[readonly]', (e) => { - const clipboardData = e.originalEvent.clipboardData; + const { clipboardData } = e.originalEvent; if (!clipboardData) return; const text = e.target.value; diff --git a/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js b/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js index 9745e37acce..5d7a3bed301 100644 --- a/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js +++ b/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js @@ -321,7 +321,7 @@ export class CopyAsGFM { } static copyAsGFM(e, transformer) { - const clipboardData = e.originalEvent.clipboardData; + const { clipboardData } = e.originalEvent; if (!clipboardData) return; const documentFragment = getSelectedFragment(); @@ -338,7 +338,7 @@ export class CopyAsGFM { } static pasteGFM(e) { - const clipboardData = e.originalEvent.clipboardData; + const { clipboardData } = e.originalEvent; if (!clipboardData) return; const text = clipboardData.getData('text/plain'); diff --git a/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js b/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js index 766039404ce..7986287f7e7 100644 --- a/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js +++ b/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js @@ -84,7 +84,7 @@ class BalsamiqViewer { renderTemplate(preview) { const resource = this.getResource(preview.resourceID); const name = BalsamiqViewer.parseTitle(resource); - const image = preview.image; + const { image } = preview; const template = PREVIEW_TEMPLATE({ name, diff --git a/app/assets/javascripts/blob/balsamiq_viewer.js b/app/assets/javascripts/blob/balsamiq_viewer.js index 06ef86ecb77..b88e69a07bf 100644 --- a/app/assets/javascripts/blob/balsamiq_viewer.js +++ b/app/assets/javascripts/blob/balsamiq_viewer.js @@ -12,7 +12,7 @@ export default function loadBalsamiqFile() { if (!(viewer instanceof Element)) return; - const endpoint = viewer.dataset.endpoint; + const { endpoint } = viewer.dataset; const balsamiqViewer = new BalsamiqViewer(viewer); balsamiqViewer.loadFile(endpoint).catch(onError); diff --git a/app/assets/javascripts/blob/stl_viewer.js b/app/assets/javascripts/blob/stl_viewer.js index 63236b6477f..339906adc34 100644 --- a/app/assets/javascripts/blob/stl_viewer.js +++ b/app/assets/javascripts/blob/stl_viewer.js @@ -5,7 +5,7 @@ export default () => { [].slice.call(document.querySelectorAll('.js-material-changer')).forEach((el) => { el.addEventListener('click', (e) => { - const target = e.target; + const { target } = e; e.preventDefault(); diff --git a/app/assets/javascripts/boards/components/sidebar/remove_issue.vue b/app/assets/javascripts/boards/components/sidebar/remove_issue.vue index 806e038a95f..55278626ffc 100644 --- a/app/assets/javascripts/boards/components/sidebar/remove_issue.vue +++ b/app/assets/javascripts/boards/components/sidebar/remove_issue.vue @@ -23,7 +23,7 @@ }, methods: { removeIssue() { - const issue = this.issue; + const { issue } = this; const lists = issue.getLists(); const listLabelIds = lists.map(list => list.label.id); diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js index 2d9141bf71c..751a66f89c6 100644 --- a/app/assets/javascripts/boards/index.js +++ b/app/assets/javascripts/boards/index.js @@ -121,7 +121,7 @@ export default () => { this.filterManager.updateTokens(); }, updateDetailIssue(newIssue) { - const sidebarInfoEndpoint = newIssue.sidebarInfoEndpoint; + const { sidebarInfoEndpoint } = newIssue; if (sidebarInfoEndpoint && newIssue.subscribed === undefined) { newIssue.setFetchingState('subscriptions', true); BoardService.getIssueInfo(sidebarInfoEndpoint) @@ -144,7 +144,7 @@ export default () => { Store.detail.issue = {}; }, toggleSubscription(id) { - const issue = Store.detail.issue; + const { issue } = Store.detail; if (issue.id === id && issue.toggleSubscriptionEndpoint) { issue.setFetchingState('subscriptions', true); BoardService.toggleIssueSubscription(issue.toggleSubscriptionEndpoint) diff --git a/app/assets/javascripts/boards/stores/modal_store.js b/app/assets/javascripts/boards/stores/modal_store.js index a4220cd840d..0d9ac367a70 100644 --- a/app/assets/javascripts/boards/stores/modal_store.js +++ b/app/assets/javascripts/boards/stores/modal_store.js @@ -26,7 +26,7 @@ class ModalStore { toggleIssue(issueObj) { const issue = issueObj; - const selected = issue.selected; + const { selected } = issue; issue.selected = !selected; diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js index e42a3632e79..8139aa69fc7 100644 --- a/app/assets/javascripts/clusters/clusters_bundle.js +++ b/app/assets/javascripts/clusters/clusters_bundle.js @@ -81,7 +81,7 @@ export default class Clusters { } initApplications() { - const store = this.store; + const { store } = this; const el = document.querySelector('#js-cluster-applications'); this.applications = new Vue({ diff --git a/app/assets/javascripts/commit/image_file.js b/app/assets/javascripts/commit/image_file.js index 2d180e9903a..410580b4c25 100644 --- a/app/assets/javascripts/commit/image_file.js +++ b/app/assets/javascripts/commit/image_file.js @@ -122,7 +122,7 @@ export default class ImageFile { return $('.swipe.view', this.file).each((function(_this) { return function(index, view) { var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding, ref; - ref = _this.prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1]; + ref = _this.prepareFrames(view), [maxWidth, maxHeight] = ref; $swipeFrame = $('.swipe-frame', view); $swipeWrap = $('.swipe-wrap', view); $swipeBar = $('.swipe-bar', view); @@ -159,7 +159,7 @@ export default class ImageFile { return $('.onion-skin.view', this.file).each((function(_this) { return function(index, view) { var $frame, $track, $dragger, $frameAdded, framePadding, ref, dragging = false; - ref = _this.prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1]; + ref = _this.prepareFrames(view), [maxWidth, maxHeight] = ref; $frame = $('.onion-skin-frame', view); $frameAdded = $('.frame.added', view); $track = $('.drag-track', view); diff --git a/app/assets/javascripts/create_merge_request_dropdown.js b/app/assets/javascripts/create_merge_request_dropdown.js index f77a5730b77..02aa507ba03 100644 --- a/app/assets/javascripts/create_merge_request_dropdown.js +++ b/app/assets/javascripts/create_merge_request_dropdown.js @@ -281,7 +281,7 @@ export default class CreateMergeRequestDropdown { if (event.target === this.branchInput) { target = 'branch'; - value = this.branchInput.value; + ({ value } = this.branchInput); } else if (event.target === this.refInput) { target = 'ref'; value = diff --git a/app/assets/javascripts/diff_notes/components/diff_note_avatars.js b/app/assets/javascripts/diff_notes/components/diff_note_avatars.js index 40f7c2fe5f3..5528d2a542b 100644 --- a/app/assets/javascripts/diff_notes/components/diff_note_avatars.js +++ b/app/assets/javascripts/diff_notes/components/diff_note_avatars.js @@ -111,7 +111,7 @@ const DiffNoteAvatars = Vue.extend({ }); }, addNoCommentClass() { - const notesCount = this.notesCount; + const { notesCount } = this; $(this.$el).closest('.js-avatar-container') .toggleClass('no-comment-btn', notesCount > 0) diff --git a/app/assets/javascripts/diff_notes/components/jump_to_discussion.js b/app/assets/javascripts/diff_notes/components/jump_to_discussion.js index 66b20cc8739..2b893e35b6d 100644 --- a/app/assets/javascripts/diff_notes/components/jump_to_discussion.js +++ b/app/assets/javascripts/diff_notes/components/jump_to_discussion.js @@ -73,7 +73,7 @@ const JumpToDiscussion = Vue.extend({ }).toArray(); }; - const discussions = this.discussions; + const { discussions } = this; if (activeTab === 'diffs') { discussionsSelector = '.diffs .notes[data-discussion-id]'; diff --git a/app/assets/javascripts/diff_notes/diff_notes_bundle.js b/app/assets/javascripts/diff_notes/diff_notes_bundle.js index a9800a11644..7dcf3594471 100644 --- a/app/assets/javascripts/diff_notes/diff_notes_bundle.js +++ b/app/assets/javascripts/diff_notes/diff_notes_bundle.js @@ -18,7 +18,7 @@ import './components/new_issue_for_discussion'; export default () => { const projectPathHolder = document.querySelector('.merge-request') || document.querySelector('.commit-box'); - const projectPath = projectPathHolder.dataset.projectPath; + const { projectPath } = projectPathHolder.dataset; const COMPONENT_SELECTOR = 'resolve-btn, resolve-discussion-btn, jump-to-discussion, comment-and-resolve-btn, new-issue-for-discussion-btn'; diff --git a/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue b/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue index 3193b18becb..7e50a0aed84 100644 --- a/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue +++ b/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue @@ -47,7 +47,7 @@ export default { methods: { ...mapActions(['toggleDiscussion']), getTooltipText(noteData) { - let note = noteData.note; + let { note } = noteData; if (note.length > LENGTH_OF_AVATAR_TOOLTIP) { note = truncate(note, LENGTH_OF_AVATAR_TOOLTIP); diff --git a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue index 05dca0cdd9a..8999fd2ac96 100644 --- a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue +++ b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue @@ -124,7 +124,7 @@ export default { const newLineNumber = this.metaData.newPos || 0; const offset = newLineNumber - oldLineNumber; const bottom = this.isBottom; - const fileHash = this.fileHash; + const { fileHash } = this; const view = this.diffViewType; let unfold = true; let lineNumber = newLineNumber - 1; diff --git a/app/assets/javascripts/diffs/components/parallel_diff_view.vue b/app/assets/javascripts/diffs/components/parallel_diff_view.vue index 2ddf8e6c6ed..60edbcbbda8 100644 --- a/app/assets/javascripts/diffs/components/parallel_diff_view.vue +++ b/app/assets/javascripts/diffs/components/parallel_diff_view.vue @@ -89,7 +89,7 @@ export default { return isLeftExpanded || isRightExpanded; }, getLineCode(line, side) { - const lineCode = side.lineCode; + const { lineCode } = side; if (lineCode) { return lineCode; } diff --git a/app/assets/javascripts/environments/components/environment_item.vue b/app/assets/javascripts/environments/components/environment_item.vue index 866e91057ec..5ecdccf63ad 100644 --- a/app/assets/javascripts/environments/components/environment_item.vue +++ b/app/assets/javascripts/environments/components/environment_item.vue @@ -292,7 +292,7 @@ if (this.model && this.model.last_deployment && this.model.last_deployment.deployable) { - const deployable = this.model.last_deployment.deployable; + const { deployable } = this.model.last_deployment; return `${deployable.name} #${deployable.id}`; } return ''; diff --git a/app/assets/javascripts/environments/stores/environments_store.js b/app/assets/javascripts/environments/stores/environments_store.js index 5f2989ab854..5ce9225a4bb 100644 --- a/app/assets/javascripts/environments/stores/environments_store.js +++ b/app/assets/javascripts/environments/stores/environments_store.js @@ -146,7 +146,7 @@ export default class EnvironmentsStore { * @return {Array} */ updateEnvironmentProp(environment, prop, newValue) { - const environments = this.state.environments; + const { environments } = this.state; const updatedEnvironments = environments.map((env) => { const updateEnv = Object.assign({}, env); @@ -161,7 +161,7 @@ export default class EnvironmentsStore { } getOpenFolders() { - const environments = this.state.environments; + const { environments } = this.state; return environments.filter(env => env.isFolder && env.isOpen); } diff --git a/app/assets/javascripts/filtered_search/dropdown_utils.js b/app/assets/javascripts/filtered_search/dropdown_utils.js index 9bc36c1f9b6..27fff488603 100644 --- a/app/assets/javascripts/filtered_search/dropdown_utils.js +++ b/app/assets/javascripts/filtered_search/dropdown_utils.js @@ -35,7 +35,7 @@ export default class DropdownUtils { // Remove the symbol for filter if (value[0] === filterSymbol) { - symbol = value[0]; + [symbol] = value; value = value.slice(1); } @@ -162,7 +162,7 @@ export default class DropdownUtils { // Determines the full search query (visual tokens + input) static getSearchQuery(untilInput = false) { - const container = FilteredSearchContainer.container; + const { container } = FilteredSearchContainer; const tokens = [].slice.call(container.querySelectorAll('.tokens-container li')); const values = []; @@ -220,7 +220,7 @@ export default class DropdownUtils { } static getInputSelectionPosition(input) { - const selectionStart = input.selectionStart; + const { selectionStart } = input; let inputValue = input.value; // Replace all spaces inside quote marks with underscores // (will continue to match entire string until an end quote is found if any) diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js index d7e1de18d09..296571606d6 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js +++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js @@ -159,7 +159,7 @@ export default class FilteredSearchDropdownManager { load(key, firstLoad = false) { const mappingKey = this.mapping[key]; const glClass = mappingKey.gl; - const element = mappingKey.element; + const { element } = mappingKey; let forceShowList = false; if (!mappingKey.reference) { diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js index cf5ba1e1771..81286c54c4c 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_manager.js +++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js @@ -235,7 +235,7 @@ export default class FilteredSearchManager { checkForEnter(e) { if (e.keyCode === 38 || e.keyCode === 40) { - const selectionStart = this.filteredSearchInput.selectionStart; + const { selectionStart } = this.filteredSearchInput; e.preventDefault(); this.filteredSearchInput.setSelectionRange(selectionStart, selectionStart); @@ -496,7 +496,7 @@ export default class FilteredSearchManager { // Replace underscore with hyphen in the sanitizedkey. // e.g. 'my_reaction' => 'my-reaction' sanitizedKey = sanitizedKey.replace('_', '-'); - const symbol = match.symbol; + const { symbol } = match; let quotationsToUse = ''; if (sanitizedValue.indexOf(' ') !== -1) { diff --git a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js index 600024c21c3..56fe1ab4e90 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js +++ b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js @@ -101,7 +101,7 @@ export default class FilteredSearchVisualTokens { static updateLabelTokenColor(tokenValueContainer, tokenValue) { const filteredSearchInput = FilteredSearchContainer.container.querySelector('.filtered-search'); - const baseEndpoint = filteredSearchInput.dataset.baseEndpoint; + const { baseEndpoint } = filteredSearchInput.dataset; const labelsEndpoint = FilteredSearchVisualTokens.getEndpointWithQueryParams( `${baseEndpoint}/labels.json`, filteredSearchInput.dataset.endpointQueryParams, @@ -215,7 +215,7 @@ export default class FilteredSearchVisualTokens { static addFilterVisualToken(tokenName, tokenValue, canEdit) { const { lastVisualToken, isLastVisualTokenValid } = FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); - const addVisualTokenElement = FilteredSearchVisualTokens.addVisualTokenElement; + const { addVisualTokenElement } = FilteredSearchVisualTokens; if (isLastVisualTokenValid) { addVisualTokenElement(tokenName, tokenValue, false, canEdit); diff --git a/app/assets/javascripts/filtered_search/recent_searches_root.js b/app/assets/javascripts/filtered_search/recent_searches_root.js index f9338b82acf..c1efa9c86f4 100644 --- a/app/assets/javascripts/filtered_search/recent_searches_root.js +++ b/app/assets/javascripts/filtered_search/recent_searches_root.js @@ -29,7 +29,7 @@ class RecentSearchesRoot { } render() { - const state = this.store.state; + const { state } = this.store; this.vm = new Vue({ el: this.wrapperElement, components: { diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js index b0f674f2c05..09186a865e4 100644 --- a/app/assets/javascripts/gfm_auto_complete.js +++ b/app/assets/javascripts/gfm_auto_complete.js @@ -80,7 +80,7 @@ class GfmAutoComplete { let tpl = '/${name} '; let referencePrefix = null; if (value.params.length > 0) { - referencePrefix = value.params[0][0]; + [[referencePrefix]] = value.params; if (/^[@%~]/.test(referencePrefix)) { tpl += '<%- referencePrefix %>'; } @@ -458,7 +458,7 @@ class GfmAutoComplete { static isLoading(data) { let dataToInspect = data; if (data && data.length > 0) { - dataToInspect = data[0]; + [dataToInspect] = data; } const loadingState = GfmAutoComplete.defaultLoadingData[0]; diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js index 45889c2d604..8d231e6c405 100644 --- a/app/assets/javascripts/gl_dropdown.js +++ b/app/assets/javascripts/gl_dropdown.js @@ -613,7 +613,7 @@ GitLabDropdown = (function() { }; GitLabDropdown.prototype.renderItem = function(data, group, index) { - var field, fieldName, html, selected, text, url, value, rowHidden; + var field, html, selected, text, url, value, rowHidden; if (!this.options.renderRow) { value = this.options.id ? this.options.id(data) : data.id; @@ -651,7 +651,7 @@ GitLabDropdown = (function() { html = this.options.renderRow.call(this.options, data, this); } else { if (!selected) { - fieldName = this.options.fieldName; + const { fieldName } = this.options; if (value) { field = this.dropdown.parent().find(`input[name='${fieldName}'][value='${value}']`); @@ -705,7 +705,8 @@ GitLabDropdown = (function() { GitLabDropdown.prototype.highlightTextMatches = function(text, term) { const occurrences = fuzzaldrinPlus.match(text, term); - const indexOf = [].indexOf; + const { indexOf } = []; + return text.split('').map(function(character, i) { if (indexOf.call(occurrences, i) !== -1) { return "<b>" + character + "</b>"; @@ -721,9 +722,9 @@ GitLabDropdown = (function() { }; GitLabDropdown.prototype.rowClicked = function(el) { - var field, fieldName, groupName, isInput, selectedIndex, selectedObject, value, isMarking; + var field, groupName, isInput, selectedIndex, selectedObject, value, isMarking; - fieldName = this.options.fieldName; + const { fieldName } = this.options; isInput = $(this.el).is('input'); if (this.renderedData) { groupName = el.data('group'); diff --git a/app/assets/javascripts/groups/index.js b/app/assets/javascripts/groups/index.js index 57eaac72906..83a9008a94b 100644 --- a/app/assets/javascripts/groups/index.js +++ b/app/assets/javascripts/groups/index.js @@ -29,7 +29,7 @@ export default () => { groupsApp, }, data() { - const dataset = this.$options.el.dataset; + const { dataset } = this.$options.el; const hideProjects = dataset.hideProjects === 'true'; const store = new GroupsStore(hideProjects); const service = new GroupsService(dataset.endpoint); @@ -42,7 +42,7 @@ export default () => { }; }, beforeMount() { - const dataset = this.$options.el.dataset; + const { dataset } = this.$options.el; let groupFilterList = null; const form = document.querySelector(dataset.formSel); const filter = document.querySelector(dataset.filterSel); diff --git a/app/assets/javascripts/ide/components/file_finder/item.vue b/app/assets/javascripts/ide/components/file_finder/item.vue index a4cf3edb981..f5252ce7706 100644 --- a/app/assets/javascripts/ide/components/file_finder/item.vue +++ b/app/assets/javascripts/ide/components/file_finder/item.vue @@ -30,7 +30,7 @@ export default { }, computed: { pathWithEllipsis() { - const path = this.file.path; + const { path } = this.file; return path.length < MAX_PATH_LENGTH ? path diff --git a/app/assets/javascripts/ide/components/new_dropdown/upload.vue b/app/assets/javascripts/ide/components/new_dropdown/upload.vue index 1814924be39..677b282bd61 100644 --- a/app/assets/javascripts/ide/components/new_dropdown/upload.vue +++ b/app/assets/javascripts/ide/components/new_dropdown/upload.vue @@ -23,6 +23,7 @@ let { result } = target; if (!isText) { + // eslint-disable-next-line prefer-destructuring result = result.split('base64,')[1]; } diff --git a/app/assets/javascripts/ide/components/repo_file.vue b/app/assets/javascripts/ide/components/repo_file.vue index c34547fcc60..f490a3a2a39 100644 --- a/app/assets/javascripts/ide/components/repo_file.vue +++ b/app/assets/javascripts/ide/components/repo_file.vue @@ -95,24 +95,53 @@ export default { return this.file.changed || this.file.tempFile || this.file.staged; }, }, + mounted() { + if (this.hasPathAtCurrentRoute()) { + this.scrollIntoView(true); + } + }, updated() { if (this.file.type === 'blob' && this.file.active) { - this.$el.scrollIntoView({ - behavior: 'smooth', - block: 'nearest', - }); + this.scrollIntoView(); } }, methods: { ...mapActions(['toggleTreeOpen']), clickFile() { // Manual Action if a tree is selected/opened - if (this.isTree && this.$router.currentRoute.path === `/project${this.file.url}`) { + if (this.isTree && this.hasUrlAtCurrentRoute()) { this.toggleTreeOpen(this.file.path); } router.push(`/project${this.file.url}`); }, + scrollIntoView(isInit = false) { + const block = isInit && this.isTree ? 'center' : 'nearest'; + + this.$el.scrollIntoView({ + behavior: 'smooth', + block, + }); + }, + hasPathAtCurrentRoute() { + if (!this.$router || !this.$router.currentRoute) { + return false; + } + + // - strip route up to "/-/" and ending "/" + const routePath = this.$router.currentRoute.path + .replace(/^.*?[/]-[/]/g, '') + .replace(/[/]$/g, ''); + + // - strip ending "/" + const filePath = this.file.path + .replace(/[/]$/g, ''); + + return filePath === routePath; + }, + hasUrlAtCurrentRoute() { + return this.$router.currentRoute.path === `/project${this.file.url}`; + }, }, }; </script> diff --git a/app/assets/javascripts/ide/lib/diff/diff_worker.js b/app/assets/javascripts/ide/lib/diff/diff_worker.js index f09930e8158..78b2eab6399 100644 --- a/app/assets/javascripts/ide/lib/diff/diff_worker.js +++ b/app/assets/javascripts/ide/lib/diff/diff_worker.js @@ -2,7 +2,7 @@ import { computeDiff } from './diff'; // eslint-disable-next-line no-restricted-globals self.addEventListener('message', (e) => { - const data = e.data; + const { data } = e; // eslint-disable-next-line no-restricted-globals self.postMessage({ diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js index 74f9c112f5a..29995a29d1a 100644 --- a/app/assets/javascripts/ide/stores/actions/file.js +++ b/app/assets/javascripts/ide/stores/actions/file.js @@ -8,7 +8,7 @@ import { setPageTitle } from '../utils'; import { viewerTypes } from '../../constants'; export const closeFile = ({ commit, state, dispatch }, file) => { - const path = file.path; + const { path } = file; const indexOfClosedFile = state.openFiles.findIndex(f => f.key === file.key); const fileWasActive = file.active; diff --git a/app/assets/javascripts/ide/stores/actions/tree.js b/app/assets/javascripts/ide/stores/actions/tree.js index cc5116413f7..2fbc9990fa2 100644 --- a/app/assets/javascripts/ide/stores/actions/tree.js +++ b/app/assets/javascripts/ide/stores/actions/tree.js @@ -9,6 +9,17 @@ export const toggleTreeOpen = ({ commit }, path) => { commit(types.TOGGLE_TREE_OPEN, path); }; +export const showTreeEntry = ({ commit, dispatch, state }, path) => { + const entry = state.entries[path]; + const parentPath = entry ? entry.parentPath : ''; + + if (parentPath) { + commit(types.SET_TREE_OPEN, parentPath); + + dispatch('showTreeEntry', parentPath); + } +}; + export const handleTreeEntryAction = ({ commit, dispatch }, row) => { if (row.type === 'tree') { dispatch('toggleTreeOpen', row.path); @@ -21,6 +32,8 @@ export const handleTreeEntryAction = ({ commit, dispatch }, row) => { } else { dispatch('getFileData', { path: row.path }); } + + dispatch('showTreeEntry', row.path); }; export const getLastCommitData = ({ state, commit, dispatch }, tree = state) => { diff --git a/app/assets/javascripts/ide/stores/mutation_types.js b/app/assets/javascripts/ide/stores/mutation_types.js index 99b315ac4db..fda606dbf01 100644 --- a/app/assets/javascripts/ide/stores/mutation_types.js +++ b/app/assets/javascripts/ide/stores/mutation_types.js @@ -28,6 +28,7 @@ export const TOGGLE_BRANCH_OPEN = 'TOGGLE_BRANCH_OPEN'; // Tree mutation types export const SET_DIRECTORY_DATA = 'SET_DIRECTORY_DATA'; export const TOGGLE_TREE_OPEN = 'TOGGLE_TREE_OPEN'; +export const SET_TREE_OPEN = 'SET_TREE_OPEN'; export const SET_LAST_COMMIT_URL = 'SET_LAST_COMMIT_URL'; export const CREATE_TREE = 'CREATE_TREE'; export const REMOVE_ALL_CHANGES_FILES = 'REMOVE_ALL_CHANGES_FILES'; diff --git a/app/assets/javascripts/ide/stores/mutations/tree.js b/app/assets/javascripts/ide/stores/mutations/tree.js index 1176c040fb9..2cf34af9274 100644 --- a/app/assets/javascripts/ide/stores/mutations/tree.js +++ b/app/assets/javascripts/ide/stores/mutations/tree.js @@ -6,6 +6,11 @@ export default { opened: !state.entries[path].opened, }); }, + [types.SET_TREE_OPEN](state, path) { + Object.assign(state.entries[path], { + opened: true, + }); + }, [types.CREATE_TREE](state, { treePath }) { Object.assign(state, { trees: Object.assign({}, state.trees, { diff --git a/app/assets/javascripts/image_diff/helpers/dom_helper.js b/app/assets/javascripts/image_diff/helpers/dom_helper.js index 12d56714b34..a319bcccb8f 100644 --- a/app/assets/javascripts/image_diff/helpers/dom_helper.js +++ b/app/assets/javascripts/image_diff/helpers/dom_helper.js @@ -2,7 +2,8 @@ export function setPositionDataAttribute(el, options) { // Update position data attribute so that the // new comment form can use this data for ajax request const { x, y, width, height } = options; - const position = el.dataset.position; + const { position } = el.dataset; + const positionObject = Object.assign({}, JSON.parse(position), { x, y, diff --git a/app/assets/javascripts/image_diff/helpers/utils_helper.js b/app/assets/javascripts/image_diff/helpers/utils_helper.js index 28d9a969143..beec99e6934 100644 --- a/app/assets/javascripts/image_diff/helpers/utils_helper.js +++ b/app/assets/javascripts/image_diff/helpers/utils_helper.js @@ -40,8 +40,7 @@ export function getTargetSelection(event) { const x = event.offsetX; const y = event.offsetY; - const width = imageEl.width; - const height = imageEl.height; + const { width, height } = imageEl; const actualWidth = imageEl.naturalWidth; const actualHeight = imageEl.naturalHeight; diff --git a/app/assets/javascripts/issuable/auto_width_dropdown_select.js b/app/assets/javascripts/issuable/auto_width_dropdown_select.js index b2c2de9e5de..07cf1eff279 100644 --- a/app/assets/javascripts/issuable/auto_width_dropdown_select.js +++ b/app/assets/javascripts/issuable/auto_width_dropdown_select.js @@ -10,7 +10,7 @@ class AutoWidthDropdownSelect { } init() { - const dropdownClass = this.dropdownClass; + const { dropdownClass } = this; this.$selectElement.select2({ dropdownCssClass: dropdownClass, ...AutoWidthDropdownSelect.selectOptions(this.dropdownClass), diff --git a/app/assets/javascripts/jobs/job_details_bundle.js b/app/assets/javascripts/jobs/job_details_bundle.js index f2939ad4dbe..0db7b95636c 100644 --- a/app/assets/javascripts/jobs/job_details_bundle.js +++ b/app/assets/javascripts/jobs/job_details_bundle.js @@ -4,7 +4,7 @@ import jobHeader from './components/header.vue'; import detailsBlock from './components/sidebar_details_block.vue'; export default () => { - const dataset = document.getElementById('js-job-details-vue').dataset; + const { dataset } = document.getElementById('js-job-details-vue'); const mediator = new JobMediator({ endpoint: dataset.endpoint }); mediator.fetchJob(); diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js index dfc3f7a94c8..37a45d1d1a2 100644 --- a/app/assets/javascripts/labels_select.js +++ b/app/assets/javascripts/labels_select.js @@ -56,7 +56,7 @@ export default class LabelsSelect { .map(function () { return this.value; }).get(); - const handleClick = options.handleClick; + const { handleClick } = options; $sidebarLabelTooltip.tooltip(); @@ -215,7 +215,7 @@ export default class LabelsSelect { } else { if (label.color != null) { - color = label.color[0]; + [color] = label.color; } } if (color) { @@ -243,7 +243,8 @@ export default class LabelsSelect { var $dropdownParent = $dropdown.parent(); var $dropdownInputField = $dropdownParent.find('.dropdown-input-field'); var isSelected = el !== null ? el.hasClass('is-active') : false; - var title = selected.title; + + var { title } = selected; var selectedLabels = this.selected; if ($dropdownInputField.length && $dropdownInputField.val().length) { @@ -382,7 +383,7 @@ export default class LabelsSelect { })); } else { - var labels = gl.issueBoards.BoardsStore.detail.issue.labels; + var { labels } = gl.issueBoards.BoardsStore.detail.issue; labels = labels.filter(function (selectedLabel) { return selectedLabel.id !== label.id; }); diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index 68f92c7f08a..5c249f3068e 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -164,7 +164,7 @@ export const scrollToElement = element => { if (!(element instanceof $)) { $el = $(element); } - const top = $el.offset().top; + const { top } = $el.offset(); return $('body, html').animate( { @@ -203,9 +203,7 @@ export const getSelectedFragment = () => { export const insertText = (target, text) => { // Firefox doesn't support `document.execCommand('insertText', false, text)` on textareas - const selectionStart = target.selectionStart; - const selectionEnd = target.selectionEnd; - const value = target.value; + const { selectionStart, selectionEnd, value } = target; const textBefore = value.substring(0, selectionStart); const textAfter = value.substring(selectionEnd, value.length); @@ -245,7 +243,8 @@ export const nodeMatchesSelector = (node, selector) => { // IE11 doesn't support `node.matches(selector)` - let parentNode = node.parentNode; + let { parentNode } = node; + if (!parentNode) { parentNode = document.createElement('div'); // eslint-disable-next-line no-param-reassign @@ -281,6 +280,8 @@ export const normalizeCRLFHeaders = headers => { headersArray.forEach(header => { const keyValue = header.split(': '); + + // eslint-disable-next-line prefer-destructuring headersObject[keyValue[0]] = keyValue[1]; }); diff --git a/app/assets/javascripts/lib/utils/number_utils.js b/app/assets/javascripts/lib/utils/number_utils.js index f086d962221..afbab59055b 100644 --- a/app/assets/javascripts/lib/utils/number_utils.js +++ b/app/assets/javascripts/lib/utils/number_utils.js @@ -13,7 +13,7 @@ export function formatRelevantDigits(number) { let relevantDigits = 0; let formattedNumber = ''; if (!Number.isNaN(Number(number))) { - digitsLeft = number.toString().split('.')[0]; + [digitsLeft] = number.toString().split('.'); switch (digitsLeft.length) { case 1: relevantDigits = 3; diff --git a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js index 70f185e3656..1501296ac4f 100644 --- a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js +++ b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js @@ -156,7 +156,7 @@ import Cookies from 'js-cookie'; return 0; } - const files = this.state.conflictsData.files; + const { files } = this.state.conflictsData; let count = 0; files.forEach((file) => { @@ -313,7 +313,7 @@ import Cookies from 'js-cookie'; }, isReadyToCommit() { - const files = this.state.conflictsData.files; + const { files } = this.state.conflictsData; const hasCommitMessage = $.trim(this.state.conflictsData.commitMessage).length; let unresolved = 0; diff --git a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js index 491858c3602..7badd68089c 100644 --- a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js +++ b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js @@ -12,7 +12,7 @@ import syntaxHighlight from '../syntax_highlight'; export default function initMergeConflicts() { const INTERACTIVE_RESOLVE_MODE = 'interactive'; const conflictsEl = document.querySelector('#conflicts'); - const mergeConflictsStore = gl.mergeConflicts.mergeConflictsStore; + const { mergeConflictsStore } = gl.mergeConflicts; const mergeConflictsService = new MergeConflictsService({ conflictsPath: conflictsEl.dataset.conflictsPath, resolveConflictsPath: conflictsEl.dataset.resolveConflictsPath, diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js index 83d326ef68f..329d4303132 100644 --- a/app/assets/javascripts/merge_request_tabs.js +++ b/app/assets/javascripts/merge_request_tabs.js @@ -64,7 +64,7 @@ import Notes from './notes'; /* eslint-enable max-len */ // Store the `location` object, allowing for easier stubbing in tests -let location = window.location; +let { location } = window; export default class MergeRequestTabs { constructor({ action, setUrl, stubLocation } = {}) { @@ -279,7 +279,7 @@ export default class MergeRequestTabs { mountPipelinesView() { const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view'); - const CommitPipelinesTable = gl.CommitPipelinesTable; + const { CommitPipelinesTable } = gl; this.commitPipelinesTable = new CommitPipelinesTable({ propsData: { endpoint: pipelineTableViewEl.dataset.endpoint, diff --git a/app/assets/javascripts/monitoring/utils/multiple_time_series.js b/app/assets/javascripts/monitoring/utils/multiple_time_series.js index ed3a27dd68b..cee39fd0559 100644 --- a/app/assets/javascripts/monitoring/utils/multiple_time_series.js +++ b/app/assets/javascripts/monitoring/utils/multiple_time_series.js @@ -41,10 +41,10 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom } else { const unusedColors = _.difference(defaultColorOrder, usedColors); if (unusedColors.length > 0) { - pick = unusedColors[0]; + [pick] = unusedColors; } else { usedColors = []; - pick = defaultColorOrder[0]; + [pick] = defaultColorOrder; } } usedColors.push(pick); diff --git a/app/assets/javascripts/network/branch_graph.js b/app/assets/javascripts/network/branch_graph.js index 6a8591692f1..94da1be4066 100644 --- a/app/assets/javascripts/network/branch_graph.js +++ b/app/assets/javascripts/network/branch_graph.js @@ -101,8 +101,8 @@ export default (function() { }; BranchGraph.prototype.buildGraph = function() { - var cuday, cumonth, day, j, len, mm, r, ref; - r = this.r; + var cuday, cumonth, day, j, len, mm, ref; + const { r } = this; cuday = 0; cumonth = ""; r.rect(0, 0, 40, this.barHeight).attr({ @@ -121,7 +121,7 @@ export default (function() { font: "12px Monaco, monospace", fill: "#BBB" }); - cuday = day[0]; + [cuday] = day; } if (cumonth !== day[1]) { // Months @@ -129,6 +129,8 @@ export default (function() { font: "12px Monaco, monospace", fill: "#EEE" }); + + // eslint-disable-next-line prefer-destructuring cumonth = day[1]; } } @@ -169,8 +171,8 @@ export default (function() { }; BranchGraph.prototype.bindEvents = function() { - var element; - element = this.element; + const { element } = this; + return $(element).scroll((function(_this) { return function(event) { return _this.renderPartialGraph(); @@ -207,11 +209,13 @@ export default (function() { }; BranchGraph.prototype.appendLabel = function(x, y, commit) { - var label, r, rect, shortrefs, text, textbox, triangle; + var label, rect, shortrefs, text, textbox, triangle; + if (!commit.refs) { return; } - r = this.r; + + const { r } = this; shortrefs = commit.refs; // Truncate if longer than 15 chars if (shortrefs.length > 17) { @@ -242,11 +246,8 @@ export default (function() { }; BranchGraph.prototype.appendAnchor = function(x, y, commit) { - var anchor, options, r, top; - r = this.r; - top = this.top; - options = this.options; - anchor = r.circle(x, y, 10).attr({ + const { r, top, options } = this; + const anchor = r.circle(x, y, 10).attr({ fill: "#000", opacity: 0, cursor: "pointer" @@ -262,14 +263,15 @@ export default (function() { }; BranchGraph.prototype.drawDot = function(x, y, commit) { - var avatar_box_x, avatar_box_y, r; - r = this.r; + const { r } = this; r.circle(x, y, 3).attr({ fill: this.colors[commit.space], stroke: "none" }); - avatar_box_x = this.offsetX + this.unitSpace * this.mspace + 10; - avatar_box_y = y - 10; + + const avatar_box_x = this.offsetX + this.unitSpace * this.mspace + 10; + const avatar_box_y = y - 10; + r.rect(avatar_box_x, avatar_box_y, 20, 20).attr({ stroke: this.colors[commit.space], "stroke-width": 2 @@ -282,10 +284,10 @@ export default (function() { }; BranchGraph.prototype.drawLines = function(x, y, commit) { - var arrow, color, i, j, len, offset, parent, parentCommit, parentX1, parentX2, parentY, r, ref, results, route; - r = this.r; - ref = commit.parents; - results = []; + var arrow, color, i, len, offset, parent, parentCommit, parentX1, parentX2, parentY, route; + const { r } = this; + const ref = commit.parents; + const results = []; for (i = 0, len = ref.length; i < len; i += 1) { parent = ref[i]; @@ -331,11 +333,10 @@ export default (function() { }; BranchGraph.prototype.markCommit = function(commit) { - var r, x, y; if (commit.id === this.options.commit_id) { - r = this.r; - x = this.offsetX + this.unitSpace * (this.mspace - commit.space); - y = this.offsetY + this.unitTime * commit.time; + const { r } = this; + const x = this.offsetX + this.unitSpace * (this.mspace - commit.space); + const y = this.offsetY + this.unitTime * commit.time; r.path(["M", x + 5, y, "L", x + 15, y + 4, "L", x + 15, y - 4, "Z"]).attr({ fill: "#000", "fill-opacity": .5, diff --git a/app/assets/javascripts/new_branch_form.js b/app/assets/javascripts/new_branch_form.js index 41ba5b28a1b..205d9766656 100644 --- a/app/assets/javascripts/new_branch_form.js +++ b/app/assets/javascripts/new_branch_form.js @@ -52,7 +52,7 @@ export default class NewBranchForm { validate() { var errorMessage, errors, formatter, unique, validator; - const indexOf = [].indexOf; + const { indexOf } = []; this.branchNameError.empty(); unique = function(values, value) { diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index da1a52155d8..48cda28a1ae 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -311,7 +311,7 @@ export default class Notes { }, }) .then(({ data }) => { - const notes = data.notes; + const { notes } = data; this.last_fetched_at = data.last_fetched_at; this.setPollingInterval(data.notes.length); $.each(notes, (i, note) => this.renderNote(note)); diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue index 17b5e8d1ae8..98f8b9af168 100644 --- a/app/assets/javascripts/notes/components/notes_app.vue +++ b/app/assets/javascripts/notes/components/notes_app.vue @@ -72,7 +72,7 @@ export default { }, mounted() { this.fetchNotes(); - const parentElement = this.$el.parentElement; + const { parentElement } = this.$el; if (parentElement && parentElement.classList.contains('js-vue-notes-event')) { parentElement.addEventListener('toggleAward', event => { diff --git a/app/assets/javascripts/notes/services/notes_service.js b/app/assets/javascripts/notes/services/notes_service.js index ee7628840cf..f5dce94caad 100644 --- a/app/assets/javascripts/notes/services/notes_service.js +++ b/app/assets/javascripts/notes/services/notes_service.js @@ -28,7 +28,7 @@ export default { }, poll(data = {}) { const endpoint = data.notesData.notesPath; - const lastFetchedAt = data.lastFetchedAt; + const { lastFetchedAt } = data; const options = { headers: { 'X-Last-Fetched-At': lastFetchedAt ? `${lastFetchedAt}` : undefined, diff --git a/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue b/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue index cc2805a1901..d6aa4bb95d2 100644 --- a/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue +++ b/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue @@ -96,7 +96,7 @@ this.enteredUsername = ''; }, onSecondaryAction() { - const form = this.$refs.form; + const { form } = this.$refs; form.action = this.blockUserUrl; this.$refs.method.value = 'put'; diff --git a/app/assets/javascripts/pages/dashboard/todos/index/todos.js b/app/assets/javascripts/pages/dashboard/todos/index/todos.js index 6fc43af2623..ff19b9a9c30 100644 --- a/app/assets/javascripts/pages/dashboard/todos/index/todos.js +++ b/app/assets/javascripts/pages/dashboard/todos/index/todos.js @@ -61,7 +61,7 @@ export default class Todos { e.stopPropagation(); e.preventDefault(); - const target = e.target; + const { target } = e; target.setAttribute('disabled', true); target.classList.add('disabled'); diff --git a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js index ae72c8cb4d5..6c1788dc160 100644 --- a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js +++ b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js @@ -80,10 +80,11 @@ export default (function() { }; ContributorsStatGraph.prototype.redraw_authors = function() { - var author_commits, x_domain; $("ol").html(""); - x_domain = ContributorsGraph.prototype.x_domain; - author_commits = ContributorsStatGraphUtil.get_author_data(this.parsed_log, this.field, x_domain); + + const { x_domain } = ContributorsGraph.prototype; + const author_commits = ContributorsStatGraphUtil.get_author_data(this.parsed_log, this.field, x_domain); + return _.each(author_commits, (function(_this) { return function(d) { _this.redraw_author_commit_info(d); @@ -102,7 +103,7 @@ export default (function() { }; ContributorsStatGraph.prototype.change_date_header = function() { - const x_domain = ContributorsGraph.prototype.x_domain; + const { x_domain } = ContributorsGraph.prototype; const formattedDateRange = sprintf( s__('ContributorsPage|%{startDate} – %{endDate}'), { diff --git a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js index 37ef77c8e43..1faa59fb45b 100644 --- a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js +++ b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js @@ -28,7 +28,7 @@ document.addEventListener('DOMContentLoaded', () => { const autoDevOpsExtraSettings = document.querySelector('.js-extra-settings'); autoDevOpsSettings.addEventListener('click', event => { - const target = event.target; + const { target } = event; if (target.classList.contains('js-toggle-extra-settings')) { autoDevOpsExtraSettings.classList.toggle( 'hidden', diff --git a/app/assets/javascripts/pipelines/pipeline_details_bundle.js b/app/assets/javascripts/pipelines/pipeline_details_bundle.js index b49a16a87e6..cf3ff48e608 100644 --- a/app/assets/javascripts/pipelines/pipeline_details_bundle.js +++ b/app/assets/javascripts/pipelines/pipeline_details_bundle.js @@ -10,7 +10,7 @@ import eventHub from './event_hub'; Vue.use(Translate); export default () => { - const dataset = document.querySelector('.js-pipeline-details-vue').dataset; + const { dataset } = document.querySelector('.js-pipeline-details-vue'); const mediator = new PipelinesMediator({ endpoint: dataset.endpoint }); diff --git a/app/assets/javascripts/pipelines/services/pipelines_service.js b/app/assets/javascripts/pipelines/services/pipelines_service.js index 59c8b9c58e5..8317d3f4510 100644 --- a/app/assets/javascripts/pipelines/services/pipelines_service.js +++ b/app/assets/javascripts/pipelines/services/pipelines_service.js @@ -19,7 +19,7 @@ export default class PipelinesService { getPipelines(data = {}) { const { scope, page } = data; - const CancelToken = axios.CancelToken; + const { CancelToken } = axios; this.cancelationSource = CancelToken.source(); diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/preview_markdown.js index 45670584679..0e973cab4d2 100644 --- a/app/assets/javascripts/preview_markdown.js +++ b/app/assets/javascripts/preview_markdown.js @@ -43,7 +43,7 @@ MarkdownPreview.prototype.showPreview = function ($form) { this.fetchMarkdownPreview(mdText, url, (function (response) { var body; if (response.body.length > 0) { - body = response.body; + ({ body } = response); } else { body = this.emptyMessage; } diff --git a/app/assets/javascripts/profile/gl_crop.js b/app/assets/javascripts/profile/gl_crop.js index c6d809d84a6..f641b23e519 100644 --- a/app/assets/javascripts/profile/gl_crop.js +++ b/app/assets/javascripts/profile/gl_crop.js @@ -47,7 +47,8 @@ import _ from 'underscore'; var _this; _this = this; this.fileInput.on('change', function(e) { - return _this.onFileInputChange(e, this); + _this.onFileInputChange(e, this); + this.value = null; }); this.pickImageEl.on('click', this.onPickImageClick); this.modalCrop.on('shown.bs.modal', this.onModalShow); @@ -85,11 +86,10 @@ import _ from 'underscore'; cropBoxResizable: false, toggleDragModeOnDblclick: false, built: function() { - var $image, container, cropBoxHeight, cropBoxWidth; - $image = $(this); - container = $image.cropper('getContainerData'); - cropBoxWidth = _this.cropBoxWidth; - cropBoxHeight = _this.cropBoxHeight; + const $image = $(this); + const container = $image.cropper('getContainerData'); + const { cropBoxWidth, cropBoxHeight } = _this; + return $image.cropper('setCropBoxData', { width: cropBoxWidth, height: cropBoxHeight, @@ -136,7 +136,7 @@ import _ from 'underscore'; } dataURLtoBlob(dataURL) { - var array, binary, i, k, len, v; + var array, binary, i, len, v; binary = atob(dataURL.split(',')[1]); array = []; diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js index bcdb3f739fe..05485e352dc 100644 --- a/app/assets/javascripts/project_find_file.js +++ b/app/assets/javascripts/project_find_file.js @@ -88,7 +88,7 @@ export default class ProjectFindFile { // render result renderList(filePaths, searchText) { - var blobItemUrl, filePath, html, i, j, len, matches, results; + var blobItemUrl, filePath, html, i, len, matches, results; this.element.find(".tree-table > tbody").empty(); results = []; diff --git a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue index c772fca14bb..a4c7c143e56 100644 --- a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue +++ b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue @@ -47,7 +47,7 @@ }, methods: { successCallback(res) { - const pipelines = res.data.pipelines; + const { pipelines } = res.data; if (pipelines.length > 0) { // The pipeline entity always keeps the latest pipeline info on the `details.status` this.ciStatus = pipelines[0].details.status; diff --git a/app/assets/javascripts/projects_dropdown/index.js b/app/assets/javascripts/projects_dropdown/index.js index e1ca70c51a6..6056f12aa4f 100644 --- a/app/assets/javascripts/projects_dropdown/index.js +++ b/app/assets/javascripts/projects_dropdown/index.js @@ -31,7 +31,7 @@ document.addEventListener('DOMContentLoaded', () => { projectsDropdownApp, }, data() { - const dataset = this.$options.el.dataset; + const { dataset } = this.$options.el; const store = new ProjectsStore(); const service = new ProjectsService(dataset.userName); diff --git a/app/assets/javascripts/registry/index.js b/app/assets/javascripts/registry/index.js index 6fb125192b2..e15cd94a915 100644 --- a/app/assets/javascripts/registry/index.js +++ b/app/assets/javascripts/registry/index.js @@ -10,7 +10,7 @@ export default () => new Vue({ registryApp, }, data() { - const dataset = document.querySelector(this.$options.el).dataset; + const { dataset } = document.querySelector(this.$options.el); return { endpoint: dataset.endpoint, }; diff --git a/app/assets/javascripts/registry/stores/actions.js b/app/assets/javascripts/registry/stores/actions.js index c0de03373d8..a78aa90b7b5 100644 --- a/app/assets/javascripts/registry/stores/actions.js +++ b/app/assets/javascripts/registry/stores/actions.js @@ -20,7 +20,7 @@ export const fetchList = ({ commit }, { repo, page }) => { commit(types.TOGGLE_REGISTRY_LIST_LOADING, repo); return Vue.http.get(repo.tagsPath, { params: { page } }).then(response => { - const headers = response.headers; + const { headers } = response; return response.json().then(resp => { commit(types.TOGGLE_REGISTRY_LIST_LOADING, repo); diff --git a/app/assets/javascripts/smart_interval.js b/app/assets/javascripts/smart_interval.js index 77ab7c964e6..5e385400747 100644 --- a/app/assets/javascripts/smart_interval.js +++ b/app/assets/javascripts/smart_interval.js @@ -42,8 +42,7 @@ export default class SmartInterval { /* public */ start() { - const cfg = this.cfg; - const state = this.state; + const { cfg, state } = this; if (cfg.immediateExecution && !this.isLoading) { cfg.immediateExecution = false; @@ -100,7 +99,7 @@ export default class SmartInterval { /* private */ initInterval() { - const cfg = this.cfg; + const { cfg } = this; if (!cfg.lazyStart) { this.start(); @@ -151,7 +150,7 @@ export default class SmartInterval { } incrementInterval() { - const cfg = this.cfg; + const { cfg } = this; const currentInterval = this.getCurrentInterval(); if (cfg.hiddenInterval && !this.isPageVisible()) return; let nextInterval = currentInterval * cfg.incrementByFactorOf; @@ -166,7 +165,7 @@ export default class SmartInterval { isPageVisible() { return this.state.pageVisibility === 'visible'; } stopTimer() { - const state = this.state; + const { state } = this; state.intervalId = window.clearInterval(state.intervalId); } diff --git a/app/assets/javascripts/test_utils/simulate_drag.js b/app/assets/javascripts/test_utils/simulate_drag.js index e39213cb098..a5c18042ce7 100644 --- a/app/assets/javascripts/test_utils/simulate_drag.js +++ b/app/assets/javascripts/test_utils/simulate_drag.js @@ -38,14 +38,14 @@ function simulateEvent(el, type, options = {}) { function isLast(target) { const el = typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el; - const children = el.children; + const { children } = el; return children.length - 1 === target.index; } function getTarget(target) { const el = typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el; - const children = el.children; + const { children } = el; return ( children[target.index] || diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js index 7abe7a6be5f..e3d7645040d 100644 --- a/app/assets/javascripts/users_select.js +++ b/app/assets/javascripts/users_select.js @@ -250,7 +250,6 @@ function UsersSelect(currentUser, els, options = {}) { let anyUser; let index; - let j; let len; let name; let obj; @@ -501,7 +500,7 @@ function UsersSelect(currentUser, els, options = {}) { if (this.multiSelect) { selected = getSelected().find(u => user.id === u); - const fieldName = this.fieldName; + const { fieldName } = this; const field = $dropdown.closest('.selectbox').find("input[name='" + fieldName + "'][value='" + user.id + "']"); if (field.length) { @@ -553,7 +552,7 @@ function UsersSelect(currentUser, els, options = {}) { minimumInputLength: 0, query: function(query) { return _this.users(query.term, options, function(users) { - var anyUser, data, emailUser, index, j, len, name, nullUser, obj, ref; + var anyUser, data, emailUser, index, len, name, nullUser, obj, ref; data = { results: users }; diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue index e455c4d2cb5..09477da40b5 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue @@ -191,7 +191,7 @@ export default { if (data.ci_status === this.mr.ciStatus) return; if (!data.pipeline) return; - const label = data.pipeline.details.status.label; + const { label } = data.pipeline.details.status; const title = `Pipeline ${label}`; const message = `Pipeline ${label} for "${data.title}"`; @@ -211,7 +211,7 @@ export default { // `params` should be an Array contains a Boolean, like `[true]` // Passing parameter as Boolean didn't work. eventHub.$on('SetBranchRemoveFlag', (params) => { - this.mr.isRemovingSourceBranch = params[0]; + [this.mr.isRemovingSourceBranch] = params; }); eventHub.$on('FailedToMerge', (mergeError) => { diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue index 6851029018a..133bdbb54f7 100644 --- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue @@ -42,7 +42,7 @@ export default { }, methods: { onImgLoad() { - const contentImg = this.$refs.contentImg; + const { contentImg } = this.$refs; if (contentImg) { this.isZoomable = diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue index 09e0094054d..a10deb93f0f 100644 --- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue @@ -4,7 +4,7 @@ import { __ } from '~/locale'; import $ from 'jquery'; import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue'; -const CancelToken = axios.CancelToken; +const { CancelToken } = axios; let axiosSource; export default { diff --git a/app/assets/javascripts/vue_shared/components/table_pagination.vue b/app/assets/javascripts/vue_shared/components/table_pagination.vue index 2370e59d017..8e9621c956f 100644 --- a/app/assets/javascripts/vue_shared/components/table_pagination.vue +++ b/app/assets/javascripts/vue_shared/components/table_pagination.vue @@ -55,7 +55,7 @@ }, getItems() { const total = this.pageInfo.totalPages; - const page = this.pageInfo.page; + const { page } = this.pageInfo; const items = []; if (page > 1) { diff --git a/app/assets/stylesheets/bootstrap.scss b/app/assets/stylesheets/bootstrap.scss new file mode 100644 index 00000000000..a040c2f8c20 --- /dev/null +++ b/app/assets/stylesheets/bootstrap.scss @@ -0,0 +1,37 @@ +/* + * Includes specific styles from the bootstrap4 foler in node_modules + */ + +@import "../../../node_modules/bootstrap/scss/functions"; +@import "../../../node_modules/bootstrap/scss/variables"; +@import "../../../node_modules/bootstrap/scss/mixins"; +@import "../../../node_modules/bootstrap/scss/root"; +@import "../../../node_modules/bootstrap/scss/reboot"; +@import "../../../node_modules/bootstrap/scss/type"; +@import "../../../node_modules/bootstrap/scss/images"; +@import "../../../node_modules/bootstrap/scss/code"; +@import "../../../node_modules/bootstrap/scss/grid"; +@import "../../../node_modules/bootstrap/scss/tables"; +@import "../../../node_modules/bootstrap/scss/forms"; +@import "../../../node_modules/bootstrap/scss/buttons"; +@import "../../../node_modules/bootstrap/scss/transitions"; +@import "../../../node_modules/bootstrap/scss/dropdown"; +@import "../../../node_modules/bootstrap/scss/button-group"; +@import "../../../node_modules/bootstrap/scss/input-group"; +@import "../../../node_modules/bootstrap/scss/custom-forms"; +@import "../../../node_modules/bootstrap/scss/nav"; +@import "../../../node_modules/bootstrap/scss/navbar"; +@import "../../../node_modules/bootstrap/scss/card"; +@import "../../../node_modules/bootstrap/scss/breadcrumb"; +@import "../../../node_modules/bootstrap/scss/pagination"; +@import "../../../node_modules/bootstrap/scss/badge"; +@import "../../../node_modules/bootstrap/scss/alert"; +@import "../../../node_modules/bootstrap/scss/progress"; +@import "../../../node_modules/bootstrap/scss/media"; +@import "../../../node_modules/bootstrap/scss/list-group"; +@import "../../../node_modules/bootstrap/scss/close"; +@import "../../../node_modules/bootstrap/scss/modal"; +@import "../../../node_modules/bootstrap/scss/tooltip"; +@import "../../../node_modules/bootstrap/scss/popover"; +@import "../../../node_modules/bootstrap/scss/utilities"; +@import "../../../node_modules/bootstrap/scss/print"; diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss index 7c28024001f..c46b0b5db09 100644 --- a/app/assets/stylesheets/framework.scss +++ b/app/assets/stylesheets/framework.scss @@ -1,6 +1,7 @@ @import 'framework/variables'; @import 'framework/mixins'; -@import '../../../node_modules/bootstrap/scss/bootstrap'; + +@import 'bootstrap'; @import 'bootstrap_migration'; @import 'framework/layout'; diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml index 1e72d88db1e..53f54db1ddf 100644 --- a/app/views/groups/new.html.haml +++ b/app/views/groups/new.html.haml @@ -4,27 +4,37 @@ - page_title 'New Group' - header_title "Groups", dashboard_groups_path -%h3.page-title - New Group -%hr +.row.prepend-top-default + .col-lg-3.profile-settings-sidebar + %h4.prepend-top-0 + = _('New group') + %p + - group_docs_path = help_page_path('user/group/index') + - group_docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: group_docs_path } + = s_('%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects.').html_safe % { group_docs_link_start: group_docs_link_start, group_docs_link_end: '</a>'.html_safe } + %p + - subgroup_docs_path = help_page_path('user/group/subgroups/index') + - subgroup_docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: subgroup_docs_path } + = s_('Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}.').html_safe % { subgroup_docs_link_start: subgroup_docs_link_start, subgroup_docs_link_end: '</a>'.html_safe } -= form_for @group, html: { class: 'group-form gl-show-field-errors' } do |f| - = form_errors(@group) - = render 'shared/group_form', f: f, autofocus: true + .col-lg-9 + = form_for @group, html: { class: 'group-form gl-show-field-errors' } do |f| + = form_errors(@group) + = render 'shared/group_form', f: f, autofocus: true - .form-group.row.group-description-holder - = f.label :avatar, "Group avatar", class: 'col-form-label col-sm-2' - .col-sm-10 - = render 'shared/choose_group_avatar_button', f: f + .form-group.row.group-description-holder + = f.label :avatar, "Group avatar", class: 'col-form-label col-sm-2' + .col-sm-10 + = render 'shared/choose_group_avatar_button', f: f - = render 'shared/visibility_level', f: f, visibility_level: default_group_visibility, can_change_visibility_level: true, form_model: @group + = render 'shared/visibility_level', f: f, visibility_level: default_group_visibility, can_change_visibility_level: true, form_model: @group - = render 'create_chat_team', f: f if Gitlab.config.mattermost.enabled + = render 'create_chat_team', f: f if Gitlab.config.mattermost.enabled - .form-group.row - .offset-sm-2.col-sm-10 - = render 'shared/group_tips' + .form-group.row + .offset-sm-2.col-sm-10 + = render 'shared/group_tips' - .form-actions - = f.submit 'Create group', class: "btn btn-create" - = link_to 'Cancel', dashboard_groups_path, class: 'btn btn-cancel' + .form-actions + = f.submit 'Create group', class: "btn btn-create" + = link_to 'Cancel', dashboard_groups_path, class: 'btn btn-cancel' diff --git a/app/views/projects/clusters/_integration_form.html.haml b/app/views/projects/clusters/_integration_form.html.haml index db97203a2aa..b46b45fea49 100644 --- a/app/views/projects/clusters/_integration_form.html.haml +++ b/app/views/projects/clusters/_integration_form.html.haml @@ -1,6 +1,6 @@ = form_for @cluster, url: namespace_project_cluster_path(@project.namespace, @project, @cluster), as: :cluster do |field| = form_errors(@cluster) - .form-group.append-bottom-20 + .form-group %h5= s_('ClusterIntegration|Integration status') %p - if @cluster.enabled? @@ -10,7 +10,7 @@ = s_('ClusterIntegration|Kubernetes cluster integration is enabled for this project.') - else = s_('ClusterIntegration|Kubernetes cluster integration is disabled for this project.') - %label.append-bottom-10.js-cluster-enable-toggle-area + %label.append-bottom-0.js-cluster-enable-toggle-area %button{ type: 'button', class: "js-project-feature-toggle project-feature-toggle #{'is-checked' if @cluster.enabled?} #{'is-disabled' unless can?(current_user, :update_cluster, @cluster)}", "aria-label": s_("ClusterIntegration|Toggle Kubernetes cluster"), @@ -20,19 +20,26 @@ = sprite_icon('status_success_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-checked') = sprite_icon('status_failed_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-unchecked') - .form-group - %h5= s_('ClusterIntegration|Security') - %p - = s_("ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application.") - = link_to s_("ClusterIntegration|Learn more about security configuration"), help_page_path('user/project/clusters/index.md', anchor: 'security-implications') - - .form-group - %h5= s_('ClusterIntegration|Environment scope') - %p - = s_("ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster.") - = link_to s_("ClusterIntegration|Learn more about environments"), help_page_path('ci/environments') - = field.text_field :environment_scope, class: 'form-control js-select-on-focus', readonly: !has_multiple_clusters?(@project), placeholder: s_('ClusterIntegration|Environment scope') + - if has_multiple_clusters?(@project) + .form-group + %h5= s_('ClusterIntegration|Environment scope') + %p + = s_("ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster.") + = link_to s_("ClusterIntegration|Learn more about environments"), help_page_path('ci/environments') + = field.text_field :environment_scope, class: 'form-control js-select-on-focus', placeholder: s_('ClusterIntegration|Environment scope') - if can?(current_user, :update_cluster, @cluster) .form-group = field.submit _('Save changes'), class: 'btn btn-success' + + - unless has_multiple_clusters?(@project) + %h5= s_('ClusterIntegration|Environment scope') + %p + %code * + is the default environment scope for this cluster. This means that all jobs, regardless of their environment, will use this cluster. + = link_to 'More information', ('https://docs.gitlab.com/ee/user/project/clusters/#setting-the-environment-scope') + + %h5= s_('ClusterIntegration|Security') + %p + = s_("ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application.") + = link_to s_("ClusterIntegration|Learn more about security configuration"), help_page_path('user/project/clusters/index.md', anchor: 'security-implications') diff --git a/changelogs/unreleased/45703-open-web-ide-file-tree.yml b/changelogs/unreleased/45703-open-web-ide-file-tree.yml new file mode 100644 index 00000000000..abee9cad2d5 --- /dev/null +++ b/changelogs/unreleased/45703-open-web-ide-file-tree.yml @@ -0,0 +1,5 @@ +--- +title: Update WebIDE to show file in tree on load +merge_request: 19887 +author: +type: changed diff --git a/changelogs/unreleased/46831-remove-unused-bootstrap-component-css.yml b/changelogs/unreleased/46831-remove-unused-bootstrap-component-css.yml new file mode 100644 index 00000000000..e0e2b481b69 --- /dev/null +++ b/changelogs/unreleased/46831-remove-unused-bootstrap-component-css.yml @@ -0,0 +1,5 @@ +--- +title: Removes unused bootstrap 4 scss files +merge_request: 19423 +author: +type: deprecated diff --git a/changelogs/unreleased/47221-explain-what-groups-are-in-the-new-group-page.yml b/changelogs/unreleased/47221-explain-what-groups-are-in-the-new-group-page.yml new file mode 100644 index 00000000000..94c58a3863a --- /dev/null +++ b/changelogs/unreleased/47221-explain-what-groups-are-in-the-new-group-page.yml @@ -0,0 +1,5 @@ +--- +title: Update new group page to better explain what groups are +merge_request: 19991 +author: +type: other diff --git a/changelogs/unreleased/47794-environment-scope-cluster-page.yml b/changelogs/unreleased/47794-environment-scope-cluster-page.yml new file mode 100644 index 00000000000..75eb7ec209c --- /dev/null +++ b/changelogs/unreleased/47794-environment-scope-cluster-page.yml @@ -0,0 +1,6 @@ +--- +title: Change environment scope text depending on number of project clusters. Update + form to only include form-groups +merge_request: +author: +type: changed diff --git a/changelogs/unreleased/48378-avatar-upload.yml b/changelogs/unreleased/48378-avatar-upload.yml new file mode 100644 index 00000000000..1e359ee72d5 --- /dev/null +++ b/changelogs/unreleased/48378-avatar-upload.yml @@ -0,0 +1,5 @@ +--- +title: Fixes issue with uploading same image to Profile Avatar twice +merge_request: 20161 +author: Chirag Bhatia +type: fixed diff --git a/changelogs/unreleased/prefer-destructuring-fix.yml b/changelogs/unreleased/prefer-destructuring-fix.yml new file mode 100644 index 00000000000..452e04f553e --- /dev/null +++ b/changelogs/unreleased/prefer-destructuring-fix.yml @@ -0,0 +1,5 @@ +--- +title: Enable prefer-structuring in JS files +merge_request: 19943 +author: gfyoung +type: other diff --git a/lib/api/branches.rb b/lib/api/branches.rb index 13cfba728fa..4b223a391ae 100644 --- a/lib/api/branches.rb +++ b/lib/api/branches.rb @@ -45,6 +45,7 @@ module API present( paginate(::Kaminari.paginate_array(branches)), with: Entities::Branch, + current_user: current_user, project: user_project, merged_branch_names: merged_branch_names ) @@ -63,7 +64,7 @@ module API get do branch = find_branch!(params[:branch]) - present branch, with: Entities::Branch, project: user_project + present branch, with: Entities::Branch, current_user: current_user, project: user_project end end @@ -101,7 +102,7 @@ module API end if protected_branch.valid? - present branch, with: Entities::Branch, project: user_project + present branch, with: Entities::Branch, current_user: current_user, project: user_project else render_api_error!(protected_branch.errors.full_messages, 422) end @@ -121,7 +122,7 @@ module API protected_branch = user_project.protected_branches.find_by(name: branch.name) protected_branch&.destroy - present branch, with: Entities::Branch, project: user_project + present branch, with: Entities::Branch, current_user: current_user, project: user_project end desc 'Create branch' do @@ -140,6 +141,7 @@ module API if result[:status] == :success present result[:branch], with: Entities::Branch, + current_user: current_user, project: user_project else render_api_error!(result[:message], 400) diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index b3016c1a637..88944cd62ea 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -684,6 +684,10 @@ module Gitlab end end + def update_branch(branch_name, user:, newrev:, oldrev:) + OperationService.new(user, self).update_branch(branch_name, newrev, oldrev) + end + def rm_branch(branch_name, user:) gitaly_migrate(:operation_user_delete_branch, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled| if is_enabled @@ -1953,8 +1957,7 @@ module Gitlab rebase_sha = run_git!(%w(rev-parse HEAD), chdir: rebase_path, env: env).strip - Gitlab::Git::OperationService.new(user, self) - .update_branch(branch, rebase_sha, branch_sha) + update_branch(branch, user: user, newrev: rebase_sha, oldrev: branch_sha) rebase_sha end diff --git a/spec/javascripts/blob/3d_viewer/mesh_object_spec.js b/spec/javascripts/blob/3d_viewer/mesh_object_spec.js index d1ebae33dab..7651792be2e 100644 --- a/spec/javascripts/blob/3d_viewer/mesh_object_spec.js +++ b/spec/javascripts/blob/3d_viewer/mesh_object_spec.js @@ -26,7 +26,7 @@ describe('Mesh object', () => { const object = new MeshObject( new BoxGeometry(10, 10, 10), ); - const radius = object.geometry.boundingSphere.radius; + const { radius } = object.geometry.boundingSphere; expect(radius).not.toBeGreaterThan(4); }); @@ -35,7 +35,7 @@ describe('Mesh object', () => { const object = new MeshObject( new BoxGeometry(1, 1, 1), ); - const radius = object.geometry.boundingSphere.radius; + const { radius } = object.geometry.boundingSphere; expect(radius).toBeLessThan(1); }); diff --git a/spec/javascripts/commit/pipelines/pipelines_spec.js b/spec/javascripts/commit/pipelines/pipelines_spec.js index 819ed7896ca..a18e09da50a 100644 --- a/spec/javascripts/commit/pipelines/pipelines_spec.js +++ b/spec/javascripts/commit/pipelines/pipelines_spec.js @@ -16,7 +16,7 @@ describe('Pipelines table in Commits and Merge requests', function () { beforeEach(() => { mock = new MockAdapter(axios); - const pipelines = getJSONFixture(jsonFixtureName).pipelines; + const { pipelines } = getJSONFixture(jsonFixtureName); PipelinesTable = Vue.extend(pipelinesTable); pipeline = pipelines.find(p => p.user !== null && p.commit !== null); diff --git a/spec/javascripts/deploy_keys/components/key_spec.js b/spec/javascripts/deploy_keys/components/key_spec.js index 4279add21d1..d1de9d132b8 100644 --- a/spec/javascripts/deploy_keys/components/key_spec.js +++ b/spec/javascripts/deploy_keys/components/key_spec.js @@ -88,7 +88,7 @@ describe('Deploy keys key', () => { }); it('expands all project labels after click', done => { - const length = vm.deployKey.deploy_keys_projects.length; + const { length } = vm.deployKey.deploy_keys_projects; vm.$el.querySelectorAll('.deploy-project-label')[1].click(); Vue.nextTick(() => { diff --git a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js index 312a684f4d2..cce10c4083c 100644 --- a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js +++ b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js @@ -92,7 +92,7 @@ describe('DiffLineGutterContent', () => { }); it('should return discussions for the given lineCode', () => { - const lineCode = getDiffFileMock().highlightedDiffLines[1].lineCode; + const { lineCode } = getDiffFileMock().highlightedDiffLines[1]; const component = createComponent({ lineCode, showCommentButton: true }); setDiscussions(component); diff --git a/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js b/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js index 59bd2650081..d926663fac0 100644 --- a/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js +++ b/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js @@ -103,7 +103,7 @@ describe('RecentSearchesDropdownContent', () => { describe('processedItems', () => { it('with items', () => { vm = createComponent(propsDataWithItems); - const processedItems = vm.processedItems; + const { processedItems } = vm; expect(processedItems.length).toEqual(2); @@ -122,7 +122,7 @@ describe('RecentSearchesDropdownContent', () => { it('with no items', () => { vm = createComponent(propsDataWithoutItems); - const processedItems = vm.processedItems; + const { processedItems } = vm; expect(processedItems.length).toEqual(0); }); @@ -131,13 +131,13 @@ describe('RecentSearchesDropdownContent', () => { describe('hasItems', () => { it('with items', () => { vm = createComponent(propsDataWithItems); - const hasItems = vm.hasItems; + const { hasItems } = vm; expect(hasItems).toEqual(true); }); it('with no items', () => { vm = createComponent(propsDataWithoutItems); - const hasItems = vm.hasItems; + const { hasItems } = vm; expect(hasItems).toEqual(false); }); }); diff --git a/spec/javascripts/filtered_search/recent_searches_root_spec.js b/spec/javascripts/filtered_search/recent_searches_root_spec.js index 1e6272bad0b..d063fcf4f2d 100644 --- a/spec/javascripts/filtered_search/recent_searches_root_spec.js +++ b/spec/javascripts/filtered_search/recent_searches_root_spec.js @@ -15,8 +15,7 @@ describe('RecentSearchesRoot', () => { }; VueSpy = spyOnDependency(RecentSearchesRoot, 'Vue').and.callFake((options) => { - data = options.data; - template = options.template; + ({ data, template } = options); }); RecentSearchesRoot.prototype.render.call(recentSearchesRoot); diff --git a/spec/javascripts/gl_field_errors_spec.js b/spec/javascripts/gl_field_errors_spec.js index 2839020b2ca..21c462cd040 100644 --- a/spec/javascripts/gl_field_errors_spec.js +++ b/spec/javascripts/gl_field_errors_spec.js @@ -18,7 +18,7 @@ describe('GL Style Field Errors', function() { expect(this.$form).toBeDefined(); expect(this.$form.length).toBe(1); expect(this.fieldErrors).toBeDefined(); - const inputs = this.fieldErrors.state.inputs; + const { inputs } = this.fieldErrors.state; expect(inputs.length).toBe(4); }); diff --git a/spec/javascripts/groups/components/app_spec.js b/spec/javascripts/groups/components/app_spec.js index 2b92c485f41..03d4b472b87 100644 --- a/spec/javascripts/groups/components/app_spec.js +++ b/spec/javascripts/groups/components/app_spec.js @@ -67,7 +67,7 @@ describe('AppComponent', () => { it('should return list of groups from store', () => { spyOn(vm.store, 'getGroups'); - const groups = vm.groups; + const { groups } = vm; expect(vm.store.getGroups).toHaveBeenCalled(); expect(groups).not.toBeDefined(); }); @@ -77,7 +77,7 @@ describe('AppComponent', () => { it('should return pagination info from store', () => { spyOn(vm.store, 'getPaginationInfo'); - const pageInfo = vm.pageInfo; + const { pageInfo } = vm; expect(vm.store.getPaginationInfo).toHaveBeenCalled(); expect(pageInfo).not.toBeDefined(); }); @@ -293,7 +293,7 @@ describe('AppComponent', () => { beforeEach(() => { groupItem = Object.assign({}, mockParentGroupItem); groupItem.children = mockChildren; - childGroupItem = groupItem.children[0]; + [childGroupItem] = groupItem.children; groupItem.isChildrenLoading = false; vm.targetGroup = childGroupItem; vm.targetParentGroup = groupItem; diff --git a/spec/javascripts/groups/components/group_item_spec.js b/spec/javascripts/groups/components/group_item_spec.js index 49a139855c8..d0cac5efc40 100644 --- a/spec/javascripts/groups/components/group_item_spec.js +++ b/spec/javascripts/groups/components/group_item_spec.js @@ -41,7 +41,7 @@ describe('GroupItemComponent', () => { describe('rowClass', () => { it('should return map of classes based on group details', () => { const classes = ['is-open', 'has-children', 'has-description', 'being-removed']; - const rowClass = vm.rowClass; + const { rowClass } = vm; expect(Object.keys(rowClass).length).toBe(classes.length); Object.keys(rowClass).forEach((className) => { diff --git a/spec/javascripts/ide/helpers.js b/spec/javascripts/ide/helpers.js index 9312e17704e..569fa5c7aae 100644 --- a/spec/javascripts/ide/helpers.js +++ b/spec/javascripts/ide/helpers.js @@ -1,3 +1,4 @@ +import * as pathUtils from 'path'; import { decorateData } from '~/ide/stores/utils'; import state from '~/ide/stores/state'; import commitState from '~/ide/stores/modules/commit/state'; @@ -14,13 +15,34 @@ export const resetStore = store => { store.replaceState(newState); }; -export const file = (name = 'name', id = name, type = '') => +export const file = (name = 'name', id = name, type = '', parent = null) => decorateData({ id, type, icon: 'icon', url: 'url', name, - path: name, + path: parent ? `${parent.path}/${name}` : name, + parentPath: parent ? parent.path : '', lastCommit: {}, }); + +export const createEntriesFromPaths = paths => + paths + .map(path => ({ + name: pathUtils.basename(path), + dir: pathUtils.dirname(path), + ext: pathUtils.extname(path), + })) + .reduce((entries, path, idx) => { + const { name } = path; + const parent = path.dir ? entries[path.dir] : null; + const type = path.ext ? 'blob' : 'tree'; + + const entry = file(name, (idx + 1).toString(), type, parent); + + return { + [entry.path]: entry, + ...entries, + }; + }, {}); diff --git a/spec/javascripts/ide/lib/diff/controller_spec.js b/spec/javascripts/ide/lib/diff/controller_spec.js index 96abd1dcd9e..90ebb95b687 100644 --- a/spec/javascripts/ide/lib/diff/controller_spec.js +++ b/spec/javascripts/ide/lib/diff/controller_spec.js @@ -63,7 +63,7 @@ describe('Multi-file editor library dirty diff controller', () => { [type]: true, }; - const range = getDecorator(change).range; + const { range } = getDecorator(change); expect(range.startLineNumber).toBe(1); expect(range.endLineNumber).toBe(2); diff --git a/spec/javascripts/ide/stores/actions/tree_spec.js b/spec/javascripts/ide/stores/actions/tree_spec.js index e0ef57a3966..cefed9ddb43 100644 --- a/spec/javascripts/ide/stores/actions/tree_spec.js +++ b/spec/javascripts/ide/stores/actions/tree_spec.js @@ -1,8 +1,11 @@ import Vue from 'vue'; +import testAction from 'spec/helpers/vuex_action_helper'; +import { showTreeEntry } from '~/ide/stores/actions/tree'; +import * as types from '~/ide/stores/mutation_types'; import store from '~/ide/stores'; import service from '~/ide/services'; import router from '~/ide/ide_router'; -import { file, resetStore } from '../../helpers'; +import { file, resetStore, createEntriesFromPaths } from '../../helpers'; describe('Multi-file store tree actions', () => { let projectTree; @@ -96,6 +99,37 @@ describe('Multi-file store tree actions', () => { }); }); + describe('showTreeEntry', () => { + beforeEach(() => { + const paths = [ + 'grandparent', + 'ancestor', + 'grandparent/parent', + 'grandparent/aunt', + 'grandparent/parent/child.txt', + 'grandparent/aunt/cousing.txt', + ]; + + Object.assign(store.state.entries, createEntriesFromPaths(paths)); + }); + + it('opens the parents', done => { + testAction( + showTreeEntry, + 'grandparent/parent/child.txt', + store.state, + [ + { type: types.SET_TREE_OPEN, payload: 'grandparent/parent' }, + { type: types.SET_TREE_OPEN, payload: 'grandparent' }, + ], + [ + { type: 'showTreeEntry' }, + ], + done, + ); + }); + }); + describe('getLastCommitData', () => { beforeEach(() => { spyOn(service, 'getTreeLastCommit').and.returnValue( diff --git a/spec/javascripts/namespace_select_spec.js b/spec/javascripts/namespace_select_spec.js index 3b2641f7646..07b82ce721e 100644 --- a/spec/javascripts/namespace_select_spec.js +++ b/spec/javascripts/namespace_select_spec.js @@ -22,7 +22,7 @@ describe('NamespaceSelect', () => { const dropdown = document.createElement('div'); // eslint-disable-next-line no-new new NamespaceSelect({ dropdown }); - glDropdownOptions = $.fn.glDropdown.calls.argsFor(0)[0]; + [glDropdownOptions] = $.fn.glDropdown.calls.argsFor(0); }); it('prevents click events', () => { @@ -43,7 +43,7 @@ describe('NamespaceSelect', () => { dropdown.dataset.isFilter = 'true'; // eslint-disable-next-line no-new new NamespaceSelect({ dropdown }); - glDropdownOptions = $.fn.glDropdown.calls.argsFor(0)[0]; + [glDropdownOptions] = $.fn.glDropdown.calls.argsFor(0); }); it('does not prevent click events', () => { diff --git a/spec/javascripts/notebook/cells/markdown_spec.js b/spec/javascripts/notebook/cells/markdown_spec.js index 8f8ba231ae8..0b1b11de1fd 100644 --- a/spec/javascripts/notebook/cells/markdown_spec.js +++ b/spec/javascripts/notebook/cells/markdown_spec.js @@ -14,6 +14,7 @@ describe('Markdown component', () => { beforeEach((done) => { json = getJSONFixture('blob/notebook/basic.json'); + // eslint-disable-next-line prefer-destructuring cell = json.cells[1]; vm = new Component({ diff --git a/spec/javascripts/pipelines/pipelines_table_row_spec.js b/spec/javascripts/pipelines/pipelines_table_row_spec.js index 78d8e9e572e..03ffc122795 100644 --- a/spec/javascripts/pipelines/pipelines_table_row_spec.js +++ b/spec/javascripts/pipelines/pipelines_table_row_spec.js @@ -24,7 +24,7 @@ describe('Pipelines Table Row', () => { preloadFixtures(jsonFixtureName); beforeEach(() => { - const pipelines = getJSONFixture(jsonFixtureName).pipelines; + const { pipelines } = getJSONFixture(jsonFixtureName); pipeline = pipelines.find(p => p.user !== null && p.commit !== null); pipelineWithoutAuthor = pipelines.find(p => p.user === null && p.commit !== null); diff --git a/spec/javascripts/pipelines/pipelines_table_spec.js b/spec/javascripts/pipelines/pipelines_table_spec.js index 4fc3c08145e..d21ba35e96d 100644 --- a/spec/javascripts/pipelines/pipelines_table_spec.js +++ b/spec/javascripts/pipelines/pipelines_table_spec.js @@ -11,7 +11,7 @@ describe('Pipelines Table', () => { preloadFixtures(jsonFixtureName); beforeEach(() => { - const pipelines = getJSONFixture(jsonFixtureName).pipelines; + const { pipelines } = getJSONFixture(jsonFixtureName); PipelinesTableComponent = Vue.extend(pipelinesTableComp); pipeline = pipelines.find(p => p.user !== null && p.commit !== null); diff --git a/spec/javascripts/smart_interval_spec.js b/spec/javascripts/smart_interval_spec.js index a54219d58c2..60153672214 100644 --- a/spec/javascripts/smart_interval_spec.js +++ b/spec/javascripts/smart_interval_spec.js @@ -87,7 +87,7 @@ describe('SmartInterval', function () { setTimeout(() => { interval.cancel(); - const intervalId = interval.state.intervalId; + const { intervalId } = interval.state; const currentInterval = interval.getCurrentInterval(); const intervalLowerLimit = interval.cfg.startingInterval; @@ -106,7 +106,7 @@ describe('SmartInterval', function () { interval.resume(); - const intervalId = interval.state.intervalId; + const { intervalId } = interval.state; expect(intervalId).toBeTruthy(); diff --git a/spec/javascripts/vue_shared/components/file_icon_spec.js b/spec/javascripts/vue_shared/components/file_icon_spec.js index f7581251bf0..1c666fc6c55 100644 --- a/spec/javascripts/vue_shared/components/file_icon_spec.js +++ b/spec/javascripts/vue_shared/components/file_icon_spec.js @@ -74,7 +74,7 @@ describe('File Icon component', () => { size: 120, }); - const classList = vm.$el.firstChild.classList; + const { classList } = vm.$el.firstChild; const containsSizeClass = classList.contains('s120'); const containsCustomClass = classList.contains('extraclasses'); expect(containsSizeClass).toBe(true); diff --git a/spec/javascripts/vue_shared/components/icon_spec.js b/spec/javascripts/vue_shared/components/icon_spec.js index 68d57ebc8f0..cc030e29d61 100644 --- a/spec/javascripts/vue_shared/components/icon_spec.js +++ b/spec/javascripts/vue_shared/components/icon_spec.js @@ -44,7 +44,7 @@ describe('Sprite Icon Component', function () { }); it('should properly render img css', function () { - const classList = icon.$el.classList; + const { classList } = icon.$el; const containsSizeClass = classList.contains('s32'); const containsCustomClass = classList.contains('extraclasses'); expect(containsSizeClass).toBe(true); diff --git a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js index 446f025c127..656b57d764e 100644 --- a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js +++ b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js @@ -51,7 +51,7 @@ describe('User Avatar Image Component', function () { }); it('should properly render img css', function () { - const classList = vm.$el.classList; + const { classList } = vm.$el; const containsAvatar = classList.contains('avatar'); const containsSizeClass = classList.contains('s99'); const containsCustomClass = classList.contains(DEFAULT_PROPS.cssClasses); @@ -73,7 +73,7 @@ describe('User Avatar Image Component', function () { }); it('should add lazy attributes', function () { - const classList = vm.$el.classList; + const { classList } = vm.$el; const lazyClass = classList.contains('lazy'); expect(lazyClass).toBe(true); diff --git a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js index adf80d0c2bb..4c5c242cbb3 100644 --- a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js +++ b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js @@ -21,7 +21,7 @@ describe('User Avatar Link Component', function () { propsData: this.propsData, }).$mount(); - this.userAvatarImage = this.userAvatarLink.$children[0]; + [this.userAvatarImage] = this.userAvatarLink.$children; }); it('should return a defined Vue component', function () { diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index 64f51d9843d..9bb6ed62393 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -155,6 +155,12 @@ describe API::Branches do end it_behaves_like 'repository branch' + + it 'returns that the current user cannot push' do + get api(route, current_user) + + expect(json_response['can_push']).to eq(false) + end end context 'when unauthenticated', 'and project is private' do @@ -169,6 +175,12 @@ describe API::Branches do it_behaves_like 'repository branch' + it 'returns that the current user can push' do + get api(route, current_user) + + expect(json_response['can_push']).to eq(true) + end + context 'when branch contains a dot' do let(:branch_name) { branch_with_dot.name } @@ -202,6 +214,23 @@ describe API::Branches do end end + context 'when authenticated', 'as a developer and branch is protected' do + let(:current_user) { create(:user) } + let!(:protected_branch) { create(:protected_branch, project: project, name: branch_name) } + + before do + project.add_developer(current_user) + end + + it_behaves_like 'repository branch' + + it 'returns that the current user cannot push' do + get api(route, current_user) + + expect(json_response['can_push']).to eq(false) + end + end + context 'when authenticated', 'as a guest' do it_behaves_like '403 response' do let(:request) { get api(route, guest) } |