diff options
382 files changed, 5649 insertions, 2175 deletions
diff --git a/.codeclimate.yml b/.codeclimate.yml index e5636a13783..42afed54371 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -10,10 +10,10 @@ engines: languages: - ruby - javascript + exclude_paths: + - "lib/api/v3/*" eslint: enabled: true - fixme: - enabled: true rubocop: enabled: true ratings: @@ -35,4 +35,13 @@ exclude_paths: - node_modules/ - spec/ - vendor/ -- lib/api/v3/ +- .yarn-cache/ +- tmp/ +- builds/ +- coverage/ +- public/ +- shared/ +- webpack-report/ +- log/ +- backups/ +- coverage-javascript/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b442e48a3d0..c6868931657 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -456,6 +456,21 @@ karma: paths: - coverage-javascript/ +codeclimate: + before_script: [] + image: docker:latest + stage: test + variables: + SETUP_DB: "false" + DOCKER_DRIVER: overlay + services: + - docker:dind + script: + - docker pull codeclimate/codeclimate + - docker run --env CODECLIMATE_CODE="$PWD" --volume "$PWD":/code --volume /var/run/docker.sock:/var/run/docker.sock --volume /tmp/cc:/tmp/cc codeclimate/codeclimate analyze -f json > codeclimate.json + artifacts: + paths: [codeclimate.json] + coverage: stage: post-test services: [] diff --git a/.rubocop.yml b/.rubocop.yml index 66a40f2cf57..4537e710dd4 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1064,6 +1064,13 @@ RSpec/NotToNot: RSpec/RepeatedDescription: Enabled: false +# Ensure RSpec hook blocks are always multi-line. +RSpec/SingleLineHook: + Enabled: true + Exclude: + - 'spec/factories/*' + - 'spec/requests/api/v3/*' + # Checks for stubbed test subjects. RSpec/SubjectStub: Enabled: false diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.js b/app/assets/javascripts/commit/pipelines/pipelines_table.js index 082fbafb740..70ba83ce5b9 100644 --- a/app/assets/javascripts/commit/pipelines/pipelines_table.js +++ b/app/assets/javascripts/commit/pipelines/pipelines_table.js @@ -1,6 +1,6 @@ import Vue from 'vue'; import Visibility from 'visibilityjs'; -import pipelinesTableComponent from '../../vue_shared/components/pipelines_table'; +import pipelinesTableComponent from '../../vue_shared/components/pipelines_table.vue'; import PipelinesService from '../../pipelines/services/pipelines_service'; import PipelineStore from '../../pipelines/stores/pipelines_store'; import eventHub from '../../pipelines/event_hub'; diff --git a/app/assets/javascripts/environments/components/environment_item.vue b/app/assets/javascripts/environments/components/environment_item.vue index de2269118cd..614637b637e 100644 --- a/app/assets/javascripts/environments/components/environment_item.vue +++ b/app/assets/javascripts/environments/components/environment_item.vue @@ -9,7 +9,7 @@ import StopComponent from './environment_stop.vue'; import RollbackComponent from './environment_rollback.vue'; import TerminalButtonComponent from './environment_terminal_button.vue'; import MonitoringButtonComponent from './environment_monitoring.vue'; -import CommitComponent from '../../vue_shared/components/commit'; +import CommitComponent from '../../vue_shared/components/commit.vue'; import eventHub from '../event_hub'; /** diff --git a/app/assets/javascripts/environments/stores/environments_store.js b/app/assets/javascripts/environments/stores/environments_store.js index 8a2f6a473de..a5773dd7e4f 100644 --- a/app/assets/javascripts/environments/stores/environments_store.js +++ b/app/assets/javascripts/environments/stores/environments_store.js @@ -158,5 +158,4 @@ export default class EnvironmentsStore { return environments.filter(env => env.isFolder && env.isOpen); } - } diff --git a/app/assets/javascripts/milestone.js b/app/assets/javascripts/milestone.js index 841b24a60a3..07ede5ee913 100644 --- a/app/assets/javascripts/milestone.js +++ b/app/assets/javascripts/milestone.js @@ -65,14 +65,18 @@ }; Milestone.successCallback = function(data, element) { - var img_tag; - if (data.assignee) { - img_tag = $('<img/>'); - img_tag.attr('src', data.assignee.avatar_url); - img_tag.addClass('avatar s16'); - $(element).find('.assignee-icon img').replaceWith(img_tag); - } else { - $(element).find('.assignee-icon').empty(); + const $avatarContainer = $(element).find('.assignee-icon'); + $avatarContainer.empty(); + + if (data.assignees && data.assignees.length > 0) { + const $avatars = data.assignees.map((assignee) => { + const img_tag = $('<img/>'); + img_tag.attr('src', assignee.avatar_url); + img_tag.addClass('avatar s16'); + return img_tag; + }); + + $avatarContainer.append($avatars); } }; @@ -161,9 +165,9 @@ data = (function() { switch (newState) { case 'ongoing': - return opts.fieldName + '[assignee_id]=' + gon.current_user_id; + return `${opts.fieldName}[assignee_ids][]=${gon.current_user_id}`; case 'unassigned': - return opts.fieldName + '[assignee_id]='; + return `${opts.fieldName}[assignee_ids][]=0`; case 'closed': return opts.fieldName + '[state_event]=close'; } diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index b0143b12cfe..d56cf959486 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -56,6 +56,7 @@ const normalizeNewlines = function(str) { this.toggleCommitList = this.toggleCommitList.bind(this); this.postComment = this.postComment.bind(this); this.clearFlashWrapper = this.clearFlash.bind(this); + this.onHashChange = this.onHashChange.bind(this); this.notes_url = notes_url; this.note_ids = note_ids; @@ -127,7 +128,9 @@ const normalizeNewlines = function(str) { $(document).on('ajax:success', '.js-main-target-form', this.resetMainTargetForm); $(document).on('ajax:complete', '.js-main-target-form', this.reenableTargetFormSubmitButton); // when a key is clicked on the notes - return $(document).on('keydown', '.js-note-text', this.keydownNoteText); + $(document).on('keydown', '.js-note-text', this.keydownNoteText); + // When the URL fragment/hash has changed, `#note_xxx` + return $(window).on('hashchange', this.onHashChange); }; Notes.prototype.cleanBinding = function() { @@ -148,6 +151,7 @@ const normalizeNewlines = function(str) { $(document).off('ajax:success', '.js-main-target-form'); $(document).off('ajax:success', '.js-discussion-note-form'); $(document).off('ajax:complete', '.js-main-target-form'); + $(window).off('hashchange', this.onHashChange); }; Notes.initCommentTypeToggle = function (form) { @@ -298,8 +302,27 @@ const normalizeNewlines = function(str) { Notes.prototype.setupNewNote = function($note) { // Update datetime format on the recent note gl.utils.localTimeAgo($note.find('.js-timeago'), false); + this.collapseLongCommitList(); this.taskList.init(); + + // This stops the note highlight, #note_xxx`, from being removed after real time update + // The `:target` selector does not re-evaluate after we replace element in the DOM + Notes.updateNoteTargetSelector($note); + this.$noteToCleanHighlight = $note; + }; + + Notes.prototype.onHashChange = function() { + if (this.$noteToCleanHighlight) { + Notes.updateNoteTargetSelector(this.$noteToCleanHighlight); + } + + this.$noteToCleanHighlight = null; + }; + + Notes.updateNoteTargetSelector = function($note) { + const hash = gl.utils.getLocationHash(); + $note.toggleClass('target', hash && $note.filter(`#${hash}`).length > 0); }; /* @@ -597,13 +620,12 @@ const normalizeNewlines = function(str) { $noteEntityEl = $(noteEntity.html); $noteEntityEl.addClass('fade-in-full'); this.revertNoteEditForm($targetNote); - gl.utils.localTimeAgo($('.js-timeago', $noteEntityEl)); $noteEntityEl.renderGFM(); - $noteEntityEl.find('.js-task-list-container').taskList('enable'); // Find the note's `li` element by ID and replace it with the updated HTML $note_li = $('.note-row-' + noteEntity.id); $note_li.replaceWith($noteEntityEl); + this.setupNewNote($noteEntityEl); if (typeof gl.diffNotesCompileComponents !== 'undefined') { gl.diffNotesCompileComponents(); @@ -1060,7 +1082,7 @@ const normalizeNewlines = function(str) { var targetId = $originalContentEl.data('target-id'); var targetType = $originalContentEl.data('target-type'); - new gl.GLForm($editForm.find('form')); + new gl.GLForm($editForm.find('form'), this.enableGFM); $editForm.find('form') .attr('action', postUrl) diff --git a/app/assets/javascripts/pipelines/components/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines.vue new file mode 100644 index 00000000000..fed42d23112 --- /dev/null +++ b/app/assets/javascripts/pipelines/components/pipelines.vue @@ -0,0 +1,289 @@ +<script> + import Visibility from 'visibilityjs'; + import PipelinesService from '../services/pipelines_service'; + import eventHub from '../event_hub'; + import pipelinesTableComponent from '../../vue_shared/components/pipelines_table.vue'; + import tablePagination from '../../vue_shared/components/table_pagination.vue'; + import emptyState from './empty_state.vue'; + import errorState from './error_state.vue'; + import navigationTabs from './navigation_tabs.vue'; + import navigationControls from './nav_controls.vue'; + import loadingIcon from '../../vue_shared/components/loading_icon.vue'; + import Poll from '../../lib/utils/poll'; + + export default { + props: { + store: { + type: Object, + required: true, + }, + }, + components: { + tablePagination, + pipelinesTableComponent, + emptyState, + errorState, + navigationTabs, + navigationControls, + loadingIcon, + }, + data() { + const pipelinesData = document.querySelector('#pipelines-list-vue').dataset; + + return { + endpoint: pipelinesData.endpoint, + cssClass: pipelinesData.cssClass, + helpPagePath: pipelinesData.helpPagePath, + newPipelinePath: pipelinesData.newPipelinePath, + canCreatePipeline: pipelinesData.canCreatePipeline, + allPath: pipelinesData.allPath, + pendingPath: pipelinesData.pendingPath, + runningPath: pipelinesData.runningPath, + finishedPath: pipelinesData.finishedPath, + branchesPath: pipelinesData.branchesPath, + tagsPath: pipelinesData.tagsPath, + hasCi: pipelinesData.hasCi, + ciLintPath: pipelinesData.ciLintPath, + state: this.store.state, + apiScope: 'all', + pagenum: 1, + isLoading: false, + hasError: false, + isMakingRequest: false, + updateGraphDropdown: false, + hasMadeRequest: false, + }; + }, + computed: { + canCreatePipelineParsed() { + return gl.utils.convertPermissionToBoolean(this.canCreatePipeline); + }, + scope() { + const scope = gl.utils.getParameterByName('scope'); + return scope === null ? 'all' : scope; + }, + shouldRenderErrorState() { + return this.hasError && !this.isLoading; + }, + + /** + * The empty state should only be rendered when the request is made to fetch all pipelines + * and none is returned. + * + * @return {Boolean} + */ + shouldRenderEmptyState() { + return !this.isLoading && + !this.hasError && + this.hasMadeRequest && + !this.state.pipelines.length && + (this.scope === 'all' || this.scope === null); + }, + /** + * When a specific scope does not have pipelines we render a message. + * + * @return {Boolean} + */ + shouldRenderNoPipelinesMessage() { + return !this.isLoading && + !this.hasError && + !this.state.pipelines.length && + this.scope !== 'all' && + this.scope !== null; + }, + + shouldRenderTable() { + return !this.hasError && + !this.isLoading && this.state.pipelines.length; + }, + /** + * Pagination should only be rendered when there is more than one page. + * + * @return {Boolean} + */ + shouldRenderPagination() { + return !this.isLoading && + this.state.pipelines.length && + this.state.pageInfo.total > this.state.pageInfo.perPage; + }, + + hasCiEnabled() { + return this.hasCi !== undefined; + }, + paths() { + return { + allPath: this.allPath, + pendingPath: this.pendingPath, + finishedPath: this.finishedPath, + runningPath: this.runningPath, + branchesPath: this.branchesPath, + tagsPath: this.tagsPath, + }; + }, + pageParameter() { + return gl.utils.getParameterByName('page') || this.pagenum; + }, + scopeParameter() { + return gl.utils.getParameterByName('scope') || this.apiScope; + }, + }, + created() { + this.service = new PipelinesService(this.endpoint); + + const poll = new Poll({ + resource: this.service, + method: 'getPipelines', + data: { page: this.pageParameter, scope: this.scopeParameter }, + successCallback: this.successCallback, + errorCallback: this.errorCallback, + notificationCallback: this.setIsMakingRequest, + }); + + if (!Visibility.hidden()) { + this.isLoading = true; + poll.makeRequest(); + } else { + // If tab is not visible we need to make the first request so we don't show the empty + // state without knowing if there are any pipelines + this.fetchPipelines(); + } + + Visibility.change(() => { + if (!Visibility.hidden()) { + poll.restart(); + } else { + poll.stop(); + } + }); + + eventHub.$on('refreshPipelines', this.fetchPipelines); + }, + beforeDestroy() { + eventHub.$off('refreshPipelines'); + }, + methods: { + /** + * Will change the page number and update the URL. + * + * @param {Number} pageNumber desired page to go to. + */ + change(pageNumber) { + const param = gl.utils.setParamInURL('page', pageNumber); + + gl.utils.visitUrl(param); + return param; + }, + + fetchPipelines() { + if (!this.isMakingRequest) { + this.isLoading = true; + + this.service.getPipelines({ scope: this.scopeParameter, page: this.pageParameter }) + .then(response => this.successCallback(response)) + .catch(() => this.errorCallback()); + } + }, + successCallback(resp) { + const response = { + headers: resp.headers, + body: resp.json(), + }; + + this.store.storeCount(response.body.count); + this.store.storePipelines(response.body.pipelines); + this.store.storePagination(response.headers); + + this.isLoading = false; + this.updateGraphDropdown = true; + this.hasMadeRequest = true; + }, + + errorCallback() { + this.hasError = true; + this.isLoading = false; + this.updateGraphDropdown = false; + }, + + setIsMakingRequest(isMakingRequest) { + this.isMakingRequest = isMakingRequest; + + if (isMakingRequest) { + this.updateGraphDropdown = false; + } + }, + }, + }; +</script> +<template> + <div :class="cssClass"> + + <div + class="top-area scrolling-tabs-container inner-page-scroll-tabs" + v-if="!isLoading && !shouldRenderEmptyState"> + <div class="fade-left"> + <i + class="fa fa-angle-left" + aria-hidden="true"> + </i> + </div> + <div class="fade-right"> + <i + class="fa fa-angle-right" + aria-hidden="true"> + </i> + </div> + <navigation-tabs + :scope="scope" + :count="state.count" + :paths="paths" + /> + + <navigation-controls + :new-pipeline-path="newPipelinePath" + :has-ci-enabled="hasCiEnabled" + :help-page-path="helpPagePath" + :ciLintPath="ciLintPath" + :can-create-pipeline="canCreatePipelineParsed " + /> + </div> + + <div class="content-list pipelines"> + + <loading-icon + label="Loading Pipelines" + size="3" + v-if="isLoading" + /> + + <empty-state + v-if="shouldRenderEmptyState" + :help-page-path="helpPagePath" + /> + + <error-state v-if="shouldRenderErrorState" /> + + <div + class="blank-state blank-state-no-icon" + v-if="shouldRenderNoPipelinesMessage"> + <h2 class="blank-state-title js-blank-state-title">No pipelines to show.</h2> + </div> + + <div + class="table-holder" + v-if="shouldRenderTable"> + + <pipelines-table-component + :pipelines="state.pipelines" + :service="service" + :update-graph-dropdown="updateGraphDropdown" + /> + </div> + + <table-pagination + v-if="shouldRenderPagination" + :change="change" + :pageInfo="state.pageInfo" + /> + </div> + </div> +</template> diff --git a/app/assets/javascripts/pipelines/components/pipelines_actions.js b/app/assets/javascripts/pipelines/components/pipelines_actions.js deleted file mode 100644 index b9e066c5db1..00000000000 --- a/app/assets/javascripts/pipelines/components/pipelines_actions.js +++ /dev/null @@ -1,91 +0,0 @@ -/* eslint-disable no-new */ -/* global Flash */ -import '~/flash'; -import playIconSvg from 'icons/_icon_play.svg'; -import eventHub from '../event_hub'; -import loadingIconComponent from '../../vue_shared/components/loading_icon.vue'; - -export default { - props: { - actions: { - type: Array, - required: true, - }, - - service: { - type: Object, - required: true, - }, - }, - - components: { - loadingIconComponent, - }, - - data() { - return { - playIconSvg, - isLoading: false, - }; - }, - - methods: { - onClickAction(endpoint) { - this.isLoading = true; - - $(this.$refs.tooltip).tooltip('destroy'); - - this.service.postAction(endpoint) - .then(() => { - this.isLoading = false; - eventHub.$emit('refreshPipelines'); - }) - .catch(() => { - this.isLoading = false; - new Flash('An error occured while making the request.'); - }); - }, - - isActionDisabled(action) { - if (action.playable === undefined) { - return false; - } - - return !action.playable; - }, - }, - - template: ` - <div class="btn-group" v-if="actions"> - <button - type="button" - class="dropdown-toggle btn btn-default has-tooltip js-pipeline-dropdown-manual-actions" - title="Manual job" - data-toggle="dropdown" - data-placement="top" - aria-label="Manual job" - ref="tooltip" - :disabled="isLoading"> - ${playIconSvg} - <i - class="fa fa-caret-down" - aria-hidden="true" /> - <loading-icon v-if="isLoading" /> - </button> - - <ul class="dropdown-menu dropdown-menu-align-right"> - <li v-for="action in actions"> - <button - type="button" - class="js-pipeline-action-link no-btn btn" - @click="onClickAction(action.path)" - :class="{ 'disabled': isActionDisabled(action) }" - :disabled="isActionDisabled(action)"> - ${playIconSvg} - <span>{{action.name}}</span> - </button> - </li> - </ul> - </div> - `, -}; diff --git a/app/assets/javascripts/pipelines/components/pipelines_actions.vue b/app/assets/javascripts/pipelines/components/pipelines_actions.vue new file mode 100644 index 00000000000..da5df2a06cf --- /dev/null +++ b/app/assets/javascripts/pipelines/components/pipelines_actions.vue @@ -0,0 +1,88 @@ +<script> + /* global Flash */ + import '~/flash'; + import playIconSvg from 'icons/_icon_play.svg'; + import eventHub from '../event_hub'; + import loadingIcon from '../../vue_shared/components/loading_icon.vue'; + + export default { + props: { + actions: { + type: Array, + required: true, + }, + service: { + type: Object, + required: true, + }, + }, + components: { + loadingIcon, + }, + data() { + return { + playIconSvg, + isLoading: false, + }; + }, + methods: { + onClickAction(endpoint) { + this.isLoading = true; + + $(this.$refs.tooltip).tooltip('destroy'); + + this.service.postAction(endpoint) + .then(() => { + this.isLoading = false; + eventHub.$emit('refreshPipelines'); + }) + .catch(() => { + this.isLoading = false; + // eslint-disable-next-line no-new + new Flash('An error occured while making the request.'); + }); + }, + isActionDisabled(action) { + if (action.playable === undefined) { + return false; + } + + return !action.playable; + }, + }, + }; +</script> +<template> + <div class="btn-group"> + <button + type="button" + class="dropdown-toggle btn btn-default has-tooltip js-pipeline-dropdown-manual-actions" + title="Manual job" + data-toggle="dropdown" + data-placement="top" + aria-label="Manual job" + ref="tooltip" + :disabled="isLoading"> + <span v-html="playIconSvg"></span> + <i + class="fa fa-caret-down" + aria-hidden="true"> + </i> + <loading-icon v-if="isLoading" /> + </button> + + <ul class="dropdown-menu dropdown-menu-align-right"> + <li v-for="action in actions"> + <button + type="button" + class="js-pipeline-action-link no-btn btn" + @click="onClickAction(action.path)" + :class="{ disabled: isActionDisabled(action) }" + :disabled="isActionDisabled(action)"> + <span v-html="playIconSvg"></span> + <span>{{action.name}}</span> + </button> + </li> + </ul> + </div> +</template> diff --git a/app/assets/javascripts/pipelines/components/pipelines_artifacts.js b/app/assets/javascripts/pipelines/components/pipelines_artifacts.js deleted file mode 100644 index f18e2dfadaf..00000000000 --- a/app/assets/javascripts/pipelines/components/pipelines_artifacts.js +++ /dev/null @@ -1,33 +0,0 @@ -export default { - props: { - artifacts: { - type: Array, - required: true, - }, - }, - - template: ` - <div class="btn-group" role="group"> - <button - class="dropdown-toggle btn btn-default build-artifacts has-tooltip js-pipeline-dropdown-download" - title="Artifacts" - data-placement="top" - data-toggle="dropdown" - aria-label="Artifacts"> - <i class="fa fa-download" aria-hidden="true"></i> - <i class="fa fa-caret-down" aria-hidden="true"></i> - </button> - <ul class="dropdown-menu dropdown-menu-align-right"> - <li v-for="artifact in artifacts"> - <a - rel="nofollow" - download - :href="artifact.path"> - <i class="fa fa-download" aria-hidden="true"></i> - <span>Download {{artifact.name}} artifacts</span> - </a> - </li> - </ul> - </div> - `, -}; diff --git a/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue b/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue new file mode 100644 index 00000000000..b4520481cdc --- /dev/null +++ b/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue @@ -0,0 +1,51 @@ +<script> + import tooltipMixin from '../../vue_shared/mixins/tooltip'; + + export default { + props: { + artifacts: { + type: Array, + required: true, + }, + }, + mixins: [ + tooltipMixin, + ], + }; +</script> +<template> + <div + class="btn-group" + role="group"> + <button + class="dropdown-toggle btn btn-default build-artifacts js-pipeline-dropdown-download" + title="Artifacts" + data-placement="top" + data-toggle="dropdown" + aria-label="Artifacts" + ref="tooltip"> + <i + class="fa fa-download" + aria-hidden="true"> + </i> + <i + class="fa fa-caret-down" + aria-hidden="true"> + </i> + </button> + <ul class="dropdown-menu dropdown-menu-align-right"> + <li v-for="artifact in artifacts"> + <a + rel="nofollow" + download + :href="artifact.path"> + <i + class="fa fa-download" + aria-hidden="true"> + </i> + <span>Download {{artifact.name}} artifacts</span> + </a> + </li> + </ul> + </div> +</template> diff --git a/app/assets/javascripts/pipelines/components/time_ago.js b/app/assets/javascripts/pipelines/components/time_ago.js deleted file mode 100644 index 188f74cc705..00000000000 --- a/app/assets/javascripts/pipelines/components/time_ago.js +++ /dev/null @@ -1,98 +0,0 @@ -import iconTimerSvg from 'icons/_icon_timer.svg'; -import '../../lib/utils/datetime_utility'; - -export default { - props: { - finishedTime: { - type: String, - required: true, - }, - - duration: { - type: Number, - required: true, - }, - }, - - data() { - return { - iconTimerSvg, - }; - }, - - updated() { - $(this.$refs.tooltip).tooltip('fixTitle'); - }, - - computed: { - hasDuration() { - return this.duration > 0; - }, - - hasFinishedTime() { - return this.finishedTime !== ''; - }, - - localTimeFinished() { - return gl.utils.formatDate(this.finishedTime); - }, - - durationFormated() { - const date = new Date(this.duration * 1000); - - let hh = date.getUTCHours(); - let mm = date.getUTCMinutes(); - let ss = date.getSeconds(); - - // left pad - if (hh < 10) { - hh = `0${hh}`; - } - if (mm < 10) { - mm = `0${mm}`; - } - if (ss < 10) { - ss = `0${ss}`; - } - - return `${hh}:${mm}:${ss}`; - }, - - finishedTimeFormated() { - const timeAgo = gl.utils.getTimeago(); - - return timeAgo.format(this.finishedTime); - }, - }, - - template: ` - <td class="pipelines-time-ago"> - <p - class="duration" - v-if="hasDuration"> - <span - v-html="iconTimerSvg"> - </span> - {{durationFormated}} - </p> - - <p - class="finished-at" - v-if="hasFinishedTime"> - - <i - class="fa fa-calendar" - aria-hidden="true" /> - - <time - ref="tooltip" - data-toggle="tooltip" - data-placement="top" - data-container="body" - :title="localTimeFinished"> - {{finishedTimeFormated}} - </time> - </p> - </td> - `, -}; diff --git a/app/assets/javascripts/pipelines/components/time_ago.vue b/app/assets/javascripts/pipelines/components/time_ago.vue new file mode 100644 index 00000000000..c47658cd6e6 --- /dev/null +++ b/app/assets/javascripts/pipelines/components/time_ago.vue @@ -0,0 +1,85 @@ +<script> + import iconTimerSvg from 'icons/_icon_timer.svg'; + import '../../lib/utils/datetime_utility'; + import tooltipMixin from '../../vue_shared/mixins/tooltip'; + import timeagoMixin from '../../vue_shared/mixins/timeago'; + + export default { + props: { + finishedTime: { + type: String, + required: true, + }, + duration: { + type: Number, + required: true, + }, + }, + mixins: [ + tooltipMixin, + timeagoMixin, + ], + data() { + return { + iconTimerSvg, + }; + }, + computed: { + hasDuration() { + return this.duration > 0; + }, + hasFinishedTime() { + return this.finishedTime !== ''; + }, + durationFormated() { + const date = new Date(this.duration * 1000); + + let hh = date.getUTCHours(); + let mm = date.getUTCMinutes(); + let ss = date.getSeconds(); + + // left pad + if (hh < 10) { + hh = `0${hh}`; + } + if (mm < 10) { + mm = `0${mm}`; + } + if (ss < 10) { + ss = `0${ss}`; + } + + return `${hh}:${mm}:${ss}`; + }, + }, + }; +</script> +<template> + <td class="pipelines-time-ago"> + <p + class="duration" + v-if="hasDuration"> + <span v-html="iconTimerSvg"> + </span> + {{durationFormated}} + </p> + + <p + class="finished-at" + v-if="hasFinishedTime"> + + <i + class="fa fa-calendar" + aria-hidden="true"> + </i> + + <time + ref="tooltip" + data-placement="top" + data-container="body" + :title="tooltipTitle(finishedTime)"> + {{timeFormated(finishedTime)}} + </time> + </p> + </td> +</script> diff --git a/app/assets/javascripts/pipelines/index.js b/app/assets/javascripts/pipelines/index.js deleted file mode 100644 index 48f9181a8d9..00000000000 --- a/app/assets/javascripts/pipelines/index.js +++ /dev/null @@ -1,22 +0,0 @@ -import Vue from 'vue'; -import PipelinesStore from './stores/pipelines_store'; -import PipelinesComponent from './pipelines'; -import '../vue_shared/vue_resource_interceptor'; - -$(() => new Vue({ - el: document.querySelector('#pipelines-list-vue'), - - data() { - const store = new PipelinesStore(); - - return { - store, - }; - }, - components: { - 'vue-pipelines': PipelinesComponent, - }, - template: ` - <vue-pipelines :store="store" /> - `, -})); diff --git a/app/assets/javascripts/pipelines/pipelines.js b/app/assets/javascripts/pipelines/pipelines.js deleted file mode 100644 index b530461837c..00000000000 --- a/app/assets/javascripts/pipelines/pipelines.js +++ /dev/null @@ -1,293 +0,0 @@ -import Visibility from 'visibilityjs'; -import PipelinesService from './services/pipelines_service'; -import eventHub from './event_hub'; -import pipelinesTableComponent from '../vue_shared/components/pipelines_table'; -import tablePagination from '../vue_shared/components/table_pagination.vue'; -import emptyState from './components/empty_state.vue'; -import errorState from './components/error_state.vue'; -import navigationTabs from './components/navigation_tabs.vue'; -import navigationControls from './components/nav_controls.vue'; -import loadingIcon from '../vue_shared/components/loading_icon.vue'; -import Poll from '../lib/utils/poll'; - -export default { - props: { - store: { - type: Object, - required: true, - }, - }, - - components: { - tablePagination, - pipelinesTableComponent, - emptyState, - errorState, - navigationTabs, - navigationControls, - loadingIcon, - }, - - data() { - const pipelinesData = document.querySelector('#pipelines-list-vue').dataset; - - return { - endpoint: pipelinesData.endpoint, - cssClass: pipelinesData.cssClass, - helpPagePath: pipelinesData.helpPagePath, - newPipelinePath: pipelinesData.newPipelinePath, - canCreatePipeline: pipelinesData.canCreatePipeline, - allPath: pipelinesData.allPath, - pendingPath: pipelinesData.pendingPath, - runningPath: pipelinesData.runningPath, - finishedPath: pipelinesData.finishedPath, - branchesPath: pipelinesData.branchesPath, - tagsPath: pipelinesData.tagsPath, - hasCi: pipelinesData.hasCi, - ciLintPath: pipelinesData.ciLintPath, - state: this.store.state, - apiScope: 'all', - pagenum: 1, - isLoading: false, - hasError: false, - isMakingRequest: false, - updateGraphDropdown: false, - hasMadeRequest: false, - }; - }, - - computed: { - canCreatePipelineParsed() { - return gl.utils.convertPermissionToBoolean(this.canCreatePipeline); - }, - - scope() { - const scope = gl.utils.getParameterByName('scope'); - return scope === null ? 'all' : scope; - }, - - shouldRenderErrorState() { - return this.hasError && !this.isLoading; - }, - - /** - * The empty state should only be rendered when the request is made to fetch all pipelines - * and none is returned. - * - * @return {Boolean} - */ - shouldRenderEmptyState() { - return !this.isLoading && - !this.hasError && - this.hasMadeRequest && - !this.state.pipelines.length && - (this.scope === 'all' || this.scope === null); - }, - - /** - * When a specific scope does not have pipelines we render a message. - * - * @return {Boolean} - */ - shouldRenderNoPipelinesMessage() { - return !this.isLoading && - !this.hasError && - !this.state.pipelines.length && - this.scope !== 'all' && - this.scope !== null; - }, - - shouldRenderTable() { - return !this.hasError && - !this.isLoading && this.state.pipelines.length; - }, - - /** - * Pagination should only be rendered when there is more than one page. - * - * @return {Boolean} - */ - shouldRenderPagination() { - return !this.isLoading && - this.state.pipelines.length && - this.state.pageInfo.total > this.state.pageInfo.perPage; - }, - - hasCiEnabled() { - return this.hasCi !== undefined; - }, - - paths() { - return { - allPath: this.allPath, - pendingPath: this.pendingPath, - finishedPath: this.finishedPath, - runningPath: this.runningPath, - branchesPath: this.branchesPath, - tagsPath: this.tagsPath, - }; - }, - - pageParameter() { - return gl.utils.getParameterByName('page') || this.pagenum; - }, - - scopeParameter() { - return gl.utils.getParameterByName('scope') || this.apiScope; - }, - }, - - created() { - this.service = new PipelinesService(this.endpoint); - - const poll = new Poll({ - resource: this.service, - method: 'getPipelines', - data: { page: this.pageParameter, scope: this.scopeParameter }, - successCallback: this.successCallback, - errorCallback: this.errorCallback, - notificationCallback: this.setIsMakingRequest, - }); - - if (!Visibility.hidden()) { - this.isLoading = true; - poll.makeRequest(); - } else { - // If tab is not visible we need to make the first request so we don't show the empty - // state without knowing if there are any pipelines - this.fetchPipelines(); - } - - Visibility.change(() => { - if (!Visibility.hidden()) { - poll.restart(); - } else { - poll.stop(); - } - }); - - eventHub.$on('refreshPipelines', this.fetchPipelines); - }, - - beforeDestroy() { - eventHub.$off('refreshPipelines'); - }, - - methods: { - /** - * Will change the page number and update the URL. - * - * @param {Number} pageNumber desired page to go to. - */ - change(pageNumber) { - const param = gl.utils.setParamInURL('page', pageNumber); - - gl.utils.visitUrl(param); - return param; - }, - - fetchPipelines() { - if (!this.isMakingRequest) { - this.isLoading = true; - - this.service.getPipelines({ scope: this.scopeParameter, page: this.pageParameter }) - .then(response => this.successCallback(response)) - .catch(() => this.errorCallback()); - } - }, - - successCallback(resp) { - const response = { - headers: resp.headers, - body: resp.json(), - }; - - this.store.storeCount(response.body.count); - this.store.storePipelines(response.body.pipelines); - this.store.storePagination(response.headers); - - this.isLoading = false; - this.updateGraphDropdown = true; - this.hasMadeRequest = true; - }, - - errorCallback() { - this.hasError = true; - this.isLoading = false; - this.updateGraphDropdown = false; - }, - - setIsMakingRequest(isMakingRequest) { - this.isMakingRequest = isMakingRequest; - - if (isMakingRequest) { - this.updateGraphDropdown = false; - } - }, - }, - - template: ` - <div :class="cssClass"> - - <div - class="top-area scrolling-tabs-container inner-page-scroll-tabs" - v-if="!isLoading && !shouldRenderEmptyState"> - <div class="fade-left"> - <i class="fa fa-angle-left" aria-hidden="true"></i> - </div> - <div class="fade-right"> - <i class="fa fa-angle-right" aria-hidden="true"></i> - </div> - <navigation-tabs - :scope="scope" - :count="state.count" - :paths="paths" /> - - <navigation-controls - :new-pipeline-path="newPipelinePath" - :has-ci-enabled="hasCiEnabled" - :help-page-path="helpPagePath" - :ciLintPath="ciLintPath" - :can-create-pipeline="canCreatePipelineParsed " /> - </div> - - <div class="content-list pipelines"> - - <loading-icon - label="Loading Pipelines" - size="3" - v-if="isLoading" - /> - - <empty-state - v-if="shouldRenderEmptyState" - :help-page-path="helpPagePath" /> - - <error-state v-if="shouldRenderErrorState" /> - - <div - class="blank-state blank-state-no-icon" - v-if="shouldRenderNoPipelinesMessage"> - <h2 class="blank-state-title js-blank-state-title">No pipelines to show.</h2> - </div> - - <div - class="table-holder" - v-if="shouldRenderTable"> - - <pipelines-table-component - :pipelines="state.pipelines" - :service="service" - :update-graph-dropdown="updateGraphDropdown" - /> - </div> - - <table-pagination - v-if="shouldRenderPagination" - :change="change" - :pageInfo="state.pageInfo" - /> - </div> - </div> - `, -}; diff --git a/app/assets/javascripts/pipelines/pipelines_bundle.js b/app/assets/javascripts/pipelines/pipelines_bundle.js new file mode 100644 index 00000000000..923d9bfb248 --- /dev/null +++ b/app/assets/javascripts/pipelines/pipelines_bundle.js @@ -0,0 +1,24 @@ +import Vue from 'vue'; +import PipelinesStore from './stores/pipelines_store'; +import pipelinesComponent from './components/pipelines.vue'; + +document.addEventListener('DOMContentLoaded', () => new Vue({ + el: '#pipelines-list-vue', + data() { + const store = new PipelinesStore(); + + return { + store, + }; + }, + components: { + pipelinesComponent, + }, + render(createElement) { + return createElement('pipelines-component', { + props: { + store: this.store, + }, + }); + }, +})); diff --git a/app/assets/javascripts/vue_shared/components/commit.js b/app/assets/javascripts/vue_shared/components/commit.js deleted file mode 100644 index ff5ae28e062..00000000000 --- a/app/assets/javascripts/vue_shared/components/commit.js +++ /dev/null @@ -1,159 +0,0 @@ -import commitIconSvg from 'icons/_icon_commit.svg'; -import userAvatarLink from './user_avatar/user_avatar_link.vue'; - -export default { - props: { - /** - * Indicates the existance of a tag. - * Used to render the correct icon, if true will render `fa-tag` icon, - * if false will render `fa-code-fork` icon. - */ - tag: { - type: Boolean, - required: false, - default: false, - }, - - /** - * If provided is used to render the branch name and url. - * Should contain the following properties: - * name - * ref_url - */ - commitRef: { - type: Object, - required: false, - default: () => ({}), - }, - - /** - * Used to link to the commit sha. - */ - commitUrl: { - type: String, - required: false, - default: '', - }, - - /** - * Used to show the commit short sha that links to the commit url. - */ - shortSha: { - type: String, - required: false, - default: '', - }, - - /** - * If provided shows the commit tile. - */ - title: { - type: String, - required: false, - default: '', - }, - - /** - * If provided renders information about the author of the commit. - * When provided should include: - * `avatar_url` to render the avatar icon - * `web_url` to link to user profile - * `username` to render alt and title tags - */ - author: { - type: Object, - required: false, - default: () => ({}), - }, - }, - - computed: { - /** - * Used to verify if all the properties needed to render the commit - * ref section were provided. - * - * TODO: Improve this! Use lodash _.has when we have it. - * - * @returns {Boolean} - */ - hasCommitRef() { - return this.commitRef && this.commitRef.name && this.commitRef.ref_url; - }, - - /** - * Used to verify if all the properties needed to render the commit - * author section were provided. - * - * TODO: Improve this! Use lodash _.has when we have it. - * - * @returns {Boolean} - */ - hasAuthor() { - return this.author && - this.author.avatar_url && - this.author.path && - this.author.username; - }, - - /** - * If information about the author is provided will return a string - * to be rendered as the alt attribute of the img tag. - * - * @returns {String} - */ - userImageAltDescription() { - return this.author && - this.author.username ? `${this.author.username}'s avatar` : null; - }, - }, - - data() { - return { commitIconSvg }; - }, - - components: { - userAvatarLink, - }, - template: ` - <div class="branch-commit"> - - <div v-if="hasCommitRef" class="icon-container"> - <i v-if="tag" class="fa fa-tag"></i> - <i v-if="!tag" class="fa fa-code-fork"></i> - </div> - - <a v-if="hasCommitRef" - class="ref-name" - :href="commitRef.ref_url"> - {{commitRef.name}} - </a> - - <div v-html="commitIconSvg" class="commit-icon js-commit-icon"></div> - - <a class="commit-sha" - :href="commitUrl"> - {{shortSha}} - </a> - - <div class="commit-title flex-truncate-parent"> - <span v-if="title" class="flex-truncate-child"> - <user-avatar-link - v-if="hasAuthor" - class="avatar-image-container" - :link-href="author.path" - :img-src="author.avatar_url" - :img-alt="userImageAltDescription" - :tooltip-text="author.username" - /> - <a class="commit-row-message" - :href="commitUrl"> - {{title}} - </a> - </span> - <span v-else> - Cant find HEAD commit for this branch - </span> - </div> - </div> - `, -}; diff --git a/app/assets/javascripts/vue_shared/components/commit.vue b/app/assets/javascripts/vue_shared/components/commit.vue new file mode 100644 index 00000000000..fcf48b11057 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/commit.vue @@ -0,0 +1,166 @@ +<script> + import commitIconSvg from 'icons/_icon_commit.svg'; + import userAvatarLink from './user_avatar/user_avatar_link.vue'; + + export default { + props: { + /** + * Indicates the existance of a tag. + * Used to render the correct icon, if true will render `fa-tag` icon, + * if false will render `fa-code-fork` icon. + */ + tag: { + type: Boolean, + required: false, + default: false, + }, + /** + * If provided is used to render the branch name and url. + * Should contain the following properties: + * name + * ref_url + */ + commitRef: { + type: Object, + required: false, + default: () => ({}), + }, + /** + * Used to link to the commit sha. + */ + commitUrl: { + type: String, + required: false, + default: '', + }, + + /** + * Used to show the commit short sha that links to the commit url. + */ + shortSha: { + type: String, + required: false, + default: '', + }, + /** + * If provided shows the commit tile. + */ + title: { + type: String, + required: false, + default: '', + }, + /** + * If provided renders information about the author of the commit. + * When provided should include: + * `avatar_url` to render the avatar icon + * `web_url` to link to user profile + * `username` to render alt and title tags + */ + author: { + type: Object, + required: false, + default: () => ({}), + }, + }, + computed: { + /** + * Used to verify if all the properties needed to render the commit + * ref section were provided. + * + * TODO: Improve this! Use lodash _.has when we have it. + * + * @returns {Boolean} + */ + hasCommitRef() { + return this.commitRef && this.commitRef.name && this.commitRef.ref_url; + }, + /** + * Used to verify if all the properties needed to render the commit + * author section were provided. + * + * TODO: Improve this! Use lodash _.has when we have it. + * + * @returns {Boolean} + */ + hasAuthor() { + return this.author && + this.author.avatar_url && + this.author.path && + this.author.username; + }, + /** + * If information about the author is provided will return a string + * to be rendered as the alt attribute of the img tag. + * + * @returns {String} + */ + userImageAltDescription() { + return this.author && + this.author.username ? `${this.author.username}'s avatar` : null; + }, + }, + data() { + return { commitIconSvg }; + }, + components: { + userAvatarLink, + }, + }; +</script> +<template> + <div class="branch-commit"> + <div v-if="hasCommitRef" class="icon-container"> + <i + v-if="tag" + class="fa fa-tag" + aria-hidden="true"> + </i> + <i + v-if="!tag" + class="fa fa-code-fork" + aria-hidden="true"> + </i> + </div> + + <a + v-if="hasCommitRef" + class="ref-name" + :href="commitRef.ref_url"> + {{commitRef.name}} + </a> + + <div + v-html="commitIconSvg" + class="commit-icon js-commit-icon"> + </div> + + <a + class="commit-sha" + :href="commitUrl"> + {{shortSha}} + </a> + + <div class="commit-title flex-truncate-parent"> + <span + v-if="title" + class="flex-truncate-child"> + <user-avatar-link + v-if="hasAuthor" + class="avatar-image-container" + :link-href="author.path" + :img-src="author.avatar_url" + :img-alt="userImageAltDescription" + :tooltip-text="author.username" + /> + <a class="commit-row-message" + :href="commitUrl"> + {{title}} + </a> + </span> + <span v-else> + Cant find HEAD commit for this branch + </span> + </div> + </div> +</template> diff --git a/app/assets/javascripts/vue_shared/components/pipelines_table.js b/app/assets/javascripts/vue_shared/components/pipelines_table.js deleted file mode 100644 index 48a39f18112..00000000000 --- a/app/assets/javascripts/vue_shared/components/pipelines_table.js +++ /dev/null @@ -1,55 +0,0 @@ -import PipelinesTableRowComponent from './pipelines_table_row'; - -/** - * Pipelines Table Component. - * - * Given an array of objects, renders a table. - */ -export default { - props: { - pipelines: { - type: Array, - required: true, - }, - - service: { - type: Object, - required: true, - }, - - updateGraphDropdown: { - type: Boolean, - required: false, - default: false, - }, - }, - - components: { - 'pipelines-table-row-component': PipelinesTableRowComponent, - }, - - template: ` - <table class="table ci-table"> - <thead> - <tr> - <th class="js-pipeline-status pipeline-status">Status</th> - <th class="js-pipeline-info pipeline-info">Pipeline</th> - <th class="js-pipeline-commit pipeline-commit">Commit</th> - <th class="js-pipeline-stages pipeline-stages">Stages</th> - <th class="js-pipeline-date pipeline-date"></th> - <th class="js-pipeline-actions pipeline-actions"></th> - </tr> - </thead> - <tbody> - <template v-for="model in pipelines" - v-bind:model="model"> - <tr is="pipelines-table-row-component" - :pipeline="model" - :service="service" - :update-graph-dropdown="updateGraphDropdown" - /> - </template> - </tbody> - </table> - `, -}; diff --git a/app/assets/javascripts/vue_shared/components/pipelines_table.vue b/app/assets/javascripts/vue_shared/components/pipelines_table.vue new file mode 100644 index 00000000000..ebe8fba8a44 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/pipelines_table.vue @@ -0,0 +1,55 @@ +<script> + import pipelinesTableRowComponent from './pipelines_table_row.vue'; + + /** + * Pipelines Table Component. + * + * Given an array of objects, renders a table. + */ + export default { + props: { + pipelines: { + type: Array, + required: true, + }, + service: { + type: Object, + required: true, + }, + updateGraphDropdown: { + type: Boolean, + required: false, + default: false, + }, + }, + components: { + pipelinesTableRowComponent, + }, + }; +</script> +<template> + <table class="table ci-table"> + <thead> + <tr> + <th class="js-pipeline-status pipeline-status">Status</th> + <th class="js-pipeline-info pipeline-info">Pipeline</th> + <th class="js-pipeline-commit pipeline-commit">Commit</th> + <th class="js-pipeline-stages pipeline-stages">Stages</th> + <th class="js-pipeline-date pipeline-date"></th> + <th class="js-pipeline-actions pipeline-actions"></th> + </tr> + </thead> + <tbody> + <template + v-for="model in pipelines" + :model="model"> + <tr + is="pipelines-table-row-component" + :pipeline="model" + :service="service" + :update-graph-dropdown="updateGraphDropdown" + /> + </template> + </tbody> + </table> +</template> diff --git a/app/assets/javascripts/vue_shared/components/pipelines_table_row.js b/app/assets/javascripts/vue_shared/components/pipelines_table_row.vue index f60f8eeb43d..33b3375deff 100644 --- a/app/assets/javascripts/vue_shared/components/pipelines_table_row.js +++ b/app/assets/javascripts/vue_shared/components/pipelines_table_row.vue @@ -1,12 +1,13 @@ +<script> /* eslint-disable no-param-reassign */ -import AsyncButtonComponent from '../../pipelines/components/async_button.vue'; -import PipelinesActionsComponent from '../../pipelines/components/pipelines_actions'; -import PipelinesArtifactsComponent from '../../pipelines/components/pipelines_artifacts'; +import asyncButtonComponent from '../../pipelines/components/async_button.vue'; +import pipelinesActionsComponent from '../../pipelines/components/pipelines_actions.vue'; +import pipelinesArtifactsComponent from '../../pipelines/components/pipelines_artifacts.vue'; import ciBadge from './ci_badge_link.vue'; -import PipelinesStageComponent from '../../pipelines/components/stage.vue'; -import PipelinesUrlComponent from '../../pipelines/components/pipeline_url.vue'; -import PipelinesTimeagoComponent from '../../pipelines/components/time_ago'; -import CommitComponent from './commit'; +import pipelineStage from '../../pipelines/components/stage.vue'; +import pipelineUrl from '../../pipelines/components/pipeline_url.vue'; +import pipelinesTimeago from '../../pipelines/components/time_ago.vue'; +import commitComponent from './commit.vue'; /** * Pipeline table row. @@ -19,30 +20,26 @@ export default { type: Object, required: true, }, - service: { type: Object, required: true, }, - updateGraphDropdown: { type: Boolean, required: false, default: false, }, }, - components: { - 'async-button-component': AsyncButtonComponent, - 'pipelines-actions-component': PipelinesActionsComponent, - 'pipelines-artifacts-component': PipelinesArtifactsComponent, - 'commit-component': CommitComponent, - 'dropdown-stage': PipelinesStageComponent, - 'pipeline-url': PipelinesUrlComponent, + asyncButtonComponent, + pipelinesActionsComponent, + pipelinesArtifactsComponent, + commitComponent, + pipelineStage, + pipelineUrl, ciBadge, - 'time-ago': PipelinesTimeagoComponent, + pipelinesTimeago, }, - computed: { /** * If provided, returns the commit tag. @@ -204,69 +201,76 @@ export default { return {}; }, }, +}; +</script> +<template> + <tr class="commit"> + <td class="commit-link"> + <ci-badge :status="pipelineStatus" /> + </td> - template: ` - <tr class="commit"> - <td class="commit-link"> - <ci-badge :status="pipelineStatus"/> - </td> - - <pipeline-url :pipeline="pipeline"></pipeline-url> + <pipeline-url :pipeline="pipeline" /> - <td> - <commit-component - :tag="commitTag" - :commit-ref="commitRef" - :commit-url="commitUrl" - :short-sha="commitShortSha" - :title="commitTitle" - :author="commitAuthor"/> - </td> + <td> + <commit-component + :tag="commitTag" + :commit-ref="commitRef" + :commit-url="commitUrl" + :short-sha="commitShortSha" + :title="commitTitle" + :author="commitAuthor" + /> + </td> - <td class="stage-cell"> - <div class="stage-container dropdown js-mini-pipeline-graph" - v-if="pipeline.details.stages.length > 0" - v-for="stage in pipeline.details.stages"> + <td class="stage-cell"> + <div class="stage-container dropdown js-mini-pipeline-graph" + v-if="pipeline.details.stages.length > 0" + v-for="stage in pipeline.details.stages"> - <dropdown-stage - :stage="stage" - :update-dropdown="updateGraphDropdown"/> - </div> - </td> + <pipeline-stage + :stage="stage" + :update-dropdown="updateGraphDropdown" + /> + </div> + </td> - <time-ago - :duration="pipelineDuration" - :finished-time="pipelineFinishedAt" /> + <pipelines-timeago + :duration="pipelineDuration" + :finished-time="pipelineFinishedAt" + /> - <td class="pipeline-actions"> - <div class="pull-right btn-group"> - <pipelines-actions-component - v-if="pipeline.details.manual_actions.length" - :actions="pipeline.details.manual_actions" - :service="service" /> + <td class="pipeline-actions"> + <div class="pull-right btn-group"> + <pipelines-actions-component + v-if="pipeline.details.manual_actions.length" + :actions="pipeline.details.manual_actions" + :service="service" + /> - <pipelines-artifacts-component - v-if="pipeline.details.artifacts.length" - :artifacts="pipeline.details.artifacts" /> + <pipelines-artifacts-component + v-if="pipeline.details.artifacts.length" + :artifacts="pipeline.details.artifacts" + /> - <async-button-component - v-if="pipeline.flags.retryable" - :service="service" - :endpoint="pipeline.retry_path" - css-class="js-pipelines-retry-button btn-default btn-retry" - title="Retry" - icon="repeat" /> + <async-button-component + v-if="pipeline.flags.retryable" + :service="service" + :endpoint="pipeline.retry_path" + css-class="js-pipelines-retry-button btn-default btn-retry" + title="Retry" + icon="repeat" + /> - <async-button-component - v-if="pipeline.flags.cancelable" - :service="service" - :endpoint="pipeline.cancel_path" - css-class="js-pipelines-cancel-button btn-remove" - title="Cancel" - icon="remove" - confirm-action-message="Are you sure you want to cancel this pipeline?" /> - </div> - </td> - </tr> - `, -}; + <async-button-component + v-if="pipeline.flags.cancelable" + :service="service" + :endpoint="pipeline.cancel_path" + css-class="js-pipelines-cancel-button btn-remove" + title="Cancel" + icon="remove" + confirm-action-message="Are you sure you want to cancel this pipeline?" + /> + </div> + </td> + </tr> +</template> diff --git a/app/assets/stylesheets/framework/panels.scss b/app/assets/stylesheets/framework/panels.scss index 9d8d08dff88..fa364e68d22 100644 --- a/app/assets/stylesheets/framework/panels.scss +++ b/app/assets/stylesheets/framework/panels.scss @@ -34,6 +34,10 @@ } } + .panel-empty-heading { + border-bottom: 0; + } + .panel-body { padding: $gl-padding; diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss index 5ae833cd5f6..1b20c35ad98 100644 --- a/app/assets/stylesheets/framework/selects.scss +++ b/app/assets/stylesheets/framework/selects.scss @@ -109,10 +109,12 @@ line-height: 15px; background-color: $gray-light; background-image: none; + padding: 3px 18px 3px 5px; .select2-search-choice-close { - top: 4px; - left: 3px; + top: 5px; + left: initial; + right: 3px; } &.select2-search-choice-focus { diff --git a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss index c9f345d24be..b666223b120 100644 --- a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss +++ b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss @@ -74,9 +74,9 @@ $pagination-hover-color: $gl-text-color; $pagination-hover-bg: $row-hover; $pagination-hover-border: $border-color; -$pagination-active-color: $blue-600; -$pagination-active-bg: $white-light; -$pagination-active-border: $border-color; +$pagination-active-color: $white-light; +$pagination-active-bg: $gl-link-color; +$pagination-active-border: $gl-link-color; $pagination-disabled-color: #cdcdcd; $pagination-disabled-bg: $gray-light; diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index 740e383dbb5..85109fec91a 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -1,3 +1,5 @@ +@import "./issues/issue_count_badge"; + [v-cloak] { display: none; } @@ -133,7 +135,7 @@ } .board-list-component, - .board-issue-count-holder { + .issue-count-badge { display: none; } } @@ -429,30 +431,6 @@ margin: 5px; } -.board-issue-count-holder { - margin-top: -3px; - - .btn { - line-height: 12px; - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } -} - -.board-issue-count { - padding-right: 10px; - padding-left: 10px; - line-height: 21px; - border-radius: $border-radius-base; - border: 1px solid $border-color; - - &.has-btn { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - border-width: 1px 0 1px 1px; - } -} - .page-with-layout-nav.page-with-sub-nav .issue-boards-sidebar { &.right-sidebar { top: 0; diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index 39022714d28..7eee0a71c66 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -150,6 +150,7 @@ overflow-y: scroll; overflow-x: hidden; padding: 10px 20px 20px 5px; + white-space: pre; } .environment-information { diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss index 5b723f7c722..4c3fa1fb8d4 100644 --- a/app/assets/stylesheets/pages/events.scss +++ b/app/assets/stylesheets/pages/events.scss @@ -89,7 +89,6 @@ background: $gray-light; border-radius: 0; color: $events-pre-color; - margin: 0 20px; overflow: hidden; } diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index f923a1104a9..8cdb3f34ae5 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -1,3 +1,5 @@ +@import "./issues/issue_count_badge"; + .issues-list { .issue { padding: 10px 0 10px $gl-padding; diff --git a/app/assets/stylesheets/pages/issues/issue_count_badge.scss b/app/assets/stylesheets/pages/issues/issue_count_badge.scss new file mode 100644 index 00000000000..ccb62bfed18 --- /dev/null +++ b/app/assets/stylesheets/pages/issues/issue_count_badge.scss @@ -0,0 +1,29 @@ +.issue-count-badge { + display: inline-flex; + align-items: stretch; + height: 24px; +} + +.issue-count-badge-count { + display: flex; + align-items: center; + padding-right: 10px; + padding-left: 10px; + border: 1px solid $border-color; + border-radius: $border-radius-base; + line-height: 1; + + &.has-btn { + border-right: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } +} + +.issue-count-badge-add-button { + display: flex; + align-items: center; + border: 1px solid $border-color; + border-radius: 0 $border-radius-base $border-radius-base 0; + line-height: 1; +} diff --git a/app/assets/stylesheets/pages/pipeline_schedules.scss b/app/assets/stylesheets/pages/pipeline_schedules.scss index ab417948931..595eb40fec7 100644 --- a/app/assets/stylesheets/pages/pipeline_schedules.scss +++ b/app/assets/stylesheets/pages/pipeline_schedules.scss @@ -12,7 +12,7 @@ .interval-pattern-form-group { label { margin-right: 10px; - font-size: 12px; + font-weight: normal; &[for='custom'] { margin-right: 0; diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 75fb19e815f..4d4b8a8425f 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -100,6 +100,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :enabled_git_access_protocol, :gravatar_enabled, :help_page_text, + :help_page_hide_commercial_content, + :help_page_support_url, :home_page_url, :housekeeping_bitmaps_enabled, :housekeeping_enabled, diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index cb4bd0ad5f5..603a51266da 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -80,10 +80,6 @@ class Projects::ApplicationController < ApplicationController cookies.permanent[:diff_view] = params.delete(:view) if params[:view].present? end - def builds_enabled - return render_404 unless @project.feature_available?(:builds, current_user) - end - def require_pages_enabled! not_found unless Gitlab.config.pages.enabled end diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb index 43fc0c39801..df5221fe95f 100644 --- a/app/controllers/projects/graphs_controller.rb +++ b/app/controllers/projects/graphs_controller.rb @@ -5,7 +5,6 @@ class Projects::GraphsController < Projects::ApplicationController before_action :require_non_empty_project before_action :assign_ref_vars before_action :authorize_download_code! - before_action :builds_enabled, only: :ci def show respond_to do |format| diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 6223e7943f8..8effb792689 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -4,7 +4,6 @@ class Projects::PipelinesController < Projects::ApplicationController before_action :authorize_read_pipeline! before_action :authorize_create_pipeline!, only: [:new, :create] before_action :authorize_update_pipeline!, only: [:retry, :cancel] - before_action :builds_enabled, only: :charts wrap_parameters Ci::Pipeline diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 71154da7ec5..2bfc7586adc 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -204,6 +204,10 @@ module ApplicationHelper 'https://' + promo_host end + def support_url + current_application_settings.help_page_support_url.presence || promo_url + '/getting-help/' + end + def page_filter_path(options = {}) without = options.delete(:without) add_label = options.delete(:label) diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index 2ae3a616933..06822747d11 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -124,6 +124,30 @@ module DiffHelper !diff_file.deleted_file? && @merge_request && @merge_request.source_project end + def diff_render_error_reason(viewer) + case viewer.render_error + when :too_large + "it is too large" + when :server_side_but_stored_externally + case viewer.diff_file.external_storage + when :lfs + 'it is stored in LFS' + else + 'it is stored externally' + end + end + end + + def diff_render_error_options(viewer) + diff_file = viewer.diff_file + options = [] + + blob_url = namespace_project_blob_path(@project.namespace, @project, tree_join(diff_file.content_sha, diff_file.file_path)) + options << link_to('view the blob', blob_url) + + options + end + private def diff_btn(title, name, selected) diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 7441b58fddb..c11dd49f4a7 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -218,6 +218,10 @@ module ProjectsHelper nav_tabs << :container_registry end + if project.builds_enabled? && can?(current_user, :read_pipeline, project) + nav_tabs << :pipelines + end + tab_ability_map.each do |tab, ability| if can?(current_user, ability, project) nav_tabs << tab @@ -231,7 +235,6 @@ module ProjectsHelper { environments: :read_environment, milestones: :read_milestone, - pipelines: :read_pipeline, snippets: :read_project_snippet, settings: :admin_project, builds: :read_build, diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 2192f76499d..668caef0d2c 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -37,7 +37,12 @@ class ApplicationSetting < ActiveRecord::Base validates :home_page_url, allow_blank: true, url: true, - if: :home_page_url_column_exist + if: :home_page_url_column_exists? + + validates :help_page_support_url, + allow_blank: true, + url: true, + if: :help_page_support_url_column_exists? validates :after_sign_out_path, allow_blank: true, @@ -215,6 +220,7 @@ class ApplicationSetting < ActiveRecord::Base domain_whitelist: Settings.gitlab['domain_whitelist'], gravatar_enabled: Settings.gravatar['enabled'], help_page_text: nil, + help_page_hide_commercial_content: false, unique_ips_limit_per_user: 10, unique_ips_limit_time_window: 3600, unique_ips_limit_enabled: false, @@ -263,10 +269,14 @@ class ApplicationSetting < ActiveRecord::Base end end - def home_page_url_column_exist + def home_page_url_column_exists? ActiveRecord::Base.connection.column_exists?(:application_settings, :home_page_url) end + def help_page_support_url_column_exists? + ActiveRecord::Base.connection.column_exists?(:application_settings, :help_page_support_url) + end + def sidekiq_throttling_column_exists? ActiveRecord::Base.connection.column_exists?(:application_settings, :sidekiq_throttling_enabled) end diff --git a/app/models/blob_viewer/server_side.rb b/app/models/blob_viewer/server_side.rb index e6bcacf7f70..fbc1b520c01 100644 --- a/app/models/blob_viewer/server_side.rb +++ b/app/models/blob_viewer/server_side.rb @@ -13,14 +13,12 @@ module BlobViewer end def render_error - if blob.stored_externally? - # Files that are not stored in the repository, like LFS files and - # build artifacts, can only be rendered using a client-side viewer, - # since we do not want to read large amounts of data into memory on the - # server side. Client-side viewers use JS and can fetch the file from - # `blob_raw_url` using AJAX. - return :server_side_but_stored_externally - end + # Files that are not stored in the repository, like LFS files and + # build artifacts, can only be rendered using a client-side viewer, + # since we do not want to read large amounts of data into memory on the + # server side. Client-side viewers use JS and can fetch the file from + # `blob_raw_url` using AJAX. + return :server_side_but_stored_externally if blob.stored_externally? super end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index cec1ca89a6a..58758f7ca8a 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -33,7 +33,7 @@ module Ci scope :with_artifacts_not_expired, ->() { with_artifacts.where('artifacts_expire_at IS NULL OR artifacts_expire_at > ?', Time.now) } scope :with_expired_artifacts, ->() { with_artifacts.where('artifacts_expire_at < ?', Time.now) } scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) } - scope :manual_actions, ->() { where(when: :manual).relevant } + scope :manual_actions, ->() { where(when: :manual, status: COMPLETED_STATUSES + [:manual]) } mount_uploader :artifacts_file, ArtifactUploader mount_uploader :artifacts_metadata, ArtifactUploader @@ -109,7 +109,7 @@ module Ci end def playable? - action? && manual? + action? && (manual? || complete?) end def action? diff --git a/app/models/diff_viewer/added.rb b/app/models/diff_viewer/added.rb new file mode 100644 index 00000000000..1909e6ef9d8 --- /dev/null +++ b/app/models/diff_viewer/added.rb @@ -0,0 +1,8 @@ +module DiffViewer + class Added < Base + include Simple + include Static + + self.partial_name = 'added' + end +end diff --git a/app/models/diff_viewer/base.rb b/app/models/diff_viewer/base.rb new file mode 100644 index 00000000000..0cbe714288d --- /dev/null +++ b/app/models/diff_viewer/base.rb @@ -0,0 +1,87 @@ +module DiffViewer + class Base + PARTIAL_PATH_PREFIX = 'projects/diffs/viewers'.freeze + + class_attribute :partial_name, :type, :extensions, :file_types, :binary, :switcher_icon, :switcher_title + + # These limits relate to the sum of the old and new blob sizes. + # Limits related to the actual size of the diff are enforced in Gitlab::Diff::File. + class_attribute :collapse_limit, :size_limit + + delegate :partial_path, :loading_partial_path, :rich?, :simple?, :text?, :binary?, to: :class + + attr_reader :diff_file + + delegate :project, to: :diff_file + + def initialize(diff_file) + @diff_file = diff_file + @initially_binary = diff_file.binary? + end + + def self.partial_path + File.join(PARTIAL_PATH_PREFIX, partial_name) + end + + def self.rich? + type == :rich + end + + def self.simple? + type == :simple + end + + def self.binary? + binary + end + + def self.text? + !binary? + end + + def self.can_render?(diff_file, verify_binary: true) + can_render_blob?(diff_file.old_blob, verify_binary: verify_binary) && + can_render_blob?(diff_file.new_blob, verify_binary: verify_binary) + end + + def self.can_render_blob?(blob, verify_binary: true) + return true if blob.nil? + return false if verify_binary && binary? != blob.binary? + return true if extensions&.include?(blob.extension) + return true if file_types&.include?(blob.file_type) + + false + end + + def collapsed? + return @collapsed if defined?(@collapsed) + return @collapsed = true if diff_file.collapsed? + + @collapsed = !diff_file.expanded? && collapse_limit && diff_file.raw_size > collapse_limit + end + + def too_large? + return @too_large if defined?(@too_large) + return @too_large = true if diff_file.too_large? + + @too_large = size_limit && diff_file.raw_size > size_limit + end + + def binary_detected_after_load? + !@initially_binary && diff_file.binary? + end + + # This method is used on the server side to check whether we can attempt to + # render the diff_file at all. Human-readable error messages are found in the + # `BlobHelper#diff_render_error_reason` helper. + def render_error + if too_large? + :too_large + end + end + + def prepare! + # To be overridden by subclasses + end + end +end diff --git a/app/models/diff_viewer/client_side.rb b/app/models/diff_viewer/client_side.rb new file mode 100644 index 00000000000..cf41d07f8eb --- /dev/null +++ b/app/models/diff_viewer/client_side.rb @@ -0,0 +1,10 @@ +module DiffViewer + module ClientSide + extend ActiveSupport::Concern + + included do + self.collapse_limit = 1.megabyte + self.size_limit = 10.megabytes + end + end +end diff --git a/app/models/diff_viewer/deleted.rb b/app/models/diff_viewer/deleted.rb new file mode 100644 index 00000000000..9c129bac694 --- /dev/null +++ b/app/models/diff_viewer/deleted.rb @@ -0,0 +1,8 @@ +module DiffViewer + class Deleted < Base + include Simple + include Static + + self.partial_name = 'deleted' + end +end diff --git a/app/models/diff_viewer/image.rb b/app/models/diff_viewer/image.rb new file mode 100644 index 00000000000..759d9a36ebb --- /dev/null +++ b/app/models/diff_viewer/image.rb @@ -0,0 +1,12 @@ +module DiffViewer + class Image < Base + include Rich + include ClientSide + + self.partial_name = 'image' + self.extensions = UploaderHelper::IMAGE_EXT + self.binary = true + self.switcher_icon = 'picture-o' + self.switcher_title = 'image diff' + end +end diff --git a/app/models/diff_viewer/mode_changed.rb b/app/models/diff_viewer/mode_changed.rb new file mode 100644 index 00000000000..d487d996f8d --- /dev/null +++ b/app/models/diff_viewer/mode_changed.rb @@ -0,0 +1,8 @@ +module DiffViewer + class ModeChanged < Base + include Simple + include Static + + self.partial_name = 'mode_changed' + end +end diff --git a/app/models/diff_viewer/no_preview.rb b/app/models/diff_viewer/no_preview.rb new file mode 100644 index 00000000000..5455fee4490 --- /dev/null +++ b/app/models/diff_viewer/no_preview.rb @@ -0,0 +1,9 @@ +module DiffViewer + class NoPreview < Base + include Simple + include Static + + self.partial_name = 'no_preview' + self.binary = true + end +end diff --git a/app/models/diff_viewer/not_diffable.rb b/app/models/diff_viewer/not_diffable.rb new file mode 100644 index 00000000000..4f9638626ea --- /dev/null +++ b/app/models/diff_viewer/not_diffable.rb @@ -0,0 +1,9 @@ +module DiffViewer + class NotDiffable < Base + include Simple + include Static + + self.partial_name = 'not_diffable' + self.binary = true + end +end diff --git a/app/models/diff_viewer/renamed.rb b/app/models/diff_viewer/renamed.rb new file mode 100644 index 00000000000..f1fbfd8c6d5 --- /dev/null +++ b/app/models/diff_viewer/renamed.rb @@ -0,0 +1,8 @@ +module DiffViewer + class Renamed < Base + include Simple + include Static + + self.partial_name = 'renamed' + end +end diff --git a/app/models/diff_viewer/rich.rb b/app/models/diff_viewer/rich.rb new file mode 100644 index 00000000000..3b0ca6e4cff --- /dev/null +++ b/app/models/diff_viewer/rich.rb @@ -0,0 +1,11 @@ +module DiffViewer + module Rich + extend ActiveSupport::Concern + + included do + self.type = :rich + self.switcher_icon = 'file-text-o' + self.switcher_title = 'rendered diff' + end + end +end diff --git a/app/models/diff_viewer/server_side.rb b/app/models/diff_viewer/server_side.rb new file mode 100644 index 00000000000..aed1a0791b1 --- /dev/null +++ b/app/models/diff_viewer/server_side.rb @@ -0,0 +1,26 @@ +module DiffViewer + module ServerSide + extend ActiveSupport::Concern + + included do + self.collapse_limit = 1.megabyte + self.size_limit = 5.megabytes + end + + def prepare! + diff_file.old_blob&.load_all_data! + diff_file.new_blob&.load_all_data! + end + + def render_error + # Files that are not stored in the repository, like LFS files and + # build artifacts, can only be rendered using a client-side viewer, + # since we do not want to read large amounts of data into memory on the + # server side. Client-side viewers use JS and can fetch the file from + # `diff_file_blob_raw_path` and `diff_file_old_blob_raw_path` using AJAX. + return :server_side_but_stored_externally if diff_file.stored_externally? + + super + end + end +end diff --git a/app/models/diff_viewer/simple.rb b/app/models/diff_viewer/simple.rb new file mode 100644 index 00000000000..65750996ee4 --- /dev/null +++ b/app/models/diff_viewer/simple.rb @@ -0,0 +1,11 @@ +module DiffViewer + module Simple + extend ActiveSupport::Concern + + included do + self.type = :simple + self.switcher_icon = 'code' + self.switcher_title = 'source diff' + end + end +end diff --git a/app/models/diff_viewer/static.rb b/app/models/diff_viewer/static.rb new file mode 100644 index 00000000000..d761328b3f6 --- /dev/null +++ b/app/models/diff_viewer/static.rb @@ -0,0 +1,10 @@ +module DiffViewer + module Static + extend ActiveSupport::Concern + + # We can always render a static viewer, even if the diff is too large. + def render_error + nil + end + end +end diff --git a/app/models/diff_viewer/text.rb b/app/models/diff_viewer/text.rb new file mode 100644 index 00000000000..98f4b2aea2a --- /dev/null +++ b/app/models/diff_viewer/text.rb @@ -0,0 +1,15 @@ +module DiffViewer + class Text < Base + include Simple + include ServerSide + + self.partial_name = 'text' + self.binary = false + + # Since the text diff viewer doesn't render the old and new blobs in full, + # we only need the limits related to the actual size of the diff which are + # already enforced in Gitlab::Diff::File. + self.collapse_limit = nil + self.size_limit = nil + end +end diff --git a/app/models/environment.rb b/app/models/environment.rb index 6211a5c1e63..d5b974b2d31 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -209,7 +209,8 @@ class Environment < ActiveRecord::Base def etag_cache_key Gitlab::Routing.url_helpers.namespace_project_environments_path( project.namespace, - project) + project, + format: :json) end private diff --git a/app/models/generic_commit_status.rb b/app/models/generic_commit_status.rb index 8867ba0d2ff..532b8f4ad69 100644 --- a/app/models/generic_commit_status.rb +++ b/app/models/generic_commit_status.rb @@ -11,6 +11,7 @@ class GenericCommitStatus < CommitStatus def set_default_values self.context ||= 'default' self.stage ||= 'external' + self.stage_idx ||= 1000000 end def tags diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb index 8977a7cdafe..48e7802c557 100644 --- a/app/models/project_services/kubernetes_service.rb +++ b/app/models/project_services/kubernetes_service.rb @@ -116,30 +116,19 @@ class KubernetesService < DeploymentService # short time later def terminals(environment) with_reactive_cache do |data| - pods = data.fetch(:pods, nil) - filter_pods(pods, app: environment.slug). - flat_map { |pod| terminals_for_pod(api_url, actual_namespace, pod) }. - each { |terminal| add_terminal_auth(terminal, terminal_auth) } + pods = filter_by_label(data[:pods], app: environment.slug) + terminals = pods.flat_map { |pod| terminals_for_pod(api_url, actual_namespace, pod) } + terminals.each { |terminal| add_terminal_auth(terminal, terminal_auth) } end end - # Caches all pods in the namespace so other calls don't need to block on - # network access. + # Caches resources in the namespace so other calls don't need to block on + # network access def calculate_reactive_cache return unless active? && project && !project.pending_delete? - kubeclient = build_kubeclient! - - # Store as hashes, rather than as third-party types - pods = begin - kubeclient.get_pods(namespace: actual_namespace).as_json - rescue KubeException => err - raise err unless err.error_code == 404 - [] - end - # We may want to cache extra things in the future - { pods: pods } + { pods: read_pods } end TEMPLATE_PLACEHOLDER = 'Kubernetes namespace'.freeze @@ -166,6 +155,16 @@ class KubernetesService < DeploymentService ) end + # Returns a hash of all pods in the namespace + def read_pods + kubeclient = build_kubeclient! + + kubeclient.get_pods(namespace: actual_namespace).as_json + rescue KubeException => err + raise err unless err.error_code == 404 + [] + end + def kubeclient_ssl_options opts = { verify_ssl: OpenSSL::SSL::VERIFY_PEER } @@ -181,11 +180,11 @@ class KubernetesService < DeploymentService { bearer_token: token } end - def join_api_url(*parts) + def join_api_url(api_path) url = URI.parse(api_url) prefix = url.path.sub(%r{/+\z}, '') - url.path = [prefix, *parts].join("/") + url.path = [prefix, api_path].join("/") url.to_s end diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index 3959b895f44..47518dddb61 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -203,7 +203,7 @@ class ProjectPolicy < BasePolicy unless project.feature_available?(:builds, user) && repository_enabled cannot!(*named_abilities(:build)) - cannot!(*named_abilities(:pipeline)) + cannot!(*named_abilities(:pipeline) - [:read_pipeline]) cannot!(*named_abilities(:pipeline_schedule)) cannot!(*named_abilities(:environment)) cannot!(*named_abilities(:deployment)) diff --git a/app/serializers/build_details_entity.rb b/app/serializers/build_details_entity.rb index 0eddbaaaebf..eeb5399aa8b 100644 --- a/app/serializers/build_details_entity.rb +++ b/app/serializers/build_details_entity.rb @@ -1,4 +1,4 @@ -class BuildDetailsEntity < BuildEntity +class BuildDetailsEntity < JobEntity expose :coverage, :erased_at, :duration expose :tag_list, as: :tags expose :user, using: UserEntity @@ -25,7 +25,7 @@ class BuildDetailsEntity < BuildEntity end expose :raw_path do |build| - raw_namespace_project_build_path(project.namespace, project, build) + raw_namespace_project_job_path(project.namespace, project, build) end private diff --git a/app/serializers/build_serializer.rb b/app/serializers/build_serializer.rb index 79b67001199..bae9932847f 100644 --- a/app/serializers/build_serializer.rb +++ b/app/serializers/build_serializer.rb @@ -1,5 +1,5 @@ class BuildSerializer < BaseSerializer - entity BuildEntity + entity JobEntity def represent_status(resource) data = represent(resource, { only: [:status] }) diff --git a/app/serializers/deployment_entity.rb b/app/serializers/deployment_entity.rb index 8b3de1bed0f..e493c9162fd 100644 --- a/app/serializers/deployment_entity.rb +++ b/app/serializers/deployment_entity.rb @@ -24,6 +24,6 @@ class DeploymentEntity < Grape::Entity expose :user, using: UserEntity expose :commit, using: CommitEntity - expose :deployable, using: BuildEntity - expose :manual_actions, using: BuildEntity + expose :deployable, using: JobEntity + expose :manual_actions, using: JobEntity end diff --git a/app/serializers/build_entity.rb b/app/serializers/job_entity.rb index 67001f4547d..d6de43bcbcb 100644 --- a/app/serializers/build_entity.rb +++ b/app/serializers/job_entity.rb @@ -1,11 +1,11 @@ -class BuildEntity < Grape::Entity +class JobEntity < Grape::Entity include RequestAwareEntity expose :id expose :name expose :build_path do |build| - path_to(:namespace_project_job, build) + build.target_url || path_to(:namespace_project_job, build) end expose :retry_path, if: -> (*) { retryable? } do |build| diff --git a/app/serializers/job_group_entity.rb b/app/serializers/job_group_entity.rb index 04487e59009..8554de55517 100644 --- a/app/serializers/job_group_entity.rb +++ b/app/serializers/job_group_entity.rb @@ -4,7 +4,7 @@ class JobGroupEntity < Grape::Entity expose :name expose :size expose :detailed_status, as: :status, with: StatusEntity - expose :jobs, with: BuildEntity + expose :jobs, with: JobEntity private diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index f080e6326a1..fb1d4aed58b 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -101,12 +101,12 @@ class GitPushService < BaseService UpdateMergeRequestsWorker .perform_async(@project.id, current_user.id, params[:oldrev], params[:newrev], params[:ref]) - SystemHookPushWorker.perform_async(build_push_data.dup, :push_hooks) - EventCreateService.new.push(@project, current_user, build_push_data) + Ci::CreatePipelineService.new(@project, current_user, build_push_data).execute(:push) + + SystemHookPushWorker.perform_async(build_push_data.dup, :push_hooks) @project.execute_hooks(build_push_data.dup, :push_hooks) @project.execute_services(build_push_data.dup, :push_hooks) - Ci::CreatePipelineService.new(@project, current_user, build_push_data).execute(:push) if push_remove_branch? AfterBranchDeleteService diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb index 7c424fba428..9917a39b795 100644 --- a/app/services/git_tag_push_service.rb +++ b/app/services/git_tag_push_service.rb @@ -8,10 +8,12 @@ class GitTagPushService < BaseService @push_data = build_push_data EventCreateService.new.push(project, current_user, @push_data) + Ci::CreatePipelineService.new(project, current_user, @push_data).execute(:push) + SystemHooksService.new.execute_hooks(build_system_push_data.dup, :tag_push_hooks) project.execute_hooks(@push_data.dup, :tag_push_hooks) project.execute_services(@push_data.dup, :tag_push_hooks) - Ci::CreatePipelineService.new(project, current_user, @push_data).execute(:push) + ProjectCacheWorker.perform_async(project.id, [], [:commit_count, :repository_size]) true diff --git a/app/services/slash_commands/interpret_service.rb b/app/services/slash_commands/interpret_service.rb index b6b411d2185..83144b1e011 100644 --- a/app/services/slash_commands/interpret_service.rb +++ b/app/services/slash_commands/interpret_service.rb @@ -410,7 +410,7 @@ module SlashCommands params '@user' command :cc - desc 'Define target branch for MR' + desc 'Set target branch' explanation do |branch_name| "Sets target branch to #{branch_name}." end diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index d552704df88..0383c7ce546 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -180,11 +180,25 @@ .col-sm-10 = f.text_area :sign_in_text, class: 'form-control', rows: 4 .help-block Markdown enabled + + %fieldset + %legend Help Page .form-group = f.label :help_page_text, class: 'control-label col-sm-2' .col-sm-10 = f.text_area :help_page_text, class: 'form-control', rows: 4 .help-block Markdown enabled + .form-group + .col-sm-offset-2.col-sm-10 + .checkbox + = f.label :help_page_hide_commercial_content do + = f.check_box :help_page_hide_commercial_content + Hide marketing-related entries from help + .form-group + = f.label :help_page_support_url, 'Support page URL', class: 'control-label col-sm-2' + .col-sm-10 + = f.text_field :help_page_support_url, class: 'form-control', placeholder: 'http://company.example.com/getting-help', :'aria-describedby' => 'support_help_block' + %span.help-block#support_help_block Alternate support URL for help page %fieldset %legend Pages diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml index 31d0e589c26..c25eae63eec 100644 --- a/app/views/help/index.html.haml +++ b/app/views/help/index.html.haml @@ -1,4 +1,9 @@ %div +- if current_application_settings.help_page_text.present? + = markdown_field(current_application_settings, :help_page_text) + %hr + +- unless current_application_settings.help_page_hide_commercial_content? %h1 GitLab Community Edition @@ -18,13 +23,9 @@ Used by more than 100,000 organizations, GitLab is the most popular solution to manage git repositories on-premises. %br Read more about GitLab at #{link_to promo_host, promo_url, target: '_blank', rel: 'noopener noreferrer'}. - - if current_application_settings.help_page_text.present? - %hr - = markdown_field(current_application_settings, :help_page_text) - -%hr + %hr -.row +.row.prepend-top-default .col-md-8 .documentation-index = markdown(@help_index) @@ -33,8 +34,9 @@ .panel-heading Quick help %ul.well-list - %li= link_to 'See our website for getting help', promo_url + '/getting-help/' + %li= link_to 'See our website for getting help', support_url %li= link_to 'Use the search bar on the top of this page', '#', onclick: 'Shortcuts.focusSearch(event)' %li= link_to 'Use shortcuts', '#', onclick: 'Shortcuts.toggleHelp()' - %li= link_to 'Get a support subscription', 'https://about.gitlab.com/pricing/' - %li= link_to 'Compare GitLab editions', 'https://about.gitlab.com/features/#compare' + - unless current_application_settings.help_page_hide_commercial_content? + %li= link_to 'Get a support subscription', 'https://about.gitlab.com/pricing/' + %li= link_to 'Compare GitLab editions', 'https://about.gitlab.com/features/#compare' diff --git a/app/views/help/show.html.haml b/app/views/help/show.html.haml index f6ebd76af9d..c07c148a12a 100644 --- a/app/views/help/show.html.haml +++ b/app/views/help/show.html.haml @@ -1,3 +1,3 @@ - page_title @path.split("/").reverse.map(&:humanize) -.documentation.wiki +.documentation.wiki.prepend-top-default = markdown @markdown diff --git a/app/views/projects/boards/components/_board.html.haml b/app/views/projects/boards/components/_board.html.haml index 55c4d51be14..539ee087b14 100644 --- a/app/views/projects/boards/components/_board.html.haml +++ b/app/views/projects/boards/components/_board.html.haml @@ -9,11 +9,11 @@ %span.has-tooltip{ ":title" => '(list.label ? list.label.description : "")', data: { container: "body", placement: "bottom" } } {{ list.title }} - .board-issue-count-holder.pull-right.clearfix{ "v-if" => 'list.type !== "blank"' } - %span.board-issue-count.pull-left{ ":class" => '{ "has-btn": list.type !== "closed" && !disabled }' } + .issue-count-badge.pull-right.clearfix{ "v-if" => 'list.type !== "blank"' } + %span.issue-count-badge-count.pull-left{ ":class" => '{ "has-btn": list.type !== "closed" && !disabled }' } {{ list.issuesSize }} - if can?(current_user, :admin_issue, @project) - %button.btn.btn-small.btn-default.pull-right.has-tooltip.js-no-trigger-collapse{ type: "button", + %button.issue-count-badge-add-button.btn.btn-small.btn-default.has-tooltip.js-no-trigger-collapse{ type: "button", "@click" => "showNewIssueForm", "v-if" => 'list.type !== "closed"', "aria-label" => "New issue", diff --git a/app/views/projects/diffs/_collapsed.html.haml b/app/views/projects/diffs/_collapsed.html.haml new file mode 100644 index 00000000000..8772bd4705f --- /dev/null +++ b/app/views/projects/diffs/_collapsed.html.haml @@ -0,0 +1,5 @@ +- diff_file = viewer.diff_file +- url = url_for(params.merge(action: :diff_for_path, old_path: diff_file.old_path, new_path: diff_file.new_path, file_identifier: diff_file.file_identifier)) +.nothing-here-block.diff-collapsed{ data: { diff_for_path: url } } + This diff is collapsed. + %a.click-to-expand Click to expand it. diff --git a/app/views/projects/diffs/_content.html.haml b/app/views/projects/diffs/_content.html.haml index ec1c434a4b8..68f74f702ea 100644 --- a/app/views/projects/diffs/_content.html.haml +++ b/app/views/projects/diffs/_content.html.haml @@ -1,27 +1,2 @@ -- blob = diff_file.blob - .diff-content - - if diff_file.too_large? - .nothing-here-block This diff could not be displayed because it is too large. - - elsif blob.truncated? - .nothing-here-block The file could not be displayed because it is too large. - - elsif blob.readable_text? - - if !diff_file.diffable? - .nothing-here-block This diff was suppressed by a .gitattributes entry. - - elsif diff_file.collapsed? - - url = url_for(params.merge(action: :diff_for_path, old_path: diff_file.old_path, new_path: diff_file.new_path, file_identifier: diff_file.file_identifier)) - .nothing-here-block.diff-collapsed{ data: { diff_for_path: url } } - This diff is collapsed. - %a.click-to-expand - Click to expand it. - - elsif diff_file.diff_lines.length > 0 - = render "projects/diffs/viewers/text", diff_file: diff_file - - else - - if diff_file.mode_changed? - .nothing-here-block File mode changed - - elsif diff_file.renamed_file? - .nothing-here-block File moved - - elsif blob.image? - = render "projects/diffs/viewers/image", diff_file: diff_file - - else - .nothing-here-block No preview for this file type + = render 'projects/diffs/viewer', viewer: diff_file.rich_viewer || diff_file.simple_viewer diff --git a/app/views/projects/diffs/_render_error.html.haml b/app/views/projects/diffs/_render_error.html.haml new file mode 100644 index 00000000000..47a9ac3ee6b --- /dev/null +++ b/app/views/projects/diffs/_render_error.html.haml @@ -0,0 +1,6 @@ +.nothing-here-block + This #{viewer.switcher_title} could not be displayed because #{diff_render_error_reason(viewer)}. + + You can + = diff_render_error_options(viewer).to_sentence(two_words_connector: ' or ', last_word_connector: ', or ').html_safe + instead. diff --git a/app/views/projects/diffs/_viewer.html.haml b/app/views/projects/diffs/_viewer.html.haml new file mode 100644 index 00000000000..5c4d1760871 --- /dev/null +++ b/app/views/projects/diffs/_viewer.html.haml @@ -0,0 +1,16 @@ +- hidden = local_assigns.fetch(:hidden, false) + +.diff-viewer{ data: { type: viewer.type }, class: ('hidden' if hidden) } + - if viewer.render_error + = render 'projects/diffs/render_error', viewer: viewer + - elsif viewer.collapsed? + = render 'projects/diffs/collapsed', viewer: viewer + - else + - viewer.prepare! + + -# In the rare case where the first kilobyte of the file looks like text, + -# but the file turns out to actually be binary after loading all data, + -# we fall back on the binary No Preview viewer. + - viewer = DiffViewer::NoPreview.new(viewer.diff_file) if viewer.binary_detected_after_load? + + = render viewer.partial_path, viewer: viewer diff --git a/app/views/projects/diffs/viewers/_added.html.haml b/app/views/projects/diffs/viewers/_added.html.haml new file mode 100644 index 00000000000..8004fe16688 --- /dev/null +++ b/app/views/projects/diffs/viewers/_added.html.haml @@ -0,0 +1,2 @@ +.nothing-here-block + File added diff --git a/app/views/projects/diffs/viewers/_deleted.html.haml b/app/views/projects/diffs/viewers/_deleted.html.haml new file mode 100644 index 00000000000..0ac7b4ca8f6 --- /dev/null +++ b/app/views/projects/diffs/viewers/_deleted.html.haml @@ -0,0 +1,2 @@ +.nothing-here-block + File deleted diff --git a/app/views/projects/diffs/viewers/_image.html.haml b/app/views/projects/diffs/viewers/_image.html.haml index ea75373581e..19d08181223 100644 --- a/app/views/projects/diffs/viewers/_image.html.haml +++ b/app/views/projects/diffs/viewers/_image.html.haml @@ -1,3 +1,4 @@ +- diff_file = viewer.diff_file - blob = diff_file.blob - old_blob = diff_file.old_blob - blob_raw_path = diff_file_blob_raw_path(diff_file) diff --git a/app/views/projects/diffs/viewers/_mode_changed.html.haml b/app/views/projects/diffs/viewers/_mode_changed.html.haml new file mode 100644 index 00000000000..69bc96bbdad --- /dev/null +++ b/app/views/projects/diffs/viewers/_mode_changed.html.haml @@ -0,0 +1,3 @@ +- diff_file = viewer.diff_file +.nothing-here-block + File mode changed from #{diff_file.a_mode} to #{diff_file.b_mode} diff --git a/app/views/projects/diffs/viewers/_no_preview.html.haml b/app/views/projects/diffs/viewers/_no_preview.html.haml new file mode 100644 index 00000000000..befe070af2b --- /dev/null +++ b/app/views/projects/diffs/viewers/_no_preview.html.haml @@ -0,0 +1,2 @@ +.nothing-here-block + No preview for this file type diff --git a/app/views/projects/diffs/viewers/_not_diffable.html.haml b/app/views/projects/diffs/viewers/_not_diffable.html.haml new file mode 100644 index 00000000000..b2c677ec59c --- /dev/null +++ b/app/views/projects/diffs/viewers/_not_diffable.html.haml @@ -0,0 +1,2 @@ +.nothing-here-block + This diff was suppressed by a .gitattributes entry. diff --git a/app/views/projects/diffs/viewers/_renamed.html.haml b/app/views/projects/diffs/viewers/_renamed.html.haml new file mode 100644 index 00000000000..ef05ee38d8d --- /dev/null +++ b/app/views/projects/diffs/viewers/_renamed.html.haml @@ -0,0 +1,2 @@ +.nothing-here-block + File moved diff --git a/app/views/projects/diffs/viewers/_text.html.haml b/app/views/projects/diffs/viewers/_text.html.haml index 120d3540223..509e68598c9 100644 --- a/app/views/projects/diffs/viewers/_text.html.haml +++ b/app/views/projects/diffs/viewers/_text.html.haml @@ -1,5 +1,5 @@ +- diff_file = viewer.diff_file - blob = diff_file.blob -- blob.load_all_data! - total_lines = blob.lines.size - total_lines -= 1 if total_lines > 0 && blob.lines.last.blank? - if diff_view == :parallel diff --git a/app/views/projects/pipeline_schedules/_form.html.haml b/app/views/projects/pipeline_schedules/_form.html.haml index 25ae4e0e18f..e8dedf26206 100644 --- a/app/views/projects/pipeline_schedules/_form.html.haml +++ b/app/views/projects/pipeline_schedules/_form.html.haml @@ -20,7 +20,7 @@ .form-group .col-md-9 = f.label :ref, _('Target Branch'), class: 'label-light' - = dropdown_tag(_("Select target branch"), options: { toggle_class: 'btn js-target-branch-dropdown git-revision-dropdown-toggle', dropdown_class: 'git-revision-dropdown', title: _("Select target branch"), filter: true, placeholder: _("Filter"), data: { data: @project.repository.branch_names, default_branch: @project.default_branch } } ) + = dropdown_tag(_("Select target branch"), options: { toggle_class: 'btn js-target-branch-dropdown', dropdown_class: 'git-revision-dropdown', title: _("Select target branch"), filter: true, placeholder: _("Filter"), data: { data: @project.repository.branch_names, default_branch: @project.default_branch } } ) = f.text_field :ref, value: @schedule.ref, id: 'schedule_ref', class: 'hidden', name: 'schedule[ref]', required: true .form-group .col-md-9 diff --git a/app/views/shared/issuable/form/_merge_params.html.haml b/app/views/shared/issuable/form/_merge_params.html.haml index 271150ed318..bfa91629e1e 100644 --- a/app/views/shared/issuable/form/_merge_params.html.haml +++ b/app/views/shared/issuable/form/_merge_params.html.haml @@ -3,7 +3,8 @@ - return unless issuable.is_a?(MergeRequest) - return if issuable.closed_without_fork? --# This check is duplicated below, to avoid conflicts with EE. +-# This check is duplicated below to avoid CE -> EE merge conflicts. +-# This comment and the following line should only exist in CE. - return unless issuable.can_remove_source_branch?(current_user) .form-group diff --git a/changelogs/unreleased/32470-pag-links.yml b/changelogs/unreleased/32470-pag-links.yml new file mode 100644 index 00000000000..d0fd284f3ee --- /dev/null +++ b/changelogs/unreleased/32470-pag-links.yml @@ -0,0 +1,4 @@ +--- +title: more visual contrast in pagination widget +merge_request: +author: diff --git a/changelogs/unreleased/disable-blocked-manual-actions.yml b/changelogs/unreleased/disable-blocked-manual-actions.yml new file mode 100644 index 00000000000..a640f61a7dd --- /dev/null +++ b/changelogs/unreleased/disable-blocked-manual-actions.yml @@ -0,0 +1,4 @@ +--- +title: disable blocked manual actions +merge_request: +author: diff --git a/changelogs/unreleased/dm-diff-viewers.yml b/changelogs/unreleased/dm-diff-viewers.yml new file mode 100644 index 00000000000..e5b1352c8f1 --- /dev/null +++ b/changelogs/unreleased/dm-diff-viewers.yml @@ -0,0 +1,4 @@ +--- +title: Implement diff viewers +merge_request: +author: diff --git a/changelogs/unreleased/dm-fix-parser-cache.yml b/changelogs/unreleased/dm-fix-parser-cache.yml new file mode 100644 index 00000000000..31c163b7272 --- /dev/null +++ b/changelogs/unreleased/dm-fix-parser-cache.yml @@ -0,0 +1,4 @@ +--- +title: Don't return nil for missing objects from parser cache +merge_request: +author: diff --git a/changelogs/unreleased/dm-target-branch-slash-command-desc.yml b/changelogs/unreleased/dm-target-branch-slash-command-desc.yml new file mode 100644 index 00000000000..768ddf0416e --- /dev/null +++ b/changelogs/unreleased/dm-target-branch-slash-command-desc.yml @@ -0,0 +1,4 @@ +--- +title: Update /target_branch slash command description to be more consistent +merge_request: +author: diff --git a/changelogs/unreleased/feature-add-support-for-services-configuration.yml b/changelogs/unreleased/feature-add-support-for-services-configuration.yml new file mode 100644 index 00000000000..88a3eacd774 --- /dev/null +++ b/changelogs/unreleased/feature-add-support-for-services-configuration.yml @@ -0,0 +1,4 @@ +--- +title: Add support for image and services configuration in .gitlab-ci.yml +merge_request: 8578 +author: diff --git a/changelogs/unreleased/fix-support-for-external-ci-services.yml b/changelogs/unreleased/fix-support-for-external-ci-services.yml new file mode 100644 index 00000000000..eecb4519259 --- /dev/null +++ b/changelogs/unreleased/fix-support-for-external-ci-services.yml @@ -0,0 +1,4 @@ +--- +title: Fix support for external CI services +merge_request: 11176 +author: diff --git a/changelogs/unreleased/help-landing-page-customizations.yml b/changelogs/unreleased/help-landing-page-customizations.yml new file mode 100644 index 00000000000..58cab751ded --- /dev/null +++ b/changelogs/unreleased/help-landing-page-customizations.yml @@ -0,0 +1,4 @@ +--- +title: Help landing page customizations +merge_request: 11878 +author: Robin Bobbitt diff --git a/changelogs/unreleased/instrument-merge-request-diff-load-commits.yml b/changelogs/unreleased/instrument-merge-request-diff-load-commits.yml new file mode 100644 index 00000000000..916b182a48b --- /dev/null +++ b/changelogs/unreleased/instrument-merge-request-diff-load-commits.yml @@ -0,0 +1,4 @@ +--- +title: Instrument MergeRequestDiff#load_commits +merge_request: +author: diff --git a/changelogs/unreleased/zj-raise-etag-route-regex-miss.yml b/changelogs/unreleased/zj-raise-etag-route-regex-miss.yml new file mode 100644 index 00000000000..57a5f4e44c0 --- /dev/null +++ b/changelogs/unreleased/zj-raise-etag-route-regex-miss.yml @@ -0,0 +1,4 @@ +--- +title: Fix etag route not being a match for environments +merge_request: +author: diff --git a/config/initializers/8_metrics.rb b/config/initializers/8_metrics.rb index 5e0eefdb154..508b886d6a0 100644 --- a/config/initializers/8_metrics.rb +++ b/config/initializers/8_metrics.rb @@ -113,6 +113,9 @@ def instrument_classes(instrumentation) # This is a Rails scope so we have to instrument it manually. instrumentation.instrument_method(Project, :visible_to_user) + + # Needed for https://gitlab.com/gitlab-org/gitlab-ce/issues/30224#note_32306159 + instrumentation.instrument_instance_method(MergeRequestDiff, :load_commits) end # rubocop:enable Metrics/AbcSize diff --git a/config/webpack.config.js b/config/webpack.config.js index 120f9d3193d..3c2455ebf35 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -61,7 +61,7 @@ var config = { network: './network/network_bundle.js', notebook_viewer: './blob/notebook_viewer.js', pdf_viewer: './blob/pdf_viewer.js', - pipelines: './pipelines/index.js', + pipelines: './pipelines/pipelines_bundle.js', pipelines_details: './pipelines/pipeline_details_bundle.js', profile: './profile/profile_bundle.js', protected_branches: './protected_branches/protected_branches_bundle.js', diff --git a/db/migrate/20170602154736_add_help_page_hide_commercial_content_to_application_settings.rb b/db/migrate/20170602154736_add_help_page_hide_commercial_content_to_application_settings.rb new file mode 100644 index 00000000000..5e8b667b86d --- /dev/null +++ b/db/migrate/20170602154736_add_help_page_hide_commercial_content_to_application_settings.rb @@ -0,0 +1,9 @@ +class AddHelpPageHideCommercialContentToApplicationSettings < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def change + add_column :application_settings, :help_page_hide_commercial_content, :boolean, default: false + end +end diff --git a/db/migrate/20170602154813_add_help_page_support_url_to_application_settings.rb b/db/migrate/20170602154813_add_help_page_support_url_to_application_settings.rb new file mode 100644 index 00000000000..138fe9b2a37 --- /dev/null +++ b/db/migrate/20170602154813_add_help_page_support_url_to_application_settings.rb @@ -0,0 +1,9 @@ +class AddHelpPageSupportUrlToApplicationSettings < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def change + add_column :application_settings, :help_page_support_url, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index b93630a410d..9d79fe162b7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -12,6 +12,7 @@ # It's strongly recommended that you check this file into your version control system. ActiveRecord::Schema.define(version: 20170606202615) do + # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" enable_extension "pg_trgm" @@ -123,6 +124,8 @@ ActiveRecord::Schema.define(version: 20170606202615) do t.boolean "clientside_sentry_enabled", default: false, null: false t.string "clientside_sentry_dsn" t.boolean "prometheus_metrics_enabled", default: false, null: false + t.boolean "help_page_hide_commercial_content", default: false + t.string "help_page_support_url" end create_table "audit_events", force: :cascade do |t| diff --git a/doc/administration/raketasks/github_import.md b/doc/administration/raketasks/github_import.md index affb4d17861..04c70c3644e 100644 --- a/doc/administration/raketasks/github_import.md +++ b/doc/administration/raketasks/github_import.md @@ -3,7 +3,7 @@ >**Note:** > > - [Introduced][ce-10308] in GitLab 9.1. -> - You need a personal access token in order to retrieve and import GitHub +> - You need a personal access token in order to retrieve and import GitHub > projects. You can get it from: https://github.com/settings/tokens > - You also need to pass an username as the second argument to the rake task > which will become the owner of the project. @@ -19,7 +19,7 @@ bundle exec rake import:github[access_token,root,foo/bar] RAILS_ENV=production ``` In this case, `access_token` is your GitHub personal access token, `root` -is your GitLab username, and `foo/bar` is the new GitLab namespace/project that +is your GitLab username, and `foo/bar` is the new GitLab namespace/project that will get created from your GitHub project. Subgroups are also possible: `foo/foo/bar`. diff --git a/doc/api/README.md b/doc/api/README.md index 1241801a81c..4f189c16673 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -55,6 +55,11 @@ following locations: - [V3 to V4](v3_to_v4.md) - [Version](version.md) +The following documentation is for the [internal CI API](ci/README.md): + +- [Builds](ci/builds.md) +- [Runners](ci/runners.md) + ## Road to GraphQL Going forward, we will start on moving to @@ -65,22 +70,20 @@ controller-specific endpoints. GraphQL has a number of benefits: 2. Callers of the API can request only what they need. 3. It is versioned by default. -It will co-exist with the current V4 REST API. If we have a V5 API, this should be -compatability layer on top of GraphQL. - -### Internal CI API +It will co-exist with the current v4 REST API. If we have a v5 API, this should +be a compatibility layer on top of GraphQL. -The following documentation is for the [internal CI API](ci/README.md): +## Authentication -- [Builds](ci/builds.md) -- [Runners](ci/runners.md) +Most API requests require authentication via a session cookie or token. For +those cases where it is not required, this will be mentioned in the documentation +for each individual endpoint. For example, the [`/projects/:id` endpoint](projects.md). -## Authentication +There are three types of access tokens available: -Most API requests require authentication via a session cookie or token. For those cases where it is not required, this will be mentioned in the documentation -for each individual endpoint. For example, the [`/projects/:id` endpoint](projects.md). -There are three types of tokens available: private tokens, OAuth 2 tokens, and personal -access tokens. +1. [OAuth2 tokens](#oauth2-tokens) +1. [Private tokens](#private-tokens) +1. [Personal access tokens](#personal-access-tokens) If authentication information is invalid or omitted, an error message will be returned with status code `401`: @@ -91,20 +94,13 @@ returned with status code `401`: } ``` -### Session Cookie +### Session cookie When signing in to GitLab as an ordinary user, a `_gitlab_session` cookie is set. The API will use this cookie for authentication if it is present, but using the API to generate a new session cookie is currently not supported. -### Private Tokens - -You need to pass a `private_token` parameter via query string or header. If passed as a -header, the header name must be `PRIVATE-TOKEN` (uppercase and with a dash instead of -an underscore). You can find or reset your private token in your account page -(`/profile/account`). - -### OAuth 2 Tokens +### OAuth2 tokens You can use an OAuth 2 token to authenticate with the API by passing it either in the `access_token` parameter or in the `Authorization` header. @@ -117,30 +113,31 @@ curl --header "Authorization: Bearer OAUTH-TOKEN" https://gitlab.example.com/api Read more about [GitLab as an OAuth2 client](oauth2.md). -### Personal Access Tokens +### Private tokens -> [Introduced][ce-3749] in GitLab 8.8. +Private tokens provide full access to the GitLab API. Anyone with access to +them can interact with GitLab as if they were you. You can find or reset your +private token in your account page (`/profile/account`). -You can create as many personal access tokens as you like from your GitLab -profile (`/profile/personal_access_tokens`); perhaps one for each application -that needs access to the GitLab API. +For examples of usage, [read the basic usage section](#basic-usage). -Once you have your token, pass it to the API using either the `private_token` -parameter or the `PRIVATE-TOKEN` header. +### Personal access tokens -> [Introduced][ce-5951] in GitLab 8.15. +Instead of using your private token which grants full access to your account, +personal access tokens could be a better fit because of their granular +permissions. -Personal Access Tokens can be created with one or more scopes that allow various actions -that a given token can perform. Although there are only two scopes available at the -moment – `read_user` and `api` – the groundwork has been laid to add more scopes easily. +Once you have your token, pass it to the API using either the `private_token` +parameter or the `PRIVATE-TOKEN` header. For examples of usage, +[read the basic usage section](#basic-usage). -At any time you can revoke any personal access token by just clicking **Revoke**. +[Read more about personal access tokens.][pat] ### Impersonation tokens > [Introduced][ce-9099] in GitLab 9.0. Needs admin permissions. -Impersonation tokens are a type of [Personal Access Token](#personal-access-tokens) +Impersonation tokens are a type of [personal access token][pat] that can only be created by an admin for a specific user. They are a better alternative to using the user's password/private token @@ -149,9 +146,11 @@ or private token, since the password/token can change over time. Impersonation tokens are a great fit if you want to build applications or tools which authenticate with the API as a specific user. -For more information about the usage please refer to the +For more information, refer to the [users API](users.md#retrieve-user-impersonation-tokens) docs. +For examples of usage, [read the basic usage section](#basic-usage). + ### Sudo > Needs admin permissions. @@ -204,11 +203,16 @@ GET /projects?private_token=9koXpg98eAheJpvBs5tK&sudo=23 curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --header "SUDO: 23" "https://gitlab.example.com/api/v4/projects" ``` -## Basic Usage +## Basic usage API requests should be prefixed with `api` and the API version. The API version is defined in [`lib/api.rb`][lib-api-url]. +For endpoints that require [authentication](#authentication), you need to pass +a `private_token` parameter via query string or header. If passed as a header, +the header name must be `PRIVATE-TOKEN` (uppercase and with a dash instead of +an underscore). + Example of a valid API request: ``` @@ -221,6 +225,12 @@ Example of a valid API request using cURL and authentication via header: curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects" ``` +Example of a valid API request using cURL and authentication via a query string: + +```shell +curl "https://gitlab.example.com/api/v4/projects?private_token=9koXpg98eAheJpvBs5tK" +``` + The API uses JSON to serialize data. You don't need to specify `.json` at the end of an API URL. @@ -436,3 +446,4 @@ programming languages. Visit the [GitLab website] for a complete list. [ce-3749]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3749 [ce-5951]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5951 [ce-9099]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9099 +[pat]: ../user/profile/personal_access_tokens.md diff --git a/doc/api/oauth2.md b/doc/api/oauth2.md index 46fe64d382e..07cb64cb373 100644 --- a/doc/api/oauth2.md +++ b/doc/api/oauth2.md @@ -134,4 +134,4 @@ access_token = client.password.get_token('user@example.com', 'secret') puts access_token.token ``` -[personal access tokens]: ./README.md#personal-access-tokens
\ No newline at end of file +[personal access tokens]: ../user/profile/personal_access_tokens.md diff --git a/doc/api/session.md b/doc/api/session.md index 7dd504b67c5..f79eac11689 100644 --- a/doc/api/session.md +++ b/doc/api/session.md @@ -1,11 +1,9 @@ # Session API -## Deprecation Notice - -1. Starting in GitLab 8.11, this feature has been *disabled* for users with two-factor authentication turned on. -2. These users can access the API using [personal access tokens] instead. - ---- +>**Deprecation notice:** +Starting in GitLab 8.11, this feature has been **disabled** for users with +[two-factor authentication][2fa] turned on. These users can access the API +using [personal access tokens] instead. You can login with both GitLab and LDAP credentials in order to obtain the private token. @@ -52,4 +50,5 @@ Example response: } ``` -[personal access tokens]: ./README.md#personal-access-tokens +[2fa]: ../user/profile/account/two_factor_authentication.md +[personal access tokens]: ../user/profile/personal_access_tokens.md diff --git a/doc/api/users.md b/doc/api/users.md index f4167ba2605..91ce4f6dac3 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -804,7 +804,7 @@ Example response: It creates a new impersonation token. Note that only administrators can do this. You are only able to create impersonation tokens to impersonate the user and perform -both API calls and Git reads and writes. The user will not see these tokens in his profile +both API calls and Git reads and writes. The user will not see these tokens in their profile settings page. ``` diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md index 408d46a756c..f7c2a0ef0ca 100644 --- a/doc/ci/docker/using_docker_build.md +++ b/doc/ci/docker/using_docker_build.md @@ -282,9 +282,9 @@ which can be avoided if a different driver is used, for example `overlay`. > **Notes:** - This feature requires GitLab 8.8 and GitLab Runner 1.2. -- Starting from GitLab 8.12, if you have 2FA enabled in your account, you need - to pass a personal access token instead of your password in order to login to - GitLab's Container Registry. +- Starting from GitLab 8.12, if you have [2FA] enabled in your account, you need + to pass a [personal access token][pat] instead of your password in order to + login to GitLab's Container Registry. Once you've built a Docker image, you can push it up to the built-in [GitLab Container Registry](../../user/project/container_registry.md). For example, @@ -409,3 +409,5 @@ Some things you should be aware of when using the Container Registry: [docker-in-docker]: https://blog.docker.com/2013/09/docker-can-now-run-within-docker/ [docker-cap]: https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities +[2fa]: ../../user/profile/account/two_factor_authentication.md +[pat]: ../../user/profile/personal_access_tokens.md diff --git a/doc/ci/examples/code_climate.md b/doc/ci/examples/code_climate.md index a047e809788..5659a8c2a2a 100644 --- a/doc/ci/examples/code_climate.md +++ b/doc/ci/examples/code_climate.md @@ -27,7 +27,7 @@ download and analyze the report artifact in JSON format. For GitLab [Enterprise Edition Starter][ee] users, this information can be automatically extracted and shown right in the merge request widget. [Learn more on code quality -diffs in merge requests](http://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.md). +diffs in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html). [cli]: https://github.com/codeclimate/codeclimate [dind]: ../docker/using_docker_build.md#use-docker-in-docker-executor diff --git a/doc/user/profile/account/two_factor_authentication.md b/doc/user/profile/account/two_factor_authentication.md index fb69d934ae1..590c3f862fb 100644 --- a/doc/user/profile/account/two_factor_authentication.md +++ b/doc/user/profile/account/two_factor_authentication.md @@ -125,23 +125,14 @@ applications and U2F devices. ## Personal access tokens When 2FA is enabled, you can no longer use your normal account password to -authenticate with Git over HTTPS on the command line, you must use a personal -access token instead. - -1. Log in to your GitLab account. -1. Go to your **Profile Settings**. -1. Go to **Access Tokens**. -1. Choose a name and expiry date for the token. -1. Click on **Create Personal Access Token**. -1. Save the personal access token somewhere safe. - -When using Git over HTTPS on the command line, enter the personal access token -into the password field. +authenticate with Git over HTTPS on the command line or when using +[GitLab's API][api], you must use a [personal access token][pat] instead. ## Recovery options To disable two-factor authentication on your account (for example, if you have lost your code generation device) you can: + * [Use a saved recovery code](#use-a-saved-recovery-code) * [Generate new recovery codes using SSH](#generate-new-recovery-codes-using-ssh) * [Ask a GitLab administrator to disable two-factor authentication on your account](#ask-a-gitlab-administrator-to-disable-two-factor-authentication-on-your-account) @@ -154,8 +145,9 @@ codes. If you saved these codes, you can use one of them to sign in. To use a recovery code, enter your username/email and password on the GitLab sign-in page. When prompted for a two-factor code, enter the recovery code. -> **Note:** Once you use a recovery code, you cannot re-use it. You can still - use the other recovery codes you saved. +>**Note:** +Once you use a recovery code, you cannot re-use it. You can still use the other +recovery codes you saved. ### Generate new recovery codes using SSH @@ -190,11 +182,14 @@ a new set of recovery codes with SSH. two-factor code. Then, visit your Profile Settings and add a new device so you do not lose access to your account again. ``` -3. Go to the GitLab sign-in page and enter your username/email and password. When prompted for a two-factor code, enter one of the recovery codes obtained -from the command-line output. -> **Note:** After signing in, visit your **Profile Settings -> Account** immediately to set up two-factor authentication with a new - device. +3. Go to the GitLab sign-in page and enter your username/email and password. + When prompted for a two-factor code, enter one of the recovery codes obtained + from the command-line output. + +>**Note:** +After signing in, visit your **Profile settings > Account** immediately to set +up two-factor authentication with a new device. ### Ask a GitLab administrator to disable two-factor authentication on your account @@ -206,23 +201,23 @@ Sign in and re-enable two-factor authentication as soon as possible. ## Note to GitLab administrators - You need to take special care to that 2FA keeps working after -[restoring a GitLab backup](../../../raketasks/backup_restore.md). - + [restoring a GitLab backup](../../../raketasks/backup_restore.md). - To ensure 2FA authorizes correctly with TOTP server, you may want to ensure -your GitLab server's time is synchronized via a service like NTP. Otherwise, -you may have cases where authorization always fails because of time differences. - -[Google Authenticator]: https://support.google.com/accounts/answer/1066447?hl=en -[FreeOTP]: https://freeotp.github.io/ -[YubiKey]: https://www.yubico.com/products/yubikey-hardware/ - + your GitLab server's time is synchronized via a service like NTP. Otherwise, + you may have cases where authorization always fails because of time differences. - The GitLab U2F implementation does _not_ work when the GitLab instance is accessed from -multiple hostnames, or FQDNs. Each U2F registration is linked to the _current hostname_ at -the time of registration, and cannot be used for other hostnames/FQDNs. + multiple hostnames, or FQDNs. Each U2F registration is linked to the _current hostname_ at + the time of registration, and cannot be used for other hostnames/FQDNs. For example, if a user is trying to access a GitLab instance from `first.host.xyz` and `second.host.xyz`: - The user logs in via `first.host.xyz` and registers their U2F key. - The user logs out and attempts to log in via `first.host.xyz` - U2F authentication suceeds. - - The user logs out and attempts to log in via `second.host.xyz` - U2F authentication fails, because + - The user logs out and attempts to log in via `second.host.xyz` - U2F authentication fails, because the U2F key has only been registered on `first.host.xyz`. + +[Google Authenticator]: https://support.google.com/accounts/answer/1066447?hl=en +[FreeOTP]: https://freeotp.github.io/ +[YubiKey]: https://www.yubico.com/products/yubikey-hardware/ +[api]: ../../../api/README.md +[pat]: ../personal_access_tokens.md diff --git a/doc/user/profile/img/personal_access_tokens.png b/doc/user/profile/img/personal_access_tokens.png Binary files differnew file mode 100644 index 00000000000..6aa63dbe342 --- /dev/null +++ b/doc/user/profile/img/personal_access_tokens.png diff --git a/doc/user/profile/personal_access_tokens.md b/doc/user/profile/personal_access_tokens.md new file mode 100644 index 00000000000..9488ce1ef30 --- /dev/null +++ b/doc/user/profile/personal_access_tokens.md @@ -0,0 +1,57 @@ +# Personal access tokens + +> [Introduced][ce-3749] in GitLab 8.8. + +Personal access tokens are useful if you need access to the [GitLab API][api]. +Instead of using your private token which grants full access to your account, +personal access tokens could be a better fit because of their +[granular permissions](#limiting-scopes-of-a-personal-access-token). + +You can also use them to authenticate against Git over HTTP. They are the only +accepted method of authentication when you have +[Two-Factor Authentication (2FA)][2fa] enabled. + +Once you have your token, [pass it to the API][usage] using either the +`private_token` parameter or the `PRIVATE-TOKEN` header. + +## Creating a personal access token + +You can create as many personal access tokens as you like from your GitLab +profile. + +1. Log in to your GitLab account. +1. Go to your **Profile settings**. +1. Go to **Access tokens**. +1. Choose a name and optionally an expiry date for the token. +1. Choose the [desired scopes](#limiting-scopes-of-a-personal-access-token). +1. Click on **Create personal access token**. +1. Save the personal access token somewhere safe. Once you leave or refresh + the page, you won't be able to access it again. + + + +## Revoking a personal access token + +At any time, you can revoke any personal access token by just clicking the +respective **Revoke** button under the 'Active personal access tokens' area. + +## Limiting scopes of a personal access token + +Personal access tokens can be created with one or more scopes that allow various +actions that a given token can perform. The available scopes are depicted in +the following table. + +| Scope | Description | +| ----- | ----------- | +|`read_user` | Allows access to the read-only endpoints under `/users`. Essentially, any of the `GET` requests in the [Users API][users] are allowed ([introduced][ce-5951] in GitLab 8.15). | +| `api` | Grants complete access to the API (read/write) ([introduced][ce-5951] in GitLab 8.15). Required for accessing Git repositories over HTTP when 2FA is enabled. | +| `read_registry` | Allows to read [container registry] images if a project is private and authorization is required ([introduced][ce-11845] in GitLab 9.3). | + +[2fa]: ../account/two_factor_authentication.md +[api]: ../../api/README.md +[ce-3749]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3749 +[ce-5951]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5951 +[ce-11845]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11845 +[container registry]: ../project/container_registry.md +[users]: ../../api/users.md +[usage]: ../../api/README.md#basic-usage diff --git a/doc/user/project/container_registry.md b/doc/user/project/container_registry.md index 75ea911b9bc..629d69d8aea 100644 --- a/doc/user/project/container_registry.md +++ b/doc/user/project/container_registry.md @@ -8,8 +8,8 @@ Registry across your GitLab instance, visit the [administrator documentation](../../administration/container_registry.md). - Starting from GitLab 8.12, if you have 2FA enabled in your account, you need - to pass a personal access token instead of your password in order to login to - GitLab's Container Registry. + to pass a [personal access token][pat] instead of your password in order to + login to GitLab's Container Registry. - Multiple level image names support was added in GitLab 9.1 With the Docker Container Registry integrated into GitLab, every project can @@ -114,12 +114,11 @@ and [Using the GitLab Container Registry documentation](../../ci/docker/using_do ## Using with private projects -If a project is private, credentials will need to be provided for authorization. -The preferred way to do this, is by using personal access tokens, which can be -created under `/profile/personal_access_tokens`. The minimal scope needed is: -`read_registry`. +> [Introduced][ce-11845] in GitLab 9.3. -This feature was introduced in GitLab 9.3. +If a project is private, credentials will need to be provided for authorization. +The preferred way to do this, is by using [personal access tokens][pat]. +The minimal scope needed is `read_registry`. ## Troubleshooting the GitLab Container Registry @@ -264,4 +263,6 @@ The solution: check the [IAM permissions again](https://docs.docker.com/registry Once the right permissions were set, the error will go away. [ce-4040]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4040 +[ce-11845]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11845 [docker-docs]: https://docs.docker.com/engine/userguide/intro/ +[pat]: ../profile/personal_access_tokens.md diff --git a/doc/user/project/integrations/jira.md b/doc/user/project/integrations/jira.md index a048260b033..e8dbf8d08d2 100644 --- a/doc/user/project/integrations/jira.md +++ b/doc/user/project/integrations/jira.md @@ -76,7 +76,7 @@ We have split this stage in steps so it is easier to follow.  ---- + --- The JIRA configuration is over. Write down the new JIRA username and its password as they will be needed when configuring GitLab in the next section. diff --git a/doc/user/project/new_ci_build_permissions_model.md b/doc/user/project/new_ci_build_permissions_model.md index e9512497d6c..271adee7da1 100644 --- a/doc/user/project/new_ci_build_permissions_model.md +++ b/doc/user/project/new_ci_build_permissions_model.md @@ -212,9 +212,9 @@ Container Registries for private projects. access token created explicitly for this purpose). This issue is resolved with latest changes in GitLab Runner 1.8 which receives GitLab credentials with build data. -- Starting with GitLab 8.12, if you have 2FA enabled in your account, you need - to pass a personal access token instead of your password in order to login to - GitLab's Container Registry. +- Starting from GitLab 8.12, if you have [2FA] enabled in your account, you need + to pass a [personal access token][pat] instead of your password in order to + login to GitLab's Container Registry. Your jobs can access all container images that you would normally have access to. The only implication is that you can push to the Container Registry of the @@ -239,3 +239,5 @@ test: [update-docs]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update [workhorse]: https://gitlab.com/gitlab-org/gitlab-workhorse [jobenv]: ../../ci/variables/README.md#predefined-variables-environment-variables +[2fa]: ../profile/account/two_factor_authentication.md +[pat]: ../profile/personal_access_tokens.md diff --git a/lib/api/entities.rb b/lib/api/entities.rb index a836df3dc81..412443a2405 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -603,6 +603,9 @@ module API expose :plantuml_url expose :terminal_max_session_time expose :polling_interval_multiplier + expose :help_page_hide_commercial_content + expose :help_page_text + expose :help_page_support_url end class Release < Grape::Entity @@ -804,7 +807,11 @@ module API end class Image < Grape::Entity - expose :name + expose :name, :entrypoint + end + + class Service < Image + expose :alias, :command end class Artifacts < Grape::Entity @@ -848,7 +855,7 @@ module API expose :variables expose :steps, using: Step expose :image, using: Image - expose :services, using: Image + expose :services, using: Service expose :artifacts, using: Artifacts expose :cache, using: Cache expose :credentials, using: Credentials diff --git a/lib/api/settings.rb b/lib/api/settings.rb index 25027c3b114..d598f9a62a2 100644 --- a/lib/api/settings.rb +++ b/lib/api/settings.rb @@ -39,7 +39,9 @@ module API :email_author_in_body, :enabled_git_access_protocol, :gravatar_enabled, + :help_page_hide_commercial_content, :help_page_text, + :help_page_support_url, :home_page_url, :housekeeping_enabled, :html_emails_enabled, @@ -101,7 +103,9 @@ module API optional :home_page_url, type: String, desc: 'We will redirect non-logged in users to this page' optional :after_sign_out_path, type: String, desc: 'We will redirect users to this page after they sign out' optional :sign_in_text, type: String, desc: 'The sign in text of the GitLab application' + optional :help_page_hide_commercial_content, type: Boolean, desc: 'Hide marketing-related entries from help' optional :help_page_text, type: String, desc: 'Custom text displayed on the help page' + optional :help_page_support_url, type: String, desc: 'Alternate support URL for help page' optional :shared_runners_enabled, type: Boolean, desc: 'Enable shared runners for new projects' given shared_runners_enabled: ->(val) { val } do requires :shared_runners_text, type: String, desc: 'Shared runners text ' diff --git a/lib/banzai/reference_parser/base_parser.rb b/lib/banzai/reference_parser/base_parser.rb index 1e2536231d8..279fca8d043 100644 --- a/lib/banzai/reference_parser/base_parser.rb +++ b/lib/banzai/reference_parser/base_parser.rb @@ -171,7 +171,7 @@ module Banzai collection.where(id: to_query).each { |row| cache[row.id] = row } end - cache.values_at(*ids) + cache.values_at(*ids).compact else collection.where(id: ids) end diff --git a/lib/ci/api/entities.rb b/lib/ci/api/entities.rb index 792ff628b09..6b82b2b4f13 100644 --- a/lib/ci/api/entities.rb +++ b/lib/ci/api/entities.rb @@ -45,7 +45,21 @@ module Ci expose :artifacts_expire_at, if: ->(build, _) { build.artifacts? } expose :options do |model| - model.options + # This part ensures that output of old API is still the same after adding support + # for extended docker configuration options, used by new API + # + # I'm leaving this here, not in the model, because it should be removed at the same time + # when old API will be removed (planned for August 2017). + model.options.dup.tap do |options| + options[:image] = options[:image][:name] if options[:image].is_a?(Hash) + options[:services].map! do |service| + if service.is_a?(Hash) + service[:name] + else + service + end + end + end end expose :timeout do |model| diff --git a/lib/gitlab/ci/build/image.rb b/lib/gitlab/ci/build/image.rb index c62aeb60fa9..b88b2e36d53 100644 --- a/lib/gitlab/ci/build/image.rb +++ b/lib/gitlab/ci/build/image.rb @@ -2,7 +2,7 @@ module Gitlab module Ci module Build class Image - attr_reader :name + attr_reader :alias, :command, :entrypoint, :name class << self def from_image(job) @@ -21,7 +21,14 @@ module Gitlab end def initialize(image) - @name = image + if image.is_a?(String) + @name = image + elsif image.is_a?(Hash) + @alias = image[:alias] + @command = image[:command] + @entrypoint = image[:entrypoint] + @name = image[:name] + end end def valid? diff --git a/lib/gitlab/ci/config/entry/image.rb b/lib/gitlab/ci/config/entry/image.rb index b5050257688..897dcff8012 100644 --- a/lib/gitlab/ci/config/entry/image.rb +++ b/lib/gitlab/ci/config/entry/image.rb @@ -8,8 +8,36 @@ module Gitlab class Image < Node include Validatable + ALLOWED_KEYS = %i[name entrypoint].freeze + validations do - validates :config, type: String + validates :config, hash_or_string: true + validates :config, allowed_keys: ALLOWED_KEYS + + validates :name, type: String, presence: true + validates :entrypoint, type: String, allow_nil: true + end + + def hash? + @config.is_a?(Hash) + end + + def string? + @config.is_a?(String) + end + + def name + value[:name] + end + + def entrypoint + value[:entrypoint] + end + + def value + return { name: @config } if string? + return @config if hash? + {} end end end diff --git a/lib/gitlab/ci/config/entry/service.rb b/lib/gitlab/ci/config/entry/service.rb new file mode 100644 index 00000000000..b52faf48b58 --- /dev/null +++ b/lib/gitlab/ci/config/entry/service.rb @@ -0,0 +1,34 @@ +module Gitlab + module Ci + class Config + module Entry + ## + # Entry that represents a configuration of Docker service. + # + class Service < Image + include Validatable + + ALLOWED_KEYS = %i[name entrypoint command alias].freeze + + validations do + validates :config, hash_or_string: true + validates :config, allowed_keys: ALLOWED_KEYS + + validates :name, type: String, presence: true + validates :entrypoint, type: String, allow_nil: true + validates :command, type: String, allow_nil: true + validates :alias, type: String, allow_nil: true + end + + def alias + value[:alias] + end + + def command + value[:command] + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/entry/services.rb b/lib/gitlab/ci/config/entry/services.rb index 84f8ab780f5..0066894e069 100644 --- a/lib/gitlab/ci/config/entry/services.rb +++ b/lib/gitlab/ci/config/entry/services.rb @@ -9,7 +9,30 @@ module Gitlab include Validatable validations do - validates :config, array_of_strings: true + validates :config, type: Array + end + + def compose!(deps = nil) + super do + @entries = [] + @config.each do |config| + @entries << Entry::Factory.new(Entry::Service) + .value(config || {}) + .create! + end + + @entries.each do |entry| + entry.compose!(deps) + end + end + end + + def value + @entries.map(&:value) + end + + def descendants + @entries end end end diff --git a/lib/gitlab/ci/config/entry/validators.rb b/lib/gitlab/ci/config/entry/validators.rb index bd7428b1272..b2ca3c881e4 100644 --- a/lib/gitlab/ci/config/entry/validators.rb +++ b/lib/gitlab/ci/config/entry/validators.rb @@ -44,6 +44,14 @@ module Gitlab end end + class HashOrStringValidator < ActiveModel::EachValidator + def validate_each(record, attribute, value) + unless value.is_a?(Hash) || value.is_a?(String) + record.errors.add(attribute, 'should be a hash or a string') + end + end + end + class KeyValidator < ActiveModel::EachValidator include LegacyValidationHelpers diff --git a/lib/gitlab/ci/status/external/common.rb b/lib/gitlab/ci/status/external/common.rb index 4969a350862..9307545b5b1 100644 --- a/lib/gitlab/ci/status/external/common.rb +++ b/lib/gitlab/ci/status/external/common.rb @@ -3,6 +3,10 @@ module Gitlab module Status module External module Common + def label + subject.description + end + def has_details? subject.target_url.present? && can?(user, :read_commit_status, subject) diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb index 4212a0dbe2e..d2863a4da71 100644 --- a/lib/gitlab/diff/file.rb +++ b/lib/gitlab/diff/file.rb @@ -5,7 +5,20 @@ module Gitlab delegate :new_file?, :deleted_file?, :renamed_file?, :old_path, :new_path, :a_mode, :b_mode, :mode_changed?, - :submodule?, :too_large?, :collapsed?, to: :diff, prefix: false + :submodule?, :expanded?, :too_large?, :collapsed?, :line_count, to: :diff, prefix: false + + # Finding a viewer for a diff file happens based only on extension and whether the + # diff file blobs are binary or text, which means 1 diff file should only be matched by 1 viewer, + # and the order of these viewers doesn't really matter. + # + # However, when the diff file blobs are LFS pointers, we cannot know for sure whether the + # file being pointed to is binary or text. In this case, we match only on + # extension, preferring binary viewers over text ones if both exist, since the + # large files referred to in "Large File Storage" are much more likely to be + # binary than text. + RICH_VIEWERS = [ + DiffViewer::Image + ].sort_by { |v| v.binary? ? 0 : 1 }.freeze def initialize(diff, repository:, diff_refs: nil, fallback_diff_refs: nil) @diff = diff @@ -177,6 +190,100 @@ module Gitlab def text? !binary? end + + def external_storage_error? + old_blob&.external_storage_error? || new_blob&.external_storage_error? + end + + def stored_externally? + old_blob&.stored_externally? || new_blob&.stored_externally? + end + + def external_storage + old_blob&.external_storage || new_blob&.external_storage + end + + def content_changed? + old_blob && new_blob && old_blob.id != new_blob.id + end + + def different_type? + old_blob && new_blob && old_blob.binary? != new_blob.binary? + end + + def size + [old_blob&.size, new_blob&.size].compact.sum + end + + def raw_size + [old_blob&.raw_size, new_blob&.raw_size].compact.sum + end + + def raw_binary? + old_blob&.raw_binary? || new_blob&.raw_binary? + end + + def raw_text? + !raw_binary? && !different_type? + end + + def simple_viewer + @simple_viewer ||= simple_viewer_class.new(self) + end + + def rich_viewer + return @rich_viewer if defined?(@rich_viewer) + + @rich_viewer = rich_viewer_class&.new(self) + end + + def rendered_as_text?(ignore_errors: true) + simple_viewer.is_a?(DiffViewer::Text) && (ignore_errors || simple_viewer.render_error.nil?) + end + + private + + def simple_viewer_class + return DiffViewer::NotDiffable unless diffable? + + if content_changed? + if raw_text? + DiffViewer::Text + else + DiffViewer::NoPreview + end + elsif new_file? + if raw_text? + DiffViewer::Text + else + DiffViewer::Added + end + elsif deleted_file? + if raw_text? + DiffViewer::Text + else + DiffViewer::Deleted + end + elsif renamed_file? + DiffViewer::Renamed + elsif mode_changed? + DiffViewer::ModeChanged + end + end + + def rich_viewer_class + viewer_class_from(RICH_VIEWERS) + end + + def viewer_class_from(classes) + return unless diffable? + return if different_type? || external_storage_error? + return unless new_file? || deleted_file? || content_changed? + + verify_binary = !stored_externally? + + classes.find { |viewer_class| viewer_class.can_render?(self, verify_binary: verify_binary) } + end end end end diff --git a/lib/gitlab/etag_caching/middleware.rb b/lib/gitlab/etag_caching/middleware.rb index 7f884183bb1..1d6f5bb5e1c 100644 --- a/lib/gitlab/etag_caching/middleware.rb +++ b/lib/gitlab/etag_caching/middleware.rb @@ -7,7 +7,7 @@ module Gitlab def call(env) request = Rack::Request.new(env) - route = Gitlab::EtagCaching::Router.match(request) + route = Gitlab::EtagCaching::Router.match(request.path_info) return @app.call(env) unless route track_event(:etag_caching_middleware_used, route) diff --git a/lib/gitlab/etag_caching/router.rb b/lib/gitlab/etag_caching/router.rb index dccc66b3918..75167a6b088 100644 --- a/lib/gitlab/etag_caching/router.rb +++ b/lib/gitlab/etag_caching/router.rb @@ -53,8 +53,8 @@ module Gitlab ) ].freeze - def self.match(request) - ROUTES.find { |route| route.regexp.match(request.path_info) } + def self.match(path) + ROUTES.find { |route| route.regexp.match(path) } end end end diff --git a/lib/gitlab/etag_caching/store.rb b/lib/gitlab/etag_caching/store.rb index 0039fc01c8f..072fcfc65e6 100644 --- a/lib/gitlab/etag_caching/store.rb +++ b/lib/gitlab/etag_caching/store.rb @@ -25,6 +25,8 @@ module Gitlab end def redis_key(key) + raise 'Invalid key' if !Rails.env.production? && !Gitlab::EtagCaching::Router.match(key) + "#{REDIS_NAMESPACE}#{key}" end end diff --git a/lib/gitlab/git/diff.rb b/lib/gitlab/git/diff.rb index 88ad760bea3..4b689f0e94f 100644 --- a/lib/gitlab/git/diff.rb +++ b/lib/gitlab/git/diff.rb @@ -17,6 +17,8 @@ module Gitlab attr_accessor :expanded + alias_method :expanded?, :expanded + # We need this accessor because of `to_hash` and `init_from_hash` attr_accessor :too_large diff --git a/lib/gitlab/git/diff_collection.rb b/lib/gitlab/git/diff_collection.rb index 334e06a6eca..555894907cc 100644 --- a/lib/gitlab/git/diff_collection.rb +++ b/lib/gitlab/git/diff_collection.rb @@ -97,7 +97,7 @@ module Gitlab diff = Gitlab::Git::Diff.new(raw, expanded: expanded) - if !expanded && over_safe_limits?(i) + if !expanded && over_safe_limits?(i) && diff.line_count > 0 diff.collapse! end diff --git a/lib/gitlab/kubernetes.rb b/lib/gitlab/kubernetes.rb index 4a6091488c8..c56c1a4322f 100644 --- a/lib/gitlab/kubernetes.rb +++ b/lib/gitlab/kubernetes.rb @@ -8,13 +8,13 @@ module Gitlab ) # Filters an array of pods (as returned by the kubernetes API) by their labels - def filter_pods(pods, labels = {}) - pods.select do |pod| - metadata = pod.fetch("metadata", {}) - pod_labels = metadata.fetch("labels", nil) - next unless pod_labels + def filter_by_label(items, labels = {}) + items.select do |item| + metadata = item.fetch("metadata", {}) + item_labels = metadata.fetch("labels", nil) + next unless item_labels - labels.all? { |k, v| pod_labels[k.to_s] == v } + labels.all? { |k, v| item_labels[k.to_s] == v } end end diff --git a/rubocop/cop/rspec/single_line_hook.rb b/rubocop/cop/rspec/single_line_hook.rb new file mode 100644 index 00000000000..be611054323 --- /dev/null +++ b/rubocop/cop/rspec/single_line_hook.rb @@ -0,0 +1,38 @@ +require 'rubocop-rspec' + +module RuboCop + module Cop + module RSpec + # This cop checks for single-line hook blocks + # + # @example + # + # # bad + # before { do_something } + # after(:each) { undo_something } + # + # # good + # before do + # do_something + # end + # + # after(:each) do + # undo_something + # end + class SingleLineHook < Cop + MESSAGE = "Don't use single-line hook blocks.".freeze + + def_node_search :rspec_hook?, <<~PATTERN + (send nil {:after :around :before} ...) + PATTERN + + def on_block(node) + return unless rspec_hook?(node) + return unless node.single_line? + + add_offense(node, :expression, MESSAGE) + end + end + end + end +end diff --git a/rubocop/rubocop.rb b/rubocop/rubocop.rb index 6b8d127dde6..55d7708fa8c 100644 --- a/rubocop/rubocop.rb +++ b/rubocop/rubocop.rb @@ -15,3 +15,4 @@ require_relative 'cop/migration/remove_index' require_relative 'cop/migration/reversible_add_column_with_default' require_relative 'cop/migration/timestamps' require_relative 'cop/migration/update_column_in_batches' +require_relative 'cop/rspec/single_line_hook' diff --git a/spec/controllers/admin/identities_controller_spec.rb b/spec/controllers/admin/identities_controller_spec.rb index c131d22a30a..a29853bf8df 100644 --- a/spec/controllers/admin/identities_controller_spec.rb +++ b/spec/controllers/admin/identities_controller_spec.rb @@ -2,7 +2,10 @@ require 'spec_helper' describe Admin::IdentitiesController do let(:admin) { create(:admin) } - before { sign_in(admin) } + + before do + sign_in(admin) + end describe 'UPDATE identity' do let(:user) { create(:omniauth_user, provider: 'ldapmain', extern_uid: 'uid=myuser,ou=people,dc=example,dc=com') } diff --git a/spec/controllers/admin/services_controller_spec.rb b/spec/controllers/admin/services_controller_spec.rb index c94616d8508..4ca0cfc74e9 100644 --- a/spec/controllers/admin/services_controller_spec.rb +++ b/spec/controllers/admin/services_controller_spec.rb @@ -3,7 +3,9 @@ require 'spec_helper' describe Admin::ServicesController do let(:admin) { create(:admin) } - before { sign_in(admin) } + before do + sign_in(admin) + end describe 'GET #edit' do let!(:project) { create(:empty_project) } diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb index 4c3a5ec49ef..b40f647644d 100644 --- a/spec/controllers/autocomplete_controller_spec.rb +++ b/spec/controllers/autocomplete_controller_spec.rb @@ -200,7 +200,9 @@ describe AutocompleteController do end context 'skip_users parameter included' do - before { sign_in(user) } + before do + sign_in(user) + end it 'skips the user IDs passed' do get(:users, skip_users: [user, user2].map(&:id)) diff --git a/spec/controllers/groups/group_members_controller_spec.rb b/spec/controllers/groups/group_members_controller_spec.rb index ed4ad7b600e..cce53f6697c 100644 --- a/spec/controllers/groups/group_members_controller_spec.rb +++ b/spec/controllers/groups/group_members_controller_spec.rb @@ -16,10 +16,14 @@ describe Groups::GroupMembersController do describe 'POST create' do let(:group_user) { create(:user) } - before { sign_in(user) } + before do + sign_in(user) + end context 'when user does not have enough rights' do - before { group.add_developer(user) } + before do + group.add_developer(user) + end it 'returns 403' do post :create, group_id: group, @@ -32,7 +36,9 @@ describe Groups::GroupMembersController do end context 'when user has enough rights' do - before { group.add_owner(user) } + before do + group.add_owner(user) + end it 'adds user to members' do post :create, group_id: group, @@ -59,7 +65,9 @@ describe Groups::GroupMembersController do describe 'DELETE destroy' do let(:member) { create(:group_member, :developer, group: group) } - before { sign_in(user) } + before do + sign_in(user) + end context 'when member is not found' do it 'returns 403' do @@ -71,7 +79,9 @@ describe Groups::GroupMembersController do context 'when member is found' do context 'when user does not have enough rights' do - before { group.add_developer(user) } + before do + group.add_developer(user) + end it 'returns 403' do delete :destroy, group_id: group, id: member @@ -82,7 +92,9 @@ describe Groups::GroupMembersController do end context 'when user has enough rights' do - before { group.add_owner(user) } + before do + group.add_owner(user) + end it '[HTML] removes user from members' do delete :destroy, group_id: group, id: member @@ -103,7 +115,9 @@ describe Groups::GroupMembersController do end describe 'DELETE leave' do - before { sign_in(user) } + before do + sign_in(user) + end context 'when member is not found' do it 'returns 404' do @@ -115,7 +129,9 @@ describe Groups::GroupMembersController do context 'when member is found' do context 'and is not an owner' do - before { group.add_developer(user) } + before do + group.add_developer(user) + end it 'removes user from members' do delete :leave, group_id: group @@ -134,7 +150,9 @@ describe Groups::GroupMembersController do end context 'and is an owner' do - before { group.add_owner(user) } + before do + group.add_owner(user) + end it 'cannot removes himself from the group' do delete :leave, group_id: group @@ -144,7 +162,9 @@ describe Groups::GroupMembersController do end context 'and is a requester' do - before { group.request_access(user) } + before do + group.request_access(user) + end it 'removes user from members' do delete :leave, group_id: group @@ -159,7 +179,9 @@ describe Groups::GroupMembersController do end describe 'POST request_access' do - before { sign_in(user) } + before do + sign_in(user) + end it 'creates a new GroupMember that is not a team member' do post :request_access, group_id: group @@ -174,7 +196,9 @@ describe Groups::GroupMembersController do describe 'POST approve_access_request' do let(:member) { create(:group_member, :access_request, group: group) } - before { sign_in(user) } + before do + sign_in(user) + end context 'when member is not found' do it 'returns 403' do @@ -186,7 +210,9 @@ describe Groups::GroupMembersController do context 'when member is found' do context 'when user does not have enough rights' do - before { group.add_developer(user) } + before do + group.add_developer(user) + end it 'returns 403' do post :approve_access_request, group_id: group, id: member @@ -197,7 +223,9 @@ describe Groups::GroupMembersController do end context 'when user has enough rights' do - before { group.add_owner(user) } + before do + group.add_owner(user) + end it 'adds user to members' do post :approve_access_request, group_id: group, id: member diff --git a/spec/controllers/notification_settings_controller_spec.rb b/spec/controllers/notification_settings_controller_spec.rb index 9e3a31e1a6b..cf136e72bac 100644 --- a/spec/controllers/notification_settings_controller_spec.rb +++ b/spec/controllers/notification_settings_controller_spec.rb @@ -94,7 +94,10 @@ describe NotificationSettingsController do context 'not authorized' do let(:private_project) { create(:empty_project, :private) } - before { sign_in(user) } + + before do + sign_in(user) + end it 'returns 404' do post :create, @@ -120,7 +123,9 @@ describe NotificationSettingsController do end context 'when authorized' do - before{ sign_in(user) } + before do + sign_in(user) + end it 'returns success' do put :update, @@ -152,7 +157,9 @@ describe NotificationSettingsController do context 'not authorized' do let(:other_user) { create(:user) } - before { sign_in(other_user) } + before do + sign_in(other_user) + end it 'returns 404' do put :update, diff --git a/spec/controllers/profiles/personal_access_tokens_controller_spec.rb b/spec/controllers/profiles/personal_access_tokens_controller_spec.rb index 98a43e278b2..ed08a4c1bf2 100644 --- a/spec/controllers/profiles/personal_access_tokens_controller_spec.rb +++ b/spec/controllers/profiles/personal_access_tokens_controller_spec.rb @@ -4,7 +4,9 @@ describe Profiles::PersonalAccessTokensController do let(:user) { create(:user) } let(:token_attributes) { attributes_for(:personal_access_token) } - before { sign_in(user) } + before do + sign_in(user) + end describe '#create' do def created_token @@ -38,7 +40,9 @@ describe Profiles::PersonalAccessTokensController do let!(:inactive_personal_access_token) { create(:personal_access_token, :revoked, user: user) } let!(:impersonation_personal_access_token) { create(:personal_access_token, :impersonation, user: user) } - before { get :index } + before do + get :index + end it "retrieves active personal access tokens" do expect(assigns(:active_personal_access_tokens)).to include(active_personal_access_token) diff --git a/spec/controllers/projects/commit_controller_spec.rb b/spec/controllers/projects/commit_controller_spec.rb index 69e4706dc71..7fb08df1950 100644 --- a/spec/controllers/projects/commit_controller_spec.rb +++ b/spec/controllers/projects/commit_controller_spec.rb @@ -281,7 +281,9 @@ describe Projects::CommitController do end context 'when the path does not exist in the diff' do - before { diff_for_path(id: commit.id, old_path: existing_path.succ, new_path: existing_path.succ) } + before do + diff_for_path(id: commit.id, old_path: existing_path.succ, new_path: existing_path.succ) + end it 'returns a 404' do expect(response).to have_http_status(404) @@ -302,7 +304,9 @@ describe Projects::CommitController do end context 'when the commit does not exist' do - before { diff_for_path(id: commit.id.succ, old_path: existing_path, new_path: existing_path) } + before do + diff_for_path(id: commit.id.succ, old_path: existing_path, new_path: existing_path) + end it 'returns a 404' do expect(response).to have_http_status(404) diff --git a/spec/controllers/projects/compare_controller_spec.rb b/spec/controllers/projects/compare_controller_spec.rb index 15ac4e0925a..8f4694c9854 100644 --- a/spec/controllers/projects/compare_controller_spec.rb +++ b/spec/controllers/projects/compare_controller_spec.rb @@ -128,7 +128,9 @@ describe Projects::CompareController do end context 'when the path does not exist in the diff' do - before { diff_for_path(from: ref_from, to: ref_to, old_path: existing_path.succ, new_path: existing_path.succ) } + before do + diff_for_path(from: ref_from, to: ref_to, old_path: existing_path.succ, new_path: existing_path.succ) + end it 'returns a 404' do expect(response).to have_http_status(404) @@ -149,7 +151,9 @@ describe Projects::CompareController do end context 'when the from ref does not exist' do - before { diff_for_path(from: ref_from.succ, to: ref_to, old_path: existing_path, new_path: existing_path) } + before do + diff_for_path(from: ref_from.succ, to: ref_to, old_path: existing_path, new_path: existing_path) + end it 'returns a 404' do expect(response).to have_http_status(404) @@ -157,7 +161,9 @@ describe Projects::CompareController do end context 'when the to ref does not exist' do - before { diff_for_path(from: ref_from, to: ref_to.succ, old_path: existing_path, new_path: existing_path) } + before do + diff_for_path(from: ref_from, to: ref_to.succ, old_path: existing_path, new_path: existing_path) + end it 'returns a 404' do expect(response).to have_http_status(404) diff --git a/spec/controllers/projects/forks_controller_spec.rb b/spec/controllers/projects/forks_controller_spec.rb index 8282d79298f..dc8290c438e 100644 --- a/spec/controllers/projects/forks_controller_spec.rb +++ b/spec/controllers/projects/forks_controller_spec.rb @@ -14,7 +14,9 @@ describe Projects::ForksController do end context 'when fork is public' do - before { forked_project.update_attribute(:visibility_level, Project::PUBLIC) } + before do + forked_project.update_attribute(:visibility_level, Project::PUBLIC) + end it 'is visible for non logged in users' do get_forks @@ -35,7 +37,9 @@ describe Projects::ForksController do end context 'when user is logged in' do - before { sign_in(project.creator) } + before do + sign_in(project.creator) + end context 'when user is not a Project member neither a group member' do it 'does not see the Project listed' do @@ -46,7 +50,9 @@ describe Projects::ForksController do end context 'when user is a member of the Project' do - before { forked_project.team << [project.creator, :developer] } + before do + forked_project.team << [project.creator, :developer] + end it 'sees the project listed' do get_forks @@ -56,7 +62,9 @@ describe Projects::ForksController do end context 'when user is a member of the Group' do - before { forked_project.group.add_developer(project.creator) } + before do + forked_project.group.add_developer(project.creator) + end it 'sees the project listed' do get_forks diff --git a/spec/controllers/projects/group_links_controller_spec.rb b/spec/controllers/projects/group_links_controller_spec.rb index ca4a8e871c0..b5435357f53 100644 --- a/spec/controllers/projects/group_links_controller_spec.rb +++ b/spec/controllers/projects/group_links_controller_spec.rb @@ -22,7 +22,10 @@ describe Projects::GroupLinksController do end context 'when user has access to group he want to link project to' do - before { group.add_developer(user) } + before do + group.add_developer(user) + end + include_context 'link project to group' it 'links project with selected group' do diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index b65e9e0dfc0..f853bfe370c 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -212,7 +212,9 @@ describe Projects::IssuesController do let(:another_project) { create(:empty_project, :private) } context 'when user has access to move issue' do - before { another_project.team << [user, :reporter] } + before do + another_project.team << [user, :reporter] + end it 'moves issue to another project' do move_issue @@ -250,14 +252,18 @@ describe Projects::IssuesController do end context 'when an issue is identified as spam' do - before { allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true) } + before do + allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true) + end context 'when captcha is not verified' do def update_spam_issue update_issue(title: 'Spam Title', description: 'Spam lives here') end - before { allow_any_instance_of(described_class).to receive(:verify_recaptcha).and_return(false) } + before do + allow_any_instance_of(described_class).to receive(:verify_recaptcha).and_return(false) + end it 'rejects an issue recognized as a spam' do expect(Gitlab::Recaptcha).to receive(:load_configurations!).and_return(true) @@ -620,14 +626,18 @@ describe Projects::IssuesController do end context 'when an issue is identified as spam' do - before { allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true) } + before do + allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true) + end context 'when captcha is not verified' do def post_spam_issue post_new_issue(title: 'Spam Title', description: 'Spam lives here') end - before { allow_any_instance_of(described_class).to receive(:verify_recaptcha).and_return(false) } + before do + allow_any_instance_of(described_class).to receive(:verify_recaptcha).and_return(false) + end it 'rejects an issue recognized as a spam' do expect { post_spam_issue }.not_to change(Issue, :count) @@ -739,7 +749,10 @@ describe Projects::IssuesController do describe "DELETE #destroy" do context "when the user is a developer" do - before { sign_in(user) } + before do + sign_in(user) + end + it "rejects a developer to destroy an issue" do delete :destroy, namespace_id: project.namespace, project_id: project, id: issue.iid expect(response).to have_http_status(404) @@ -751,7 +764,9 @@ describe Projects::IssuesController do let(:namespace) { create(:namespace, owner: owner) } let(:project) { create(:empty_project, namespace: namespace) } - before { sign_in(owner) } + before do + sign_in(owner) + end it "deletes the issue" do delete :destroy, namespace_id: project.namespace, project_id: project, id: issue.iid diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb index 4a737587899..472e5fc51a0 100644 --- a/spec/controllers/projects/jobs_controller_spec.rb +++ b/spec/controllers/projects/jobs_controller_spec.rb @@ -28,7 +28,7 @@ describe Projects::JobsController do get_index(scope: 'running') end - it 'has only running builds' do + it 'has only running jobs' do expect(response).to have_http_status(:ok) expect(assigns(:builds).first.status).to eq('running') end @@ -41,7 +41,7 @@ describe Projects::JobsController do get_index(scope: 'finished') end - it 'has only finished builds' do + it 'has only finished jobs' do expect(response).to have_http_status(:ok) expect(assigns(:builds).first.status).to eq('success') end @@ -67,7 +67,7 @@ describe Projects::JobsController do context 'number of queries' do before do Ci::Build::AVAILABLE_STATUSES.each do |status| - create_build(status, status) + create_job(status, status) end end @@ -76,7 +76,7 @@ describe Projects::JobsController do expect(recorded.count).to be_within(5).of(7) end - def create_build(name, status) + def create_job(name, status) pipeline = create(:ci_pipeline, project: project) create(:ci_build, :tags, :triggered, :artifacts, pipeline: pipeline, name: name, status: status) @@ -94,21 +94,21 @@ describe Projects::JobsController do end describe 'GET show' do - let!(:build) { create(:ci_build, :failed, pipeline: pipeline) } + let!(:job) { create(:ci_build, :failed, pipeline: pipeline) } context 'when requesting HTML' do - context 'when build exists' do + context 'when job exists' do before do - get_show(id: build.id) + get_show(id: job.id) end - it 'has a build' do + it 'has a job' do expect(response).to have_http_status(:ok) - expect(assigns(:build).id).to eq(build.id) + expect(assigns(:build).id).to eq(job.id) end end - context 'when build does not exist' do + context 'when job does not exist' do before do get_show(id: 1234) end @@ -128,12 +128,12 @@ describe Projects::JobsController do allow_any_instance_of(Ci::Build).to receive(:merge_request).and_return(merge_request) - get_show(id: build.id, format: :json) + get_show(id: job.id, format: :json) end it 'exposes needed information' do expect(response).to have_http_status(:ok) - expect(json_response['raw_path']).to match(/builds\/\d+\/raw\z/) + expect(json_response['raw_path']).to match(/jobs\/\d+\/raw\z/) expect(json_response.dig('merge_request', 'path')).to match(/merge_requests\/\d+\z/) expect(json_response['new_issue_path']) .to include('/issues/new') @@ -155,35 +155,35 @@ describe Projects::JobsController do get_trace end - context 'when build has a trace' do - let(:build) { create(:ci_build, :trace, pipeline: pipeline) } + context 'when job has a trace' do + let(:job) { create(:ci_build, :trace, pipeline: pipeline) } it 'returns a trace' do expect(response).to have_http_status(:ok) - expect(json_response['id']).to eq build.id - expect(json_response['status']).to eq build.status + expect(json_response['id']).to eq job.id + expect(json_response['status']).to eq job.status expect(json_response['html']).to eq('BUILD TRACE') end end - context 'when build has no traces' do - let(:build) { create(:ci_build, pipeline: pipeline) } + context 'when job has no traces' do + let(:job) { create(:ci_build, pipeline: pipeline) } it 'returns no traces' do expect(response).to have_http_status(:ok) - expect(json_response['id']).to eq build.id - expect(json_response['status']).to eq build.status + expect(json_response['id']).to eq job.id + expect(json_response['status']).to eq job.status expect(json_response['html']).to be_nil end end - context 'when build has a trace with ANSI sequence and Unicode' do - let(:build) { create(:ci_build, :unicode_trace, pipeline: pipeline) } + context 'when job has a trace with ANSI sequence and Unicode' do + let(:job) { create(:ci_build, :unicode_trace, pipeline: pipeline) } it 'returns a trace with Unicode' do expect(response).to have_http_status(:ok) - expect(json_response['id']).to eq build.id - expect(json_response['status']).to eq build.status + expect(json_response['id']).to eq job.id + expect(json_response['status']).to eq job.status expect(json_response['html']).to include("ヾ(´༎ຶД༎ຶ`)ノ") end end @@ -191,23 +191,23 @@ describe Projects::JobsController do def get_trace get :trace, namespace_id: project.namespace, project_id: project, - id: build.id, + id: job.id, format: :json end end describe 'GET status.json' do - let(:build) { create(:ci_build, pipeline: pipeline) } - let(:status) { build.detailed_status(double('user')) } + let(:job) { create(:ci_build, pipeline: pipeline) } + let(:status) { job.detailed_status(double('user')) } before do get :status, namespace_id: project.namespace, project_id: project, - id: build.id, + id: job.id, format: :json end - it 'return a detailed build status in json' do + it 'return a detailed job status in json' do expect(response).to have_http_status(:ok) expect(json_response['text']).to eq status.text expect(json_response['label']).to eq status.label @@ -224,17 +224,17 @@ describe Projects::JobsController do post_retry end - context 'when build is retryable' do - let(:build) { create(:ci_build, :retryable, pipeline: pipeline) } + context 'when job is retryable' do + let(:job) { create(:ci_build, :retryable, pipeline: pipeline) } - it 'redirects to the retried build page' do + it 'redirects to the retried job page' do expect(response).to have_http_status(:found) expect(response).to redirect_to(namespace_project_job_path(id: Ci::Build.last.id)) end end - context 'when build is not retryable' do - let(:build) { create(:ci_build, pipeline: pipeline) } + context 'when job is not retryable' do + let(:job) { create(:ci_build, pipeline: pipeline) } it 'renders unprocessable_entity' do expect(response).to have_http_status(:unprocessable_entity) @@ -244,7 +244,7 @@ describe Projects::JobsController do def post_retry post :retry, namespace_id: project.namespace, project_id: project, - id: build.id + id: job.id end end @@ -260,21 +260,21 @@ describe Projects::JobsController do post_play end - context 'when build is playable' do - let(:build) { create(:ci_build, :playable, pipeline: pipeline) } + context 'when job is playable' do + let(:job) { create(:ci_build, :playable, pipeline: pipeline) } - it 'redirects to the played build page' do + it 'redirects to the played job page' do expect(response).to have_http_status(:found) - expect(response).to redirect_to(namespace_project_job_path(id: build.id)) + expect(response).to redirect_to(namespace_project_job_path(id: job.id)) end it 'transits to pending' do - expect(build.reload).to be_pending + expect(job.reload).to be_pending end end - context 'when build is not playable' do - let(:build) { create(:ci_build, pipeline: pipeline) } + context 'when job is not playable' do + let(:job) { create(:ci_build, pipeline: pipeline) } it 'renders unprocessable_entity' do expect(response).to have_http_status(:unprocessable_entity) @@ -284,7 +284,7 @@ describe Projects::JobsController do def post_play post :play, namespace_id: project.namespace, project_id: project, - id: build.id + id: job.id end end @@ -296,21 +296,21 @@ describe Projects::JobsController do post_cancel end - context 'when build is cancelable' do - let(:build) { create(:ci_build, :cancelable, pipeline: pipeline) } + context 'when job is cancelable' do + let(:job) { create(:ci_build, :cancelable, pipeline: pipeline) } - it 'redirects to the canceled build page' do + it 'redirects to the canceled job page' do expect(response).to have_http_status(:found) - expect(response).to redirect_to(namespace_project_job_path(id: build.id)) + expect(response).to redirect_to(namespace_project_job_path(id: job.id)) end it 'transits to canceled' do - expect(build.reload).to be_canceled + expect(job.reload).to be_canceled end end - context 'when build is not cancelable' do - let(:build) { create(:ci_build, :canceled, pipeline: pipeline) } + context 'when job is not cancelable' do + let(:job) { create(:ci_build, :canceled, pipeline: pipeline) } it 'returns unprocessable_entity' do expect(response).to have_http_status(:unprocessable_entity) @@ -320,7 +320,7 @@ describe Projects::JobsController do def post_cancel post :cancel, namespace_id: project.namespace, project_id: project, - id: build.id + id: job.id end end @@ -330,7 +330,7 @@ describe Projects::JobsController do sign_in(user) end - context 'when builds are cancelable' do + context 'when jobs are cancelable' do before do create_list(:ci_build, 2, :cancelable, pipeline: pipeline) @@ -347,7 +347,7 @@ describe Projects::JobsController do end end - context 'when builds are not cancelable' do + context 'when jobs are not cancelable' do before do create_list(:ci_build, 2, :canceled, pipeline: pipeline) @@ -374,26 +374,26 @@ describe Projects::JobsController do post_erase end - context 'when build is erasable' do - let(:build) { create(:ci_build, :erasable, :trace, pipeline: pipeline) } + context 'when job is erasable' do + let(:job) { create(:ci_build, :erasable, :trace, pipeline: pipeline) } - it 'redirects to the erased build page' do + it 'redirects to the erased job page' do expect(response).to have_http_status(:found) - expect(response).to redirect_to(namespace_project_job_path(id: build.id)) + expect(response).to redirect_to(namespace_project_job_path(id: job.id)) end it 'erases artifacts' do - expect(build.artifacts_file.exists?).to be_falsey - expect(build.artifacts_metadata.exists?).to be_falsey + expect(job.artifacts_file.exists?).to be_falsey + expect(job.artifacts_metadata.exists?).to be_falsey end it 'erases trace' do - expect(build.trace.exist?).to be_falsey + expect(job.trace.exist?).to be_falsey end end - context 'when build is not erasable' do - let(:build) { create(:ci_build, :erased, pipeline: pipeline) } + context 'when job is not erasable' do + let(:job) { create(:ci_build, :erased, pipeline: pipeline) } it 'returns unprocessable_entity' do expect(response).to have_http_status(:unprocessable_entity) @@ -403,7 +403,7 @@ describe Projects::JobsController do def post_erase post :erase, namespace_id: project.namespace, project_id: project, - id: build.id + id: job.id end end @@ -412,8 +412,8 @@ describe Projects::JobsController do get_raw end - context 'when build has a trace file' do - let(:build) { create(:ci_build, :trace, pipeline: pipeline) } + context 'when job has a trace file' do + let(:job) { create(:ci_build, :trace, pipeline: pipeline) } it 'send a trace file' do expect(response).to have_http_status(:ok) @@ -422,8 +422,8 @@ describe Projects::JobsController do end end - context 'when build does not have a trace file' do - let(:build) { create(:ci_build, pipeline: pipeline) } + context 'when job does not have a trace file' do + let(:job) { create(:ci_build, pipeline: pipeline) } it 'returns not_found' do expect(response).to have_http_status(:not_found) @@ -433,7 +433,7 @@ describe Projects::JobsController do def get_raw post :raw, namespace_id: project.namespace, project_id: project, - id: build.id + id: job.id end end end diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 6e1c91738db..d8a3a510f97 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -19,7 +19,10 @@ describe Projects::MergeRequestsController do render_views let(:fork_project) { create(:forked_project_with_submodules) } - before { fork_project.team << [user, :master] } + + before do + fork_project.team << [user, :master] + end context 'when rendering HTML response' do it 'renders new merge request widget template' do @@ -328,7 +331,9 @@ describe Projects::MergeRequestsController do end context 'when the sha parameter does not match the source SHA' do - before { post :merge, base_params.merge(sha: 'foo') } + before do + post :merge, base_params.merge(sha: 'foo') + end it 'returns :sha_mismatch' do expect(json_response).to eq('status' => 'sha_mismatch') @@ -473,7 +478,9 @@ describe Projects::MergeRequestsController do let(:namespace) { create(:namespace, owner: owner) } let(:project) { create(:project, namespace: namespace) } - before { sign_in owner } + before do + sign_in owner + end it "deletes the merge request" do delete :destroy, namespace_id: project.namespace, project_id: project, id: merge_request.iid @@ -505,7 +512,9 @@ describe Projects::MergeRequestsController do context 'with default params' do context 'as html' do - before { go(format: 'html') } + before do + go(format: 'html') + end it 'renders the diff template' do expect(response).to render_template('diffs') @@ -513,7 +522,9 @@ describe Projects::MergeRequestsController do end context 'as json' do - before { go(format: 'json') } + before do + go(format: 'json') + end it 'renders the diffs template to a string' do expect(response).to render_template('projects/merge_requests/show/_diffs') @@ -544,7 +555,9 @@ describe Projects::MergeRequestsController do context 'with ignore_whitespace_change' do context 'as html' do - before { go(format: 'html', w: 1) } + before do + go(format: 'html', w: 1) + end it 'renders the diff template' do expect(response).to render_template('diffs') @@ -552,7 +565,9 @@ describe Projects::MergeRequestsController do end context 'as json' do - before { go(format: 'json', w: 1) } + before do + go(format: 'json', w: 1) + end it 'renders the diffs template to a string' do expect(response).to render_template('projects/merge_requests/show/_diffs') @@ -562,7 +577,9 @@ describe Projects::MergeRequestsController do end context 'with view' do - before { go(view: 'parallel') } + before do + go(view: 'parallel') + end it 'saves the preferred diff view in a cookie' do expect(response.cookies['diff_view']).to eq('parallel') @@ -605,7 +622,9 @@ describe Projects::MergeRequestsController do end context 'when the path does not exist in the diff' do - before { diff_for_path(id: merge_request.iid, old_path: 'files/ruby/nopen.rb', new_path: 'files/ruby/nopen.rb') } + before do + diff_for_path(id: merge_request.iid, old_path: 'files/ruby/nopen.rb', new_path: 'files/ruby/nopen.rb') + end it 'returns a 404' do expect(response).to have_http_status(404) @@ -626,7 +645,9 @@ describe Projects::MergeRequestsController do end context 'when the merge request does not exist' do - before { diff_for_path(id: merge_request.iid.succ, old_path: existing_path, new_path: existing_path) } + before do + diff_for_path(id: merge_request.iid.succ, old_path: existing_path, new_path: existing_path) + end it 'returns a 404' do expect(response).to have_http_status(404) @@ -670,7 +691,9 @@ describe Projects::MergeRequestsController do context 'when the source branch is in a different project to the target' do let(:other_project) { create(:project) } - before { other_project.team << [user, :master] } + before do + other_project.team << [user, :master] + end context 'when the path exists in the diff' do it 'disables diff notes' do @@ -690,7 +713,9 @@ describe Projects::MergeRequestsController do end context 'when the path does not exist in the diff' do - before { diff_for_path(old_path: 'files/ruby/nopen.rb', new_path: 'files/ruby/nopen.rb', merge_request: { source_project: other_project, source_branch: 'feature', target_branch: 'master' }) } + before do + diff_for_path(old_path: 'files/ruby/nopen.rb', new_path: 'files/ruby/nopen.rb', merge_request: { source_project: other_project, source_branch: 'feature', target_branch: 'master' }) + end it 'returns a 404' do expect(response).to have_http_status(404) @@ -913,7 +938,9 @@ describe Projects::MergeRequestsController do end context 'when the file does not exist cannot be resolved in the UI' do - before { conflict_for_path('files/ruby/regexp.rb') } + before do + conflict_for_path('files/ruby/regexp.rb') + end it 'returns a 404 status code' do expect(response).to have_http_status(:not_found) @@ -923,7 +950,9 @@ describe Projects::MergeRequestsController do context 'with an existing file' do let(:path) { 'files/ruby/regex.rb' } - before { conflict_for_path(path) } + before do + conflict_for_path(path) + end it 'returns a 200 status code' do expect(response).to have_http_status(:ok) @@ -1195,7 +1224,9 @@ describe Projects::MergeRequestsController do end context 'when head_pipeline does not exist' do - before { get_pipeline_status } + before do + get_pipeline_status + end it 'return empty' do expect(response).to have_http_status(:ok) diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index 954f89e3854..734532668d3 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -5,9 +5,12 @@ describe Projects::PipelinesController do let(:user) { create(:user) } let(:project) { create(:empty_project, :public) } + let(:feature) { ProjectFeature::DISABLED } before do project.add_developer(user) + project.project_feature.update( + builds_access_level: feature) sign_in(user) end @@ -153,16 +156,26 @@ describe Projects::PipelinesController do format: :json end - it 'retries a pipeline without returning any content' do - expect(response).to have_http_status(:no_content) - expect(build.reload).to be_retried + context 'when builds are enabled' do + let(:feature) { ProjectFeature::ENABLED } + + it 'retries a pipeline without returning any content' do + expect(response).to have_http_status(:no_content) + expect(build.reload).to be_retried + end + end + + context 'when builds are disabled' do + it 'fails to retry pipeline' do + expect(response).to have_http_status(:not_found) + end end end describe 'POST cancel.json' do let!(:pipeline) { create(:ci_pipeline, project: project) } let!(:build) { create(:ci_build, :running, pipeline: pipeline) } - + before do post :cancel, namespace_id: project.namespace, project_id: project, @@ -170,9 +183,19 @@ describe Projects::PipelinesController do format: :json end - it 'cancels a pipeline without returning any content' do - expect(response).to have_http_status(:no_content) - expect(pipeline.reload).to be_canceled + context 'when builds are enabled' do + let(:feature) { ProjectFeature::ENABLED } + + it 'cancels a pipeline without returning any content' do + expect(response).to have_http_status(:no_content) + expect(pipeline.reload).to be_canceled + end + end + + context 'when builds are disabled' do + it 'fails to retry pipeline' do + expect(response).to have_http_status(:not_found) + end end end end diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb index 2294d5df581..f2b59ba82ca 100644 --- a/spec/controllers/projects/project_members_controller_spec.rb +++ b/spec/controllers/projects/project_members_controller_spec.rb @@ -16,10 +16,14 @@ describe Projects::ProjectMembersController do describe 'POST create' do let(:project_user) { create(:user) } - before { sign_in(user) } + before do + sign_in(user) + end context 'when user does not have enough rights' do - before { project.team << [user, :developer] } + before do + project.team << [user, :developer] + end it 'returns 404' do post :create, namespace_id: project.namespace, @@ -33,7 +37,9 @@ describe Projects::ProjectMembersController do end context 'when user has enough rights' do - before { project.team << [user, :master] } + before do + project.team << [user, :master] + end it 'adds user to members' do expect_any_instance_of(Members::CreateService).to receive(:execute).and_return(status: :success) @@ -64,7 +70,9 @@ describe Projects::ProjectMembersController do describe 'DELETE destroy' do let(:member) { create(:project_member, :developer, project: project) } - before { sign_in(user) } + before do + sign_in(user) + end context 'when member is not found' do it 'returns 404' do @@ -78,7 +86,9 @@ describe Projects::ProjectMembersController do context 'when member is found' do context 'when user does not have enough rights' do - before { project.team << [user, :developer] } + before do + project.team << [user, :developer] + end it 'returns 404' do delete :destroy, namespace_id: project.namespace, @@ -91,7 +101,9 @@ describe Projects::ProjectMembersController do end context 'when user has enough rights' do - before { project.team << [user, :master] } + before do + project.team << [user, :master] + end it '[HTML] removes user from members' do delete :destroy, namespace_id: project.namespace, @@ -117,7 +129,9 @@ describe Projects::ProjectMembersController do end describe 'DELETE leave' do - before { sign_in(user) } + before do + sign_in(user) + end context 'when member is not found' do it 'returns 404' do @@ -130,7 +144,9 @@ describe Projects::ProjectMembersController do context 'when member is found' do context 'and is not an owner' do - before { project.team << [user, :developer] } + before do + project.team << [user, :developer] + end it 'removes user from members' do delete :leave, namespace_id: project.namespace, @@ -145,7 +161,9 @@ describe Projects::ProjectMembersController do context 'and is an owner' do let(:project) { create(:empty_project, namespace: user.namespace) } - before { project.team << [user, :master] } + before do + project.team << [user, :master] + end it 'cannot remove himself from the project' do delete :leave, namespace_id: project.namespace, @@ -156,7 +174,9 @@ describe Projects::ProjectMembersController do end context 'and is a requester' do - before { project.request_access(user) } + before do + project.request_access(user) + end it 'removes user from members' do delete :leave, namespace_id: project.namespace, @@ -172,7 +192,9 @@ describe Projects::ProjectMembersController do end describe 'POST request_access' do - before { sign_in(user) } + before do + sign_in(user) + end it 'creates a new ProjectMember that is not a team member' do post :request_access, namespace_id: project.namespace, @@ -190,7 +212,9 @@ describe Projects::ProjectMembersController do describe 'POST approve' do let(:member) { create(:project_member, :access_request, project: project) } - before { sign_in(user) } + before do + sign_in(user) + end context 'when member is not found' do it 'returns 404' do @@ -204,7 +228,9 @@ describe Projects::ProjectMembersController do context 'when member is found' do context 'when user does not have enough rights' do - before { project.team << [user, :developer] } + before do + project.team << [user, :developer] + end it 'returns 404' do post :approve_access_request, namespace_id: project.namespace, @@ -217,7 +243,9 @@ describe Projects::ProjectMembersController do end context 'when user has enough rights' do - before { project.team << [user, :master] } + before do + project.team << [user, :master] + end it 'adds user to members' do post :approve_access_request, namespace_id: project.namespace, @@ -252,7 +280,10 @@ describe Projects::ProjectMembersController do end context 'when user can access source project members' do - before { another_project.team << [user, :guest] } + before do + another_project.team << [user, :guest] + end + include_context 'import applied' it 'imports source project members' do diff --git a/spec/controllers/projects/snippets_controller_spec.rb b/spec/controllers/projects/snippets_controller_spec.rb index 8c23c46798e..2434f822c6f 100644 --- a/spec/controllers/projects/snippets_controller_spec.rb +++ b/spec/controllers/projects/snippets_controller_spec.rb @@ -46,7 +46,9 @@ describe Projects::SnippetsController do end context 'when signed in as the author' do - before { sign_in(user) } + before do + sign_in(user) + end it 'renders the snippet' do get :index, namespace_id: project.namespace, project_id: project @@ -57,7 +59,9 @@ describe Projects::SnippetsController do end context 'when signed in as a project member' do - before { sign_in(user2) } + before do + sign_in(user2) + end it 'renders the snippet' do get :index, namespace_id: project.namespace, project_id: project @@ -317,7 +321,9 @@ describe Projects::SnippetsController do end context 'when signed in as the author' do - before { sign_in(user) } + before do + sign_in(user) + end it 'renders the snippet' do get action, namespace_id: project.namespace, project_id: project, id: project_snippet.to_param @@ -328,7 +334,9 @@ describe Projects::SnippetsController do end context 'when signed in as a project member' do - before { sign_in(user2) } + before do + sign_in(user2) + end it 'renders the snippet' do get action, namespace_id: project.namespace, project_id: project, id: project_snippet.to_param @@ -349,7 +357,9 @@ describe Projects::SnippetsController do end context 'when signed in' do - before { sign_in(user) } + before do + sign_in(user) + end it 'responds with status 404' do get action, namespace_id: project.namespace, project_id: project, id: 42 diff --git a/spec/controllers/projects/tags_controller_spec.rb b/spec/controllers/projects/tags_controller_spec.rb index fc97bac64cd..c48f41ca12e 100644 --- a/spec/controllers/projects/tags_controller_spec.rb +++ b/spec/controllers/projects/tags_controller_spec.rb @@ -6,7 +6,9 @@ describe Projects::TagsController do let!(:invalid_release) { create(:release, project: project, tag: 'does-not-exist') } describe 'GET index' do - before { get :index, namespace_id: project.namespace.to_param, project_id: project } + before do + get :index, namespace_id: project.namespace.to_param, project_id: project + end it 'returns the tags for the page' do expect(assigns(:tags).map(&:name)).to eq(['v1.1.0', 'v1.0.0']) @@ -19,7 +21,9 @@ describe Projects::TagsController do end describe 'GET show' do - before { get :show, namespace_id: project.namespace.to_param, project_id: project, id: id } + before do + get :show, namespace_id: project.namespace.to_param, project_id: project, id: id + end context "valid tag" do let(:id) { 'v1.0.0' } diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 4f6fc6691be..240a81367d0 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -29,7 +29,9 @@ describe ProjectsController do describe "GET show" do context "user not project member" do - before { sign_in(user) } + before do + sign_in(user) + end context "user does not have access to project" do let(:private_project) { create(:empty_project, :private) } @@ -108,7 +110,9 @@ describe ProjectsController do context "project with empty repo" do let(:empty_project) { create(:project_empty_repo, :public) } - before { sign_in(user) } + before do + sign_in(user) + end User.project_views.keys.each do |project_view| context "with #{project_view} view set" do @@ -128,7 +132,9 @@ describe ProjectsController do context "project with broken repo" do let(:empty_project) { create(:project_broken_repo, :public) } - before { sign_in(user) } + before do + sign_in(user) + end User.project_views.keys.each do |project_view| context "with #{project_view} view set" do diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb index 3173aae664c..a3708ad0908 100644 --- a/spec/controllers/search_controller_spec.rb +++ b/spec/controllers/search_controller_spec.rb @@ -18,7 +18,9 @@ describe SearchController do context 'on restricted projects' do context 'when signed out' do - before { sign_out(user) } + before do + sign_out(user) + end it "doesn't expose comments on issues" do project = create(:empty_project, :public, :issues_private) diff --git a/spec/controllers/sent_notifications_controller_spec.rb b/spec/controllers/sent_notifications_controller_spec.rb index 954fc2eaf21..0cc8a3b68eb 100644 --- a/spec/controllers/sent_notifications_controller_spec.rb +++ b/spec/controllers/sent_notifications_controller_spec.rb @@ -14,7 +14,9 @@ describe SentNotificationsController, type: :controller do describe 'GET unsubscribe' do context 'when the user is not logged in' do context 'when the force param is passed' do - before { get(:unsubscribe, id: sent_notification.reply_key, force: true) } + before do + get(:unsubscribe, id: sent_notification.reply_key, force: true) + end it 'unsubscribes the user' do expect(issue.subscribed?(user, project)).to be_falsey @@ -30,7 +32,9 @@ describe SentNotificationsController, type: :controller do end context 'when the force param is not passed' do - before { get(:unsubscribe, id: sent_notification.reply_key) } + before do + get(:unsubscribe, id: sent_notification.reply_key) + end it 'does not unsubscribe the user' do expect(issue.subscribed?(user, project)).to be_truthy @@ -47,10 +51,14 @@ describe SentNotificationsController, type: :controller do end context 'when the user is logged in' do - before { sign_in(user) } + before do + sign_in(user) + end context 'when the ID passed does not exist' do - before { get(:unsubscribe, id: sent_notification.reply_key.reverse) } + before do + get(:unsubscribe, id: sent_notification.reply_key.reverse) + end it 'does not unsubscribe the user' do expect(issue.subscribed?(user, project)).to be_truthy @@ -66,7 +74,9 @@ describe SentNotificationsController, type: :controller do end context 'when the force param is passed' do - before { get(:unsubscribe, id: sent_notification.reply_key, force: true) } + before do + get(:unsubscribe, id: sent_notification.reply_key, force: true) + end it 'unsubscribes the user' do expect(issue.subscribed?(user, project)).to be_falsey @@ -89,7 +99,10 @@ describe SentNotificationsController, type: :controller do end end let(:sent_notification) { create(:sent_notification, project: project, noteable: merge_request, recipient: user) } - before { get(:unsubscribe, id: sent_notification.reply_key) } + + before do + get(:unsubscribe, id: sent_notification.reply_key) + end it 'unsubscribes the user' do expect(merge_request.subscribed?(user, project)).to be_falsey diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb index e87e24a33a1..03f4b0ba343 100644 --- a/spec/controllers/sessions_controller_spec.rb +++ b/spec/controllers/sessions_controller_spec.rb @@ -142,7 +142,9 @@ describe SessionsController do end context 'when OTP is invalid' do - before { authenticate_2fa(otp_attempt: 'invalid') } + before do + authenticate_2fa(otp_attempt: 'invalid') + end it 'does not authenticate' do expect(subject.current_user).not_to eq user @@ -169,7 +171,9 @@ describe SessionsController do end context 'when OTP is invalid' do - before { authenticate_2fa(otp_attempt: 'invalid') } + before do + authenticate_2fa(otp_attempt: 'invalid') + end it 'does not authenticate' do expect(subject.current_user).not_to eq user diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb index 9073c39f562..b1339b2a185 100644 --- a/spec/controllers/snippets_controller_spec.rb +++ b/spec/controllers/snippets_controller_spec.rb @@ -437,7 +437,9 @@ describe SnippetsController do end context 'when signed in user is the author' do - before { get :raw, id: personal_snippet.to_param } + before do + get :raw, id: personal_snippet.to_param + end it 'responds with status 200' do expect(assigns(:snippet)).to eq(personal_snippet) diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index d33e2ba1e53..842d82cdbe9 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -43,7 +43,9 @@ describe UsersController do end context 'when logged in' do - before { sign_in(user) } + before do + sign_in(user) + end it 'renders show' do get :show, username: user.username @@ -62,7 +64,9 @@ describe UsersController do end context 'when logged in' do - before { sign_in(user) } + before do + sign_in(user) + end it 'renders 404' do get :show, username: 'nonexistent' diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb index 0bb5a86d9b9..0cc498f0ce9 100644 --- a/spec/factories/ci/builds.rb +++ b/spec/factories/ci/builds.rb @@ -194,8 +194,8 @@ FactoryGirl.define do trait :extended_options do options do { - image: 'ruby:2.1', - services: ['postgres'], + image: { name: 'ruby:2.1', entrypoint: '/bin/sh' }, + services: ['postgres', { name: 'docker:dind', entrypoint: '/bin/sh', command: 'sleep 30', alias: 'docker' }], after_script: %w(ls date), artifacts: { name: 'artifacts_file', diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb index 5dcc7d35d82..bc11b090fdb 100644 --- a/spec/features/admin/admin_runners_spec.rb +++ b/spec/features/admin/admin_runners_spec.rb @@ -134,7 +134,10 @@ describe "Admin Runners" do describe 'runners registration token' do let!(:token) { current_application_settings.runners_registration_token } - before { visit admin_runners_path } + + before do + visit admin_runners_path + end it 'has a registration token' do expect(page).to have_content("Registration token is #{token}") diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index 5099441dce2..27bc25be580 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -20,10 +20,15 @@ feature 'Admin updates settings', feature: true do uncheck 'Gravatar enabled' fill_in 'Home page URL', with: 'https://about.gitlab.com/' fill_in 'Help page text', with: 'Example text' + check 'Hide marketing-related entries from help' + fill_in 'Support page URL', with: 'http://example.com/help' click_button 'Save' expect(current_application_settings.gravatar_enabled).to be_falsey expect(current_application_settings.home_page_url).to eq "https://about.gitlab.com/" + expect(current_application_settings.help_page_text).to eq "Example text" + expect(current_application_settings.help_page_hide_commercial_content).to be_truthy + expect(current_application_settings.help_page_support_url).to eq "http://example.com/help" expect(page).to have_content "Application settings saved successfully" end diff --git a/spec/features/admin/admin_users_impersonation_tokens_spec.rb b/spec/features/admin/admin_users_impersonation_tokens_spec.rb index 0fb4baeb71c..849ec829f75 100644 --- a/spec/features/admin/admin_users_impersonation_tokens_spec.rb +++ b/spec/features/admin/admin_users_impersonation_tokens_spec.rb @@ -12,7 +12,9 @@ describe 'Admin > Users > Impersonation Tokens', feature: true, js: true do find(".table.inactive-tokens") end - before { login_as(admin) } + before do + login_as(admin) + end describe "token creation" do it "allows creation of a token" do diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb index 301a47169a4..f72651667ee 100644 --- a/spec/features/admin/admin_users_spec.rb +++ b/spec/features/admin/admin_users_spec.rb @@ -124,7 +124,10 @@ describe "Admin::Users", feature: true do describe 'Impersonation' do let(:another_user) { create(:user) } - before { visit admin_user_path(another_user) } + + before do + visit admin_user_path(another_user) + end context 'before impersonating' do it 'shows impersonate button for other users' do @@ -149,7 +152,9 @@ describe "Admin::Users", feature: true do end context 'when impersonating' do - before { click_link 'Impersonate' } + before do + click_link 'Impersonate' + end it 'logs in as the user when impersonate is clicked' do expect(page.find(:css, '.header-user .profile-link')['data-user']).to eql(another_user.username) diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb index 056224dc436..7ba60247587 100644 --- a/spec/features/boards/new_issue_spec.rb +++ b/spec/features/boards/new_issue_spec.rb @@ -19,18 +19,18 @@ describe 'Issue Boards new issue', feature: true, js: true do end it 'displays new issue button' do - expect(first('.board')).to have_selector('.board-issue-count-holder .btn', count: 1) + expect(first('.board')).to have_selector('.issue-count-badge-add-button', count: 1) end it 'does not display new issue button in closed list' do page.within('.board:nth-child(3)') do - expect(page).not_to have_selector('.board-issue-count-holder .btn') + expect(page).not_to have_selector('.issue-count-badge-add-button') end end it 'shows form when clicking button' do page.within(first('.board')) do - find('.board-issue-count-holder .btn').click + find('.issue-count-badge-add-button').click expect(page).to have_selector('.board-new-issue-form') end @@ -38,7 +38,7 @@ describe 'Issue Boards new issue', feature: true, js: true do it 'hides form when clicking cancel' do page.within(first('.board')) do - find('.board-issue-count-holder .btn').click + find('.issue-count-badge-add-button').click expect(page).to have_selector('.board-new-issue-form') @@ -50,7 +50,7 @@ describe 'Issue Boards new issue', feature: true, js: true do it 'creates new issue' do page.within(first('.board')) do - find('.board-issue-count-holder .btn').click + find('.issue-count-badge-add-button').click end page.within(first('.board-new-issue-form')) do @@ -60,14 +60,14 @@ describe 'Issue Boards new issue', feature: true, js: true do wait_for_requests - page.within(first('.board .board-issue-count')) do + page.within(first('.board .issue-count-badge-count')) do expect(page).to have_content('1') end end it 'shows sidebar when creating new issue' do page.within(first('.board')) do - find('.board-issue-count-holder .btn').click + find('.issue-count-badge-add-button').click end page.within(first('.board-new-issue-form')) do @@ -88,7 +88,7 @@ describe 'Issue Boards new issue', feature: true, js: true do end it 'does not display new issue button' do - expect(page).to have_selector('.board-issue-count-holder .btn', count: 0) + expect(page).to have_selector('.issue-count-badge-add-button', count: 0) end end end diff --git a/spec/features/dashboard/merge_requests_spec.rb b/spec/features/dashboard/merge_requests_spec.rb index 9cebe52c444..bcb52f602b0 100644 --- a/spec/features/dashboard/merge_requests_spec.rb +++ b/spec/features/dashboard/merge_requests_spec.rb @@ -12,7 +12,9 @@ describe 'Dashboard Merge Requests' do end describe 'new merge request dropdown' do - before { visit merge_requests_dashboard_path } + before do + visit merge_requests_dashboard_path + end it 'shows projects only with merge requests feature enabled', js: true do find('.new-project-item-select-button').trigger('click') diff --git a/spec/features/dashboard/project_member_activity_index_spec.rb b/spec/features/dashboard/project_member_activity_index_spec.rb index cdf919af9b5..0ba87d921d0 100644 --- a/spec/features/dashboard/project_member_activity_index_spec.rb +++ b/spec/features/dashboard/project_member_activity_index_spec.rb @@ -17,19 +17,25 @@ feature 'Project member activity', feature: true, js: true do subject { page.find(".event-title").text } context 'when a user joins the project' do - before { visit_activities_and_wait_with_event(Event::JOINED) } + before do + visit_activities_and_wait_with_event(Event::JOINED) + end it { is_expected.to eq("#{user.name} joined project") } end context 'when a user leaves the project' do - before { visit_activities_and_wait_with_event(Event::LEFT) } + before do + visit_activities_and_wait_with_event(Event::LEFT) + end it { is_expected.to eq("#{user.name} left project") } end context 'when a users membership expires for the project' do - before { visit_activities_and_wait_with_event(Event::EXPIRED) } + before do + visit_activities_and_wait_with_event(Event::EXPIRED) + end it "presents the correct message" do message = "#{user.name} removed due to membership expiration from project" diff --git a/spec/features/expand_collapse_diffs_spec.rb b/spec/features/expand_collapse_diffs_spec.rb index c4d5077e5e1..36b0c371e6e 100644 --- a/spec/features/expand_collapse_diffs_spec.rb +++ b/spec/features/expand_collapse_diffs_spec.rb @@ -140,7 +140,9 @@ feature 'Expand and collapse diffs', js: true, feature: true do end context 'reloading the page' do - before { refresh } + before do + refresh + end it 'collapses the large diff by default' do expect(large_diff).not_to have_selector('.code') @@ -262,7 +264,7 @@ feature 'Expand and collapse diffs', js: true, feature: true do # Wait for elements to appear to ensure full page reload expect(page).to have_content('This diff was suppressed by a .gitattributes entry') - expect(page).to have_content('This diff could not be displayed because it is too large.') + expect(page).to have_content('This source diff could not be displayed because it is too large.') expect(page).to have_content('too_large_image.jpg') find('.note-textarea') diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb index cc25db4ad60..6afde1d0bed 100644 --- a/spec/features/groups/group_settings_spec.rb +++ b/spec/features/groups/group_settings_spec.rb @@ -52,9 +52,14 @@ feature 'Edit group settings', feature: true do given!(:project) { create(:project, group: group, path: 'project') } given(:old_project_full_path) { "/#{group.path}/#{project.path}" } given(:new_project_full_path) { "/#{new_group_path}/#{project.path}" } - - before(:context) { TestEnv.clean_test_path } - after(:example) { TestEnv.clean_test_path } + + before(:context) do + TestEnv.clean_test_path + end + + after(:example) do + TestEnv.clean_test_path + end scenario 'the project is accessible via the new path' do update_path(new_group_path) diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb index 24ea7aba0cc..5737ca39b4e 100644 --- a/spec/features/groups_spec.rb +++ b/spec/features/groups_spec.rb @@ -12,7 +12,9 @@ feature 'Group', feature: true do end describe 'create a group' do - before { visit new_group_path } + before do + visit new_group_path + end describe 'with space in group path' do it 'renders new group form with validation errors' do @@ -138,7 +140,9 @@ feature 'Group', feature: true do let(:path) { edit_group_path(group) } let(:new_name) { 'new-name' } - before { visit path } + before do + visit path + end it 'saves new settings' do fill_in 'group_name', with: new_name diff --git a/spec/features/help_pages_spec.rb b/spec/features/help_pages_spec.rb index 31014f5cad2..18102146b5f 100644 --- a/spec/features/help_pages_spec.rb +++ b/spec/features/help_pages_spec.rb @@ -37,7 +37,7 @@ describe 'Help Pages', feature: true do context 'in a production environment with version check enabled', :js do before do allow(Rails.env).to receive(:production?) { true } - allow(current_application_settings).to receive(:version_check_enabled) { true } + allow_any_instance_of(ApplicationSetting).to receive(:version_check_enabled) { true } allow_any_instance_of(VersionCheck).to receive(:url) { '/version-check-url' } login_as :user @@ -53,4 +53,27 @@ describe 'Help Pages', feature: true do expect(find('.js-version-status-badge', visible: false)).not_to be_visible end end + + describe 'when help page is customized' do + before do + allow_any_instance_of(ApplicationSetting).to receive(:help_page_hide_commercial_content?) { true } + allow_any_instance_of(ApplicationSetting).to receive(:help_page_text) { "My Custom Text" } + allow_any_instance_of(ApplicationSetting).to receive(:help_page_support_url) { "http://example.com/help" } + + login_as :user + visit help_path + end + + it 'should display custom help page text' do + expect(page).to have_text "My Custom Text" + end + + it 'should hide marketing content when enabled' do + expect(page).not_to have_link "Get a support subscription" + end + + it 'should use a custom support url' do + expect(page).to have_link "See our website for getting help", href: "http://example.com/help" + end + end end diff --git a/spec/features/issuables/default_sort_order_spec.rb b/spec/features/issuables/default_sort_order_spec.rb index bfe43bff10f..56c9b10e757 100644 --- a/spec/features/issuables/default_sort_order_spec.rb +++ b/spec/features/issuables/default_sort_order_spec.rb @@ -153,7 +153,9 @@ describe 'Projects > Issuables > Default sort order', feature: true do context 'when the sort in the URL is id_desc' do let(:issuable_type) { :issue } - before { visit_issues(project, sort: 'id_desc') } + before do + visit_issues(project, sort: 'id_desc') + end it 'shows the sort order as last created' do expect(find('.issues-other-filters')).to have_content('Last created') @@ -165,7 +167,9 @@ describe 'Projects > Issuables > Default sort order', feature: true do context 'when the sort in the URL is id_asc' do let(:issuable_type) { :issue } - before { visit_issues(project, sort: 'id_asc') } + before do + visit_issues(project, sort: 'id_asc') + end it 'shows the sort order as oldest created' do expect(find('.issues-other-filters')).to have_content('Oldest created') diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index eecc565d2bd..2cff53539f3 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -246,7 +246,10 @@ describe 'Issues', feature: true do context 'with a filter on labels' do let(:label) { create(:label, project: project) } - before { create(:label_link, label: label, target: foo) } + + before do + create(:label_link, label: label, target: foo) + end it 'sorts by least recently due date by excluding nil due dates' do bar.update(due_date: nil) diff --git a/spec/features/login_spec.rb b/spec/features/login_spec.rb index c82e8c03343..4763f454810 100644 --- a/spec/features/login_spec.rb +++ b/spec/features/login_spec.rb @@ -202,10 +202,12 @@ feature 'Login', feature: true do # TODO: otp_grace_period_started_at context 'global setting' do - before(:each) { stub_application_setting(require_two_factor_authentication: true) } + before do + stub_application_setting(require_two_factor_authentication: true) + end context 'with grace period defined' do - before(:each) do + before do stub_application_setting(two_factor_grace_period: 48) login_with(user) end @@ -242,7 +244,7 @@ feature 'Login', feature: true do end context 'without grace period defined' do - before(:each) do + before do stub_application_setting(two_factor_grace_period: 0) login_with(user) end @@ -265,7 +267,7 @@ feature 'Login', feature: true do end context 'with grace period defined' do - before(:each) do + before do stub_application_setting(two_factor_grace_period: 48) login_with(user) end @@ -306,7 +308,7 @@ feature 'Login', feature: true do end context 'without grace period defined' do - before(:each) do + before do stub_application_setting(two_factor_grace_period: 0) login_with(user) end diff --git a/spec/features/merge_requests/conflicts_spec.rb b/spec/features/merge_requests/conflicts_spec.rb index 27e2d5d16f3..9409c32104b 100644 --- a/spec/features/merge_requests/conflicts_spec.rb +++ b/spec/features/merge_requests/conflicts_spec.rb @@ -85,14 +85,18 @@ feature 'Merge request conflict resolution', js: true, feature: true do context 'the conflicts are resolvable' do let(:merge_request) { create_merge_request('conflict-resolvable') } - before { visit namespace_project_merge_request_path(project.namespace, project, merge_request) } + before do + visit namespace_project_merge_request_path(project.namespace, project, merge_request) + end it 'shows a link to the conflict resolution page' do expect(page).to have_link('conflicts', href: /\/conflicts\Z/) end context 'in Inline view mode' do - before { click_link('conflicts', href: /\/conflicts\Z/) } + before do + click_link('conflicts', href: /\/conflicts\Z/) + end include_examples "conflicts are resolved in Interactive mode" include_examples "conflicts are resolved in Edit inline mode" diff --git a/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb b/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb index c1d4d508e57..836a7b6e09a 100644 --- a/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb +++ b/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb @@ -18,7 +18,9 @@ feature 'Merge immediately', :feature, :js do sha: project.repository.commit('master').id) end - before { project.team << [user, :master] } + before do + project.team << [user, :master] + end context 'when there is active pipeline for merge request' do background do diff --git a/spec/features/milestones/milestones_spec.rb b/spec/features/milestones/milestones_spec.rb index b3dfd6d0e81..c8a4d23f695 100644 --- a/spec/features/milestones/milestones_spec.rb +++ b/spec/features/milestones/milestones_spec.rb @@ -37,6 +37,14 @@ describe 'Milestone draggable', feature: true, js: true do expect(issue_target).to have_selector('.issuable-row') end + + it 'assigns issue when it has been dragged to ongoing list' do + login_as(:admin) + create_and_drag_issue + + expect(@issue.reload.assignees).not_to be_empty + expect(page).to have_selector("#sortable_issue_#{@issue.iid} .assignee-icon img", count: 1) + end end context 'merge requests' do @@ -72,7 +80,7 @@ describe 'Milestone draggable', feature: true, js: true do end def create_and_drag_issue(params = {}) - create(:issue, params.merge(title: 'Foo', project: project, milestone: milestone)) + @issue = create(:issue, params.merge(title: 'Foo', project: project, milestone: milestone)) visit namespace_project_milestone_path(project.namespace, project, milestone) scroll_into_view('.milestone-content') diff --git a/spec/features/profiles/account_spec.rb b/spec/features/profiles/account_spec.rb index 05a7587f8d4..89868c737f7 100644 --- a/spec/features/profiles/account_spec.rb +++ b/spec/features/profiles/account_spec.rb @@ -31,8 +31,13 @@ feature 'Profile > Account', feature: true do given(:new_project_path) { "/#{new_username}/#{project.path}" } given(:old_project_path) { "/#{user.username}/#{project.path}" } - before(:context) { TestEnv.clean_test_path } - after(:example) { TestEnv.clean_test_path } + before(:context) do + TestEnv.clean_test_path + end + + after(:example) do + TestEnv.clean_test_path + end scenario 'the project is accessible via the new path' do update_username(new_username) diff --git a/spec/features/projects/blobs/blob_show_spec.rb b/spec/features/projects/blobs/blob_show_spec.rb index 45fdb36e506..71ffa352f80 100644 --- a/spec/features/projects/blobs/blob_show_spec.rb +++ b/spec/features/projects/blobs/blob_show_spec.rb @@ -17,6 +17,7 @@ feature 'File blob', :js, feature: true do it 'displays the blob' do aggregate_failures do # shows highlighted Ruby code + expect(page).to have_css(".js-syntax-highlight") expect(page).to have_content("require 'fileutils'") # does not show a viewer switcher @@ -71,6 +72,7 @@ feature 'File blob', :js, feature: true do expect(page).to have_selector('.blob-viewer[data-type="rich"]', visible: false) # shows highlighted Markdown code + expect(page).to have_css(".js-syntax-highlight") expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)") # shows an enabled copy button @@ -114,6 +116,7 @@ feature 'File blob', :js, feature: true do expect(page).to have_selector('#LC1.hll') # shows highlighted Markdown code + expect(page).to have_css(".js-syntax-highlight") expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)") # shows an enabled copy button diff --git a/spec/features/projects/diffs/diff_show_spec.rb b/spec/features/projects/diffs/diff_show_spec.rb new file mode 100644 index 00000000000..48b7f1e0f34 --- /dev/null +++ b/spec/features/projects/diffs/diff_show_spec.rb @@ -0,0 +1,133 @@ +require 'spec_helper' + +feature 'Diff file viewer', :js, feature: true do + let(:project) { create(:project, :public, :repository) } + + def visit_commit(sha, anchor: nil) + visit namespace_project_commit_path(project.namespace, project, sha, anchor: anchor) + + wait_for_requests + end + + context 'Ruby file' do + before do + visit_commit('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') + end + + it 'shows highlighted Ruby code' do + within('.diff-file[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd"]') do + expect(page).to have_css(".js-syntax-highlight") + expect(page).to have_content("def popen(cmd, path=nil)") + end + end + end + + context 'Ruby file (stored in LFS)' do + before do + project.add_master(project.creator) + + @commit_id = Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add Ruby file in LFS", + file_path: 'files/lfs/ruby.rb', + file_content: project.repository.blob_at('master', 'files/lfs/lfs_object.iso').data + ).execute[:result] + end + + context 'when LFS is enabled on the project' do + before do + allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) + project.update_attribute(:lfs_enabled, true) + + visit_commit(@commit_id) + end + + it 'shows an error message' do + expect(page).to have_content('This source diff could not be displayed because it is stored in LFS. You can view the blob instead.') + end + end + + context 'when LFS is disabled on the project' do + before do + visit_commit(@commit_id) + end + + it 'displays the diff' do + expect(page).to have_content('size 1575078') + end + end + end + + context 'Image file' do + before do + visit_commit('2f63565e7aac07bcdadb654e253078b727143ec4') + end + + it 'shows a rendered image' do + within('.diff-file[id="e986451b8f7397b617dbb6fffcb5539328c56921"]') do + expect(page).to have_css('img[alt="files/images/6049019_460s.jpg"]') + end + end + end + + context 'ISO file (stored in LFS)' do + context 'when LFS is enabled on the project' do + before do + allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) + project.update_attribute(:lfs_enabled, true) + + visit_commit('048721d90c449b244b7b4c53a9186b04330174ec') + end + + it 'shows that file was added' do + expect(page).to have_content('File added') + end + end + + context 'when LFS is disabled on the project' do + before do + visit_commit('048721d90c449b244b7b4c53a9186b04330174ec') + end + + it 'displays the diff' do + expect(page).to have_content('size 1575078') + end + end + end + + context 'ZIP file' do + before do + visit_commit('ae73cb07c9eeaf35924a10f713b364d32b2dd34f') + end + + it 'shows that file was added' do + expect(page).to have_content('File added') + end + end + + context 'binary file that appears to be text in the first 1024 bytes' do + before do + visit_commit('7b1cf4336b528e0f3d1d140ee50cafdbc703597c') + end + + it 'shows the diff is collapsed' do + expect(page).to have_content('This diff is collapsed. Click to expand it.') + end + + context 'expanding the diff' do + before do + # We can't use `click_link` because the "link" doesn't have an `href`. + find('a.click-to-expand').click + + wait_for_requests + end + + it 'shows there is no preview' do + expect(page).to have_content('No preview for this file type') + end + end + end +end diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb index c49648f54bd..d76b5e4ef1b 100644 --- a/spec/features/projects/features_visibility_spec.rb +++ b/spec/features/projects/features_visibility_spec.rb @@ -68,9 +68,12 @@ describe 'Edit Project Settings', feature: true do end describe 'project features visibility pages' do + let(:pipeline) { create(:ci_empty_pipeline, project: project) } + let(:job) { create(:ci_build, pipeline: pipeline) } + let(:tools) do { - builds: namespace_project_pipelines_path(project.namespace, project), + builds: namespace_project_job_path(project.namespace, project, job), issues: namespace_project_issues_path(project.namespace, project), wiki: namespace_project_wiki_path(project.namespace, project, :home), snippets: namespace_project_snippets_path(project.namespace, project), diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb index 727ae7081b0..31c93c75d25 100644 --- a/spec/features/projects/jobs_spec.rb +++ b/spec/features/projects/jobs_spec.rb @@ -8,8 +8,8 @@ feature 'Jobs', :feature do let(:namespace) { project.namespace } let(:pipeline) { create(:ci_pipeline, project: project) } - let(:build) { create(:ci_build, :trace, pipeline: pipeline) } - let(:build2) { create(:ci_build) } + let(:job) { create(:ci_build, :trace, pipeline: pipeline) } + let(:job2) { create(:ci_build) } let(:artifacts_file) do fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') @@ -21,7 +21,7 @@ feature 'Jobs', :feature do end describe "GET /:project/jobs" do - let!(:build) { create(:ci_build, pipeline: pipeline) } + let!(:job) { create(:ci_build, pipeline: pipeline) } context "Pending scope" do before do @@ -31,30 +31,30 @@ feature 'Jobs', :feature do it "shows Pending tab jobs" do expect(page).to have_link 'Cancel running' expect(page).to have_selector('.nav-links li.active', text: 'Pending') - expect(page).to have_content build.short_sha - expect(page).to have_content build.ref - expect(page).to have_content build.name + expect(page).to have_content job.short_sha + expect(page).to have_content job.ref + expect(page).to have_content job.name end end context "Running scope" do before do - build.run! + job.run! visit namespace_project_jobs_path(project.namespace, project, scope: :running) end it "shows Running tab jobs" do expect(page).to have_selector('.nav-links li.active', text: 'Running') expect(page).to have_link 'Cancel running' - expect(page).to have_content build.short_sha - expect(page).to have_content build.ref - expect(page).to have_content build.name + expect(page).to have_content job.short_sha + expect(page).to have_content job.ref + expect(page).to have_content job.name end end context "Finished scope" do before do - build.run! + job.run! visit namespace_project_jobs_path(project.namespace, project, scope: :finished) end @@ -73,9 +73,9 @@ feature 'Jobs', :feature do it "shows All tab jobs" do expect(page).to have_selector('.nav-links li.active', text: 'All') - expect(page).to have_content build.short_sha - expect(page).to have_content build.ref - expect(page).to have_content build.name + expect(page).to have_content job.short_sha + expect(page).to have_content job.ref + expect(page).to have_content job.name expect(page).not_to have_link 'Cancel running' end end @@ -97,7 +97,7 @@ feature 'Jobs', :feature do describe "POST /:project/jobs/:id/cancel_all" do before do - build.run! + job.run! visit namespace_project_jobs_path(project.namespace, project) click_link "Cancel running" end @@ -105,19 +105,19 @@ feature 'Jobs', :feature do it 'shows all necessary content' do expect(page).to have_selector('.nav-links li.active', text: 'All') expect(page).to have_content 'canceled' - expect(page).to have_content build.short_sha - expect(page).to have_content build.ref - expect(page).to have_content build.name + expect(page).to have_content job.short_sha + expect(page).to have_content job.ref + expect(page).to have_content job.name expect(page).not_to have_link 'Cancel running' end end describe "GET /:project/jobs/:id" do context "Job from project" do - let(:build) { create(:ci_build, :success, pipeline: pipeline) } + let(:job) { create(:ci_build, :success, pipeline: pipeline) } before do - visit namespace_project_job_path(project.namespace, project, build) + visit namespace_project_job_path(project.namespace, project, job) end it 'shows status name', :js do @@ -131,33 +131,33 @@ feature 'Jobs', :feature do expect(page).to have_content pipeline.git_author_name end - it 'shows active build' do + it 'shows active job' do expect(page).to have_selector('.build-job.active') end end context 'when job is not running', :js do - let(:build) { create(:ci_build, :success, pipeline: pipeline) } + let(:job) { create(:ci_build, :success, pipeline: pipeline) } before do - visit namespace_project_job_path(project.namespace, project, build) + visit namespace_project_job_path(project.namespace, project, job) end it 'shows retry button' do expect(page).to have_link('Retry') end - context 'if build passed' do + context 'if job passed' do it 'does not show New issue button' do expect(page).not_to have_link('New issue') end end - context 'if build failed' do - let(:build) { create(:ci_build, :failed, pipeline: pipeline) } + context 'if job failed' do + let(:job) { create(:ci_build, :failed, pipeline: pipeline) } before do - visit namespace_project_job_path(namespace, project, build) + visit namespace_project_job_path(namespace, project, job) end it 'shows New issue button' do @@ -165,9 +165,9 @@ feature 'Jobs', :feature do end it 'links to issues/new with the title and description filled in' do - button_title = "Build Failed ##{build.id}" - build_path = namespace_project_job_path(namespace, project, build) - options = { issue: { title: button_title, description: build_path } } + button_title = "Build Failed ##{job.id}" + job_path = namespace_project_job_path(namespace, project, job) + options = { issue: { title: button_title, description: job_path } } href = new_namespace_project_issue_path(namespace, project, options) @@ -180,7 +180,7 @@ feature 'Jobs', :feature do context "Job from other project" do before do - visit namespace_project_job_path(project.namespace, project, build2) + visit namespace_project_job_path(project.namespace, project, job2) end it { expect(page.status_code).to eq(404) } @@ -188,8 +188,8 @@ feature 'Jobs', :feature do context "Download artifacts" do before do - build.update_attributes(artifacts_file: artifacts_file) - visit namespace_project_job_path(project.namespace, project, build) + job.update_attributes(artifacts_file: artifacts_file) + visit namespace_project_job_path(project.namespace, project, job) end it 'has button to download artifacts' do @@ -199,10 +199,10 @@ feature 'Jobs', :feature do context 'Artifacts expire date' do before do - build.update_attributes(artifacts_file: artifacts_file, - artifacts_expire_at: expire_at) + job.update_attributes(artifacts_file: artifacts_file, + artifacts_expire_at: expire_at) - visit namespace_project_job_path(project.namespace, project, build) + visit namespace_project_job_path(project.namespace, project, job) end context 'no expire date defined' do @@ -248,7 +248,7 @@ feature 'Jobs', :feature do context "when visiting old URL" do let(:job_url) do - namespace_project_job_path(project.namespace, project, build) + namespace_project_job_path(project.namespace, project, job) end before do @@ -262,9 +262,9 @@ feature 'Jobs', :feature do feature 'Raw trace' do before do - build.run! + job.run! - visit namespace_project_job_path(project.namespace, project, build) + visit namespace_project_job_path(project.namespace, project, job) end it do @@ -274,16 +274,16 @@ feature 'Jobs', :feature do feature 'HTML trace', :js do before do - build.run! + job.run! - visit namespace_project_job_path(project.namespace, project, build) + visit namespace_project_job_path(project.namespace, project, job) end context 'when job has an initial trace' do it 'loads job trace' do expect(page).to have_content 'BUILD TRACE' - build.trace.write do |stream| + job.trace.write do |stream| stream.append(' and more trace', 11) end @@ -295,12 +295,12 @@ feature 'Jobs', :feature do feature 'Variables' do let(:trigger_request) { create(:ci_trigger_request_with_variables) } - let(:build) do + let(:job) do create :ci_build, pipeline: pipeline, trigger_request: trigger_request end before do - visit namespace_project_job_path(project.namespace, project, build) + visit namespace_project_job_path(project.namespace, project, job) end it 'shows variable key and value after click', js: true do @@ -322,20 +322,20 @@ feature 'Jobs', :feature do context 'job is successfull and has deployment' do let(:deployment) { create(:deployment) } - let(:build) { create(:ci_build, :success, environment: environment.name, deployments: [deployment], pipeline: pipeline) } + let(:job) { create(:ci_build, :success, environment: environment.name, deployments: [deployment], pipeline: pipeline) } it 'shows a link for the job' do - visit namespace_project_job_path(project.namespace, project, build) + visit namespace_project_job_path(project.namespace, project, job) expect(page).to have_link environment.name end end context 'job is complete and not successful' do - let(:build) { create(:ci_build, :failed, environment: environment.name, pipeline: pipeline) } + let(:job) { create(:ci_build, :failed, environment: environment.name, pipeline: pipeline) } it 'shows a link for the job' do - visit namespace_project_job_path(project.namespace, project, build) + visit namespace_project_job_path(project.namespace, project, job) expect(page).to have_link environment.name end @@ -343,10 +343,10 @@ feature 'Jobs', :feature do context 'job creates a new deployment' do let!(:deployment) { create(:deployment, environment: environment, sha: project.commit.id) } - let(:build) { create(:ci_build, :success, environment: environment.name, pipeline: pipeline) } + let(:job) { create(:ci_build, :success, environment: environment.name, pipeline: pipeline) } it 'shows a link to latest deployment' do - visit namespace_project_job_path(project.namespace, project, build) + visit namespace_project_job_path(project.namespace, project, job) expect(page).to have_link('latest deployment') end @@ -357,8 +357,8 @@ feature 'Jobs', :feature do describe "POST /:project/jobs/:id/cancel", :js do context "Job from project" do before do - build.run! - visit namespace_project_job_path(project.namespace, project, build) + job.run! + visit namespace_project_job_path(project.namespace, project, job) find('.js-cancel-job').click() end @@ -372,8 +372,8 @@ feature 'Jobs', :feature do describe "POST /:project/jobs/:id/retry" do context "Job from project", :js do before do - build.run! - visit namespace_project_job_path(project.namespace, project, build) + job.run! + visit namespace_project_job_path(project.namespace, project, job) find('.js-cancel-job').click() find('.js-retry-button').trigger('click') end @@ -388,13 +388,13 @@ feature 'Jobs', :feature do context "Job that current user is not allowed to retry" do before do - build.run! - build.cancel! + job.run! + job.cancel! project.update(visibility_level: Gitlab::VisibilityLevel::PUBLIC) logout_direct login_with(create(:user)) - visit namespace_project_job_path(project.namespace, project, build) + visit namespace_project_job_path(project.namespace, project, job) end it 'does not show the Retry button' do @@ -407,15 +407,15 @@ feature 'Jobs', :feature do describe "GET /:project/jobs/:id/download" do before do - build.update_attributes(artifacts_file: artifacts_file) - visit namespace_project_job_path(project.namespace, project, build) + job.update_attributes(artifacts_file: artifacts_file) + visit namespace_project_job_path(project.namespace, project, job) click_link 'Download' end context "Build from other project" do before do - build2.update_attributes(artifacts_file: artifacts_file) - visit download_namespace_project_job_artifacts_path(project.namespace, project, build2) + job2.update_attributes(artifacts_file: artifacts_file) + visit download_namespace_project_job_artifacts_path(project.namespace, project, job2) end it { expect(page.status_code).to eq(404) } @@ -427,23 +427,23 @@ feature 'Jobs', :feature do context 'job from project' do before do Capybara.current_session.driver.headers = { 'X-Sendfile-Type' => 'X-Sendfile' } - build.run! - visit namespace_project_job_path(project.namespace, project, build) + job.run! + visit namespace_project_job_path(project.namespace, project, job) find('.js-raw-link-controller').click() end it 'sends the right headers' do expect(page.status_code).to eq(200) expect(page.response_headers['Content-Type']).to eq('text/plain; charset=utf-8') - expect(page.response_headers['X-Sendfile']).to eq(build.trace.send(:current_path)) + expect(page.response_headers['X-Sendfile']).to eq(job.trace.send(:current_path)) end end context 'job from other project' do before do Capybara.current_session.driver.headers = { 'X-Sendfile-Type' => 'X-Sendfile' } - build2.run! - visit raw_namespace_project_job_path(project.namespace, project, build2) + job2.run! + visit raw_namespace_project_job_path(project.namespace, project, job2) end it 'sends the right headers' do @@ -458,16 +458,16 @@ feature 'Jobs', :feature do before do Capybara.current_session.driver.headers = { 'X-Sendfile-Type' => 'X-Sendfile' } - build.run! + job.run! end - context 'when build has trace in file', :js do + context 'when job has trace in file', :js do before do allow_any_instance_of(Gitlab::Ci::Trace) .to receive(:paths) .and_return([existing_file]) - visit namespace_project_job_path(namespace, project, build) + visit namespace_project_job_path(namespace, project, job) find('.js-raw-link-controller').click end @@ -485,7 +485,7 @@ feature 'Jobs', :feature do .to receive(:paths) .and_return([]) - visit namespace_project_job_path(namespace, project, build) + visit namespace_project_job_path(namespace, project, job) end it 'sends the right headers' do @@ -496,7 +496,7 @@ feature 'Jobs', :feature do context "when visiting old URL" do let(:raw_job_url) do - raw_namespace_project_job_path(project.namespace, project, build) + raw_namespace_project_job_path(project.namespace, project, job) end before do @@ -512,7 +512,7 @@ feature 'Jobs', :feature do describe "GET /:project/jobs/:id/trace.json" do context "Job from project" do before do - visit trace_namespace_project_job_path(project.namespace, project, build, format: :json) + visit trace_namespace_project_job_path(project.namespace, project, job, format: :json) end it { expect(page.status_code).to eq(200) } @@ -520,7 +520,7 @@ feature 'Jobs', :feature do context "Job from other project" do before do - visit trace_namespace_project_job_path(project.namespace, project, build2, format: :json) + visit trace_namespace_project_job_path(project.namespace, project, job2, format: :json) end it { expect(page.status_code).to eq(404) } @@ -530,7 +530,7 @@ feature 'Jobs', :feature do describe "GET /:project/jobs/:id/status" do context "Job from project" do before do - visit status_namespace_project_job_path(project.namespace, project, build) + visit status_namespace_project_job_path(project.namespace, project, job) end it { expect(page.status_code).to eq(200) } @@ -538,7 +538,7 @@ feature 'Jobs', :feature do context "Job from other project" do before do - visit status_namespace_project_job_path(project.namespace, project, build2) + visit status_namespace_project_job_path(project.namespace, project, job2) end it { expect(page.status_code).to eq(404) } diff --git a/spec/features/projects/pipeline_schedules_spec.rb b/spec/features/projects/pipeline_schedules_spec.rb index 317949d6b56..2d43f7a10bc 100644 --- a/spec/features/projects/pipeline_schedules_spec.rb +++ b/spec/features/projects/pipeline_schedules_spec.rb @@ -127,7 +127,7 @@ feature 'Pipeline Schedules', :feature do end it 'shows the pipeline schedule with default ref' do - page.within('.git-revision-dropdown-toggle') do + page.within('.js-target-branch-dropdown') do expect(first('.dropdown-toggle-text').text).to eq('master') end end diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index 36a3ddca6ef..12c5ad45baf 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -47,7 +47,9 @@ describe 'Pipeline', :feature, :js do let(:project) { create(:project) } let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id, user: user) } - before { visit namespace_project_pipeline_path(project.namespace, project, pipeline) } + before do + visit namespace_project_pipeline_path(project.namespace, project, pipeline) + end it 'shows the pipeline graph' do expect(page).to have_selector('.pipeline-visualization') @@ -164,7 +166,9 @@ describe 'Pipeline', :feature, :js do it { expect(page).not_to have_content('retried') } context 'when retrying' do - before { find('.js-retry-button').trigger('click') } + before do + find('.js-retry-button').trigger('click') + end it { expect(page).not_to have_content('Retry') } end @@ -174,7 +178,9 @@ describe 'Pipeline', :feature, :js do it { expect(page).not_to have_selector('.ci-canceled') } context 'when canceling' do - before { click_on 'Cancel running' } + before do + click_on 'Cancel running' + end it { expect(page).not_to have_content('Cancel running') } end @@ -226,7 +232,9 @@ describe 'Pipeline', :feature, :js do it { expect(page).not_to have_content('retried') } context 'when retrying' do - before { find('.js-retry-button').trigger('click') } + before do + find('.js-retry-button').trigger('click') + end it { expect(page).not_to have_content('Retry') } end @@ -236,7 +244,9 @@ describe 'Pipeline', :feature, :js do it { expect(page).not_to have_selector('.ci-canceled') } context 'when canceling' do - before { click_on 'Cancel running' } + before do + click_on 'Cancel running' + end it { expect(page).not_to have_content('Cancel running') } end diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index 05c2bf350f1..026e2ce5b68 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -149,7 +149,9 @@ describe 'Pipelines', :feature, :js do create(:ci_pipeline, :invalid, project: project) end - before { visit_project_pipelines } + before do + visit_project_pipelines + end it 'contains badge that indicates errors' do expect(page).to have_content 'yaml invalid' @@ -171,7 +173,9 @@ describe 'Pipelines', :feature, :js do commands: 'test') end - before { visit_project_pipelines } + before do + visit_project_pipelines + end it 'has a dropdown with play button' do expect(page).to have_selector('.dropdown-toggle.btn.btn-default .icon-play') @@ -204,7 +208,9 @@ describe 'Pipelines', :feature, :js do stage: 'test') end - before { visit_project_pipelines } + before do + visit_project_pipelines + end it 'is cancelable' do expect(page).to have_selector('.js-pipelines-cancel-button') @@ -215,7 +221,9 @@ describe 'Pipelines', :feature, :js do end context 'when canceling' do - before { find('.js-pipelines-cancel-button').trigger('click') } + before do + find('.js-pipelines-cancel-button').trigger('click') + end it 'indicates that pipeline was canceled' do expect(page).not_to have_selector('.js-pipelines-cancel-button') @@ -255,7 +263,9 @@ describe 'Pipelines', :feature, :js do stage: 'test') end - before { visit_project_pipelines } + before do + visit_project_pipelines + end it 'has artifats' do expect(page).to have_selector('.build-artifacts') @@ -284,7 +294,9 @@ describe 'Pipelines', :feature, :js do stage: 'test') end - before { visit_project_pipelines } + before do + visit_project_pipelines + end it { expect(page).not_to have_selector('.build-artifacts') } end @@ -297,7 +309,9 @@ describe 'Pipelines', :feature, :js do stage: 'test') end - before { visit_project_pipelines } + before do + visit_project_pipelines + end it { expect(page).not_to have_selector('.build-artifacts') } end @@ -310,7 +324,9 @@ describe 'Pipelines', :feature, :js do name: 'build') end - before { visit_project_pipelines } + before do + visit_project_pipelines + end it 'should render a mini pipeline graph' do expect(page).to have_selector('.js-mini-pipeline-graph') @@ -437,7 +453,9 @@ describe 'Pipelines', :feature, :js do end context 'with gitlab-ci.yml' do - before { stub_ci_pipeline_to_return_yaml_file } + before do + stub_ci_pipeline_to_return_yaml_file + end it 'creates a new pipeline' do expect { click_on 'Create pipeline' } @@ -448,7 +466,9 @@ describe 'Pipelines', :feature, :js do end context 'without gitlab-ci.yml' do - before { click_on 'Create pipeline' } + before do + click_on 'Create pipeline' + end it { expect(page).to have_content('Missing .gitlab-ci.yml file') } end diff --git a/spec/features/projects/project_settings_spec.rb b/spec/features/projects/project_settings_spec.rb index 11dcab4d737..2a9b32ea07e 100644 --- a/spec/features/projects/project_settings_spec.rb +++ b/spec/features/projects/project_settings_spec.rb @@ -58,8 +58,13 @@ describe 'Edit Project Settings', feature: true do # Not using empty project because we need a repo to exist let(:project) { create(:project, namespace: user.namespace, name: 'gitlabhq') } - before(:context) { TestEnv.clean_test_path } - after(:example) { TestEnv.clean_test_path } + before(:context) do + TestEnv.clean_test_path + end + + after(:example) do + TestEnv.clean_test_path + end specify 'the project is accessible via the new path' do rename_project(project, path: 'bar') @@ -96,9 +101,17 @@ describe 'Edit Project Settings', feature: true do let!(:project) { create(:project, namespace: user.namespace, name: 'gitlabhq') } let!(:group) { create(:group) } - before(:context) { TestEnv.clean_test_path } - before(:example) { group.add_owner(user) } - after(:example) { TestEnv.clean_test_path } + before(:context) do + TestEnv.clean_test_path + end + + before(:example) do + group.add_owner(user) + end + + after(:example) do + TestEnv.clean_test_path + end specify 'the project is accessible via the new path' do transfer_project(project, group) diff --git a/spec/features/protected_branches_spec.rb b/spec/features/protected_branches_spec.rb index 667895bffa5..aa9164dd979 100644 --- a/spec/features/protected_branches_spec.rb +++ b/spec/features/protected_branches_spec.rb @@ -4,7 +4,9 @@ feature 'Protected Branches', feature: true, js: true do let(:user) { create(:user, :admin) } let(:project) { create(:project, :repository) } - before { login_as(user) } + before do + login_as(user) + end def set_protected_branch_name(branch_name) find(".js-protected-branch-select").trigger('click') diff --git a/spec/features/protected_tags_spec.rb b/spec/features/protected_tags_spec.rb index 66236dbc7fc..63a20585776 100644 --- a/spec/features/protected_tags_spec.rb +++ b/spec/features/protected_tags_spec.rb @@ -4,7 +4,9 @@ feature 'Projected Tags', feature: true, js: true do let(:user) { create(:user, :admin) } let(:project) { create(:project, :repository) } - before { login_as(user) } + before do + login_as(user) + end def set_protected_tag_name(tag_name) find(".js-protected-tag-select").click diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb index 0e1cc9a0f73..e87d52f5c8f 100644 --- a/spec/features/runners_spec.rb +++ b/spec/features/runners_spec.rb @@ -4,7 +4,10 @@ describe "Runners" do include GitlabRoutingHelper let(:user) { create(:user) } - before { login_as(user) } + + before do + login_as(user) + end describe "specific runners" do before do @@ -127,7 +130,9 @@ describe "Runners" do end context 'when runner has tags' do - before { runner.update_attribute(:tag_list, ['tag']) } + before do + runner.update_attribute(:tag_list, ['tag']) + end scenario 'user wants to prevent runner from running untagged job' do visit runners_path(project) diff --git a/spec/features/search_spec.rb b/spec/features/search_spec.rb index 7834807b1f1..89d4f536b20 100644 --- a/spec/features/search_spec.rb +++ b/spec/features/search_spec.rb @@ -83,7 +83,9 @@ describe "Search", feature: true do let(:project) { create(:project, :repository) } let(:note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'Bug here') } - before { note.update_attributes(commit_id: 12345678) } + before do + note.update_attributes(commit_id: 12345678) + end it 'finds comment' do visit namespace_project_path(project.namespace, project) diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb index 2a2655bbdb5..f33406a40a7 100644 --- a/spec/features/security/project/internal_access_spec.rb +++ b/spec/features/security/project/internal_access_spec.rb @@ -337,7 +337,9 @@ describe "Internal Project Access", feature: true do subject { namespace_project_jobs_path(project.namespace, project) } context "when allowed for public and internal" do - before { project.update(public_builds: true) } + before do + project.update(public_builds: true) + end it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:owner).of(project) } @@ -351,7 +353,9 @@ describe "Internal Project Access", feature: true do end context "when disallowed for public and internal" do - before { project.update(public_builds: false) } + before do + project.update(public_builds: false) + end it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:owner).of(project) } @@ -371,7 +375,9 @@ describe "Internal Project Access", feature: true do subject { namespace_project_job_path(project.namespace, project, build.id) } context "when allowed for public and internal" do - before { project.update(public_builds: true) } + before do + project.update(public_builds: true) + end it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:owner).of(project) } @@ -385,7 +391,9 @@ describe "Internal Project Access", feature: true do end context "when disallowed for public and internal" do - before { project.update(public_builds: false) } + before do + project.update(public_builds: false) + end it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:owner).of(project) } diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb index 35d5163941e..16a1331b2f3 100644 --- a/spec/features/security/project/public_access_spec.rb +++ b/spec/features/security/project/public_access_spec.rb @@ -157,7 +157,9 @@ describe "Public Project Access", feature: true do subject { namespace_project_jobs_path(project.namespace, project) } context "when allowed for public" do - before { project.update(public_builds: true) } + before do + project.update(public_builds: true) + end it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:owner).of(project) } @@ -171,7 +173,9 @@ describe "Public Project Access", feature: true do end context "when disallowed for public" do - before { project.update(public_builds: false) } + before do + project.update(public_builds: false) + end it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:owner).of(project) } @@ -191,7 +195,9 @@ describe "Public Project Access", feature: true do subject { namespace_project_job_path(project.namespace, project, build.id) } context "when allowed for public" do - before { project.update(public_builds: true) } + before do + project.update(public_builds: true) + end it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:owner).of(project) } @@ -205,7 +211,9 @@ describe "Public Project Access", feature: true do end context "when disallowed for public" do - before { project.update(public_builds: false) } + before do + project.update(public_builds: false) + end it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:owner).of(project) } diff --git a/spec/features/signup_spec.rb b/spec/features/signup_spec.rb index d7b6dda4946..5d6d1e79af2 100644 --- a/spec/features/signup_spec.rb +++ b/spec/features/signup_spec.rb @@ -3,7 +3,9 @@ require 'spec_helper' feature 'Signup', feature: true do describe 'signup with no errors' do context "when sending confirmation email" do - before { stub_application_setting(send_user_confirmation_email: true) } + before do + stub_application_setting(send_user_confirmation_email: true) + end it 'creates the user account and sends a confirmation email' do user = build(:user) @@ -23,7 +25,9 @@ feature 'Signup', feature: true do end context "when not sending confirmation email" do - before { stub_application_setting(send_user_confirmation_email: false) } + before do + stub_application_setting(send_user_confirmation_email: false) + end it 'creates the user account and goes to dashboard' do user = build(:user) diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb index 563e65d3cc5..51b1b8e2328 100644 --- a/spec/features/task_lists_spec.rb +++ b/spec/features/task_lists_spec.rb @@ -144,7 +144,9 @@ feature 'Task Lists', feature: true do describe 'nested tasks', js: true do let(:issue) { create(:issue, description: nested_tasks_markdown, author: user, project: project) } - before { visit_issue(project, issue) } + before do + visit_issue(project, issue) + end it 'renders' do expect(page).to have_selector('ul.task-list', count: 2) diff --git a/spec/features/todos/todos_sorting_spec.rb b/spec/features/todos/todos_sorting_spec.rb index 4d5bd476301..f012d250887 100644 --- a/spec/features/todos/todos_sorting_spec.rb +++ b/spec/features/todos/todos_sorting_spec.rb @@ -8,7 +8,9 @@ describe "Dashboard > User sorts todos", feature: true do let(:label_2) { create(:label, title: 'label_2', project: project, priority: 2) } let(:label_3) { create(:label, title: 'label_3', project: project, priority: 3) } - before { project.team << [user, :developer] } + before do + project.team << [user, :developer] + end context 'sort options' do let(:issue_1) { create(:issue, title: 'issue_1', project: project) } diff --git a/spec/features/triggers_spec.rb b/spec/features/triggers_spec.rb index c1ae6db00c6..2ea9992173d 100644 --- a/spec/features/triggers_spec.rb +++ b/spec/features/triggers_spec.rb @@ -5,7 +5,10 @@ feature 'Triggers', feature: true, js: true do let(:user) { create(:user) } let(:user2) { create(:user) } let(:guest_user) { create(:user) } - before { login_as(user) } + + before do + login_as(user) + end before do @project = create(:empty_project) diff --git a/spec/features/u2f_spec.rb b/spec/features/u2f_spec.rb index 2fed8067042..dc21637967f 100644 --- a/spec/features/u2f_spec.rb +++ b/spec/features/u2f_spec.rb @@ -1,7 +1,9 @@ require 'spec_helper' feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do - before { allow_any_instance_of(U2fHelper).to receive(:inject_u2f_api?).and_return(true) } + before do + allow_any_instance_of(U2fHelper).to receive(:inject_u2f_api?).and_return(true) + end def manage_two_factor_authentication click_on 'Manage two-factor authentication' @@ -28,7 +30,9 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do end describe 'when 2FA via OTP is disabled' do - before { user.update_attribute(:otp_required_for_login, false) } + before do + user.update_attribute(:otp_required_for_login, false) + end it 'does not allow registering a new device' do visit profile_account_path diff --git a/spec/features/unsubscribe_links_spec.rb b/spec/features/unsubscribe_links_spec.rb index 8509551ce4a..0a8db15c75f 100644 --- a/spec/features/unsubscribe_links_spec.rb +++ b/spec/features/unsubscribe_links_spec.rb @@ -56,7 +56,9 @@ describe 'Unsubscribe links', feature: true do end context 'when logged in' do - before { login_as(recipient) } + before do + login_as(recipient) + end it 'unsubscribes from the issue when visiting the link from the email body' do visit body_link diff --git a/spec/features/users_spec.rb b/spec/features/users_spec.rb index fbe078bd136..c241dae12cf 100644 --- a/spec/features/users_spec.rb +++ b/spec/features/users_spec.rb @@ -45,7 +45,9 @@ feature 'Users', feature: true, js: true do end describe 'redirect alias routes' do - before { user } + before do + expect(user).to be_persisted + end scenario '/u/user1 redirects to user page' do visit '/u/user1' diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb index 96151689359..8f2d60f2f1b 100644 --- a/spec/finders/issues_finder_spec.rb +++ b/spec/finders/issues_finder_spec.rb @@ -148,7 +148,9 @@ describe IssuesFinder do let(:params) { { label_name: [label.title, label2.title].join(',') } } let(:label2) { create(:label, project: project2) } - before { create(:label_link, label: label2, target: issue2) } + before do + create(:label_link, label: label2, target: issue2) + end it 'returns the unique issues with any of those labels' do expect(issues).to contain_exactly(issue2) diff --git a/spec/finders/personal_access_tokens_finder_spec.rb b/spec/finders/personal_access_tokens_finder_spec.rb index fd92664ca24..3f22b3a253d 100644 --- a/spec/finders/personal_access_tokens_finder_spec.rb +++ b/spec/finders/personal_access_tokens_finder_spec.rb @@ -25,49 +25,65 @@ describe PersonalAccessTokensFinder do end describe 'without impersonation' do - before { params[:impersonation] = false } + before do + params[:impersonation] = false + end it { is_expected.to contain_exactly(active_personal_access_token, revoked_personal_access_token, expired_personal_access_token) } describe 'with active state' do - before { params[:state] = 'active' } + before do + params[:state] = 'active' + end it { is_expected.to contain_exactly(active_personal_access_token) } end describe 'with inactive state' do - before { params[:state] = 'inactive' } + before do + params[:state] = 'inactive' + end it { is_expected.to contain_exactly(revoked_personal_access_token, expired_personal_access_token) } end end describe 'with impersonation' do - before { params[:impersonation] = true } + before do + params[:impersonation] = true + end it { is_expected.to contain_exactly(active_impersonation_token, revoked_impersonation_token, expired_impersonation_token) } describe 'with active state' do - before { params[:state] = 'active' } + before do + params[:state] = 'active' + end it { is_expected.to contain_exactly(active_impersonation_token) } end describe 'with inactive state' do - before { params[:state] = 'inactive' } + before do + params[:state] = 'inactive' + end it { is_expected.to contain_exactly(revoked_impersonation_token, expired_impersonation_token) } end end describe 'with active state' do - before { params[:state] = 'active' } + before do + params[:state] = 'active' + end it { is_expected.to contain_exactly(active_personal_access_token, active_impersonation_token) } end describe 'with inactive state' do - before { params[:state] = 'inactive' } + before do + params[:state] = 'inactive' + end it do is_expected.to contain_exactly(expired_personal_access_token, revoked_personal_access_token, @@ -81,7 +97,9 @@ describe PersonalAccessTokensFinder do it { is_expected.to eq(active_personal_access_token) } describe 'with impersonation' do - before { params[:impersonation] = true } + before do + params[:impersonation] = true + end it { is_expected.to be_nil } end @@ -93,7 +111,9 @@ describe PersonalAccessTokensFinder do it { is_expected.to eq(active_personal_access_token) } describe 'with impersonation' do - before { params[:impersonation] = true } + before do + params[:impersonation] = true + end it { is_expected.to be_nil } end @@ -109,7 +129,9 @@ describe PersonalAccessTokensFinder do let!(:other_user_expired_impersonation_token) { create(:personal_access_token, :expired, :impersonation, user: user2) } let!(:other_user_revoked_impersonation_token) { create(:personal_access_token, :revoked, :impersonation, user: user2) } - before { params[:user] = user } + before do + params[:user] = user + end it do is_expected.to contain_exactly(active_personal_access_token, active_impersonation_token, @@ -118,49 +140,65 @@ describe PersonalAccessTokensFinder do end describe 'without impersonation' do - before { params[:impersonation] = false } + before do + params[:impersonation] = false + end it { is_expected.to contain_exactly(active_personal_access_token, revoked_personal_access_token, expired_personal_access_token) } describe 'with active state' do - before { params[:state] = 'active' } + before do + params[:state] = 'active' + end it { is_expected.to contain_exactly(active_personal_access_token) } end describe 'with inactive state' do - before { params[:state] = 'inactive' } + before do + params[:state] = 'inactive' + end it { is_expected.to contain_exactly(revoked_personal_access_token, expired_personal_access_token) } end end describe 'with impersonation' do - before { params[:impersonation] = true } + before do + params[:impersonation] = true + end it { is_expected.to contain_exactly(active_impersonation_token, revoked_impersonation_token, expired_impersonation_token) } describe 'with active state' do - before { params[:state] = 'active' } + before do + params[:state] = 'active' + end it { is_expected.to contain_exactly(active_impersonation_token) } end describe 'with inactive state' do - before { params[:state] = 'inactive' } + before do + params[:state] = 'inactive' + end it { is_expected.to contain_exactly(revoked_impersonation_token, expired_impersonation_token) } end end describe 'with active state' do - before { params[:state] = 'active' } + before do + params[:state] = 'active' + end it { is_expected.to contain_exactly(active_personal_access_token, active_impersonation_token) } end describe 'with inactive state' do - before { params[:state] = 'inactive' } + before do + params[:state] = 'inactive' + end it do is_expected.to contain_exactly(expired_personal_access_token, revoked_personal_access_token, @@ -174,7 +212,9 @@ describe PersonalAccessTokensFinder do it { is_expected.to eq(active_personal_access_token) } describe 'with impersonation' do - before { params[:impersonation] = true } + before do + params[:impersonation] = true + end it { is_expected.to be_nil } end @@ -186,7 +226,9 @@ describe PersonalAccessTokensFinder do it { is_expected.to eq(active_personal_access_token) } describe 'with impersonation' do - before { params[:impersonation] = true } + before do + params[:impersonation] = true + end it { is_expected.to be_nil } end diff --git a/spec/finders/personal_projects_finder_spec.rb b/spec/finders/personal_projects_finder_spec.rb index e0e17af681a..304b0fb67fb 100644 --- a/spec/finders/personal_projects_finder_spec.rb +++ b/spec/finders/personal_projects_finder_spec.rb @@ -32,7 +32,9 @@ describe PersonalProjectsFinder do end context 'external' do - before { current_user.update_attributes(external: true) } + before do + current_user.update_attributes(external: true) + end it { is_expected.to eq([private_project, public_project]) } end diff --git a/spec/finders/todos_finder_spec.rb b/spec/finders/todos_finder_spec.rb index f7e7e733cf7..8be447418b0 100644 --- a/spec/finders/todos_finder_spec.rb +++ b/spec/finders/todos_finder_spec.rb @@ -6,7 +6,9 @@ describe TodosFinder do let(:project) { create(:empty_project) } let(:finder) { described_class } - before { project.team << [user, :developer] } + before do + project.team << [user, :developer] + end describe '#sort' do context 'by date' do diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 49df91b236f..cc7f889b927 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -257,4 +257,24 @@ describe ApplicationHelper do it { expect(helper.active_when(true)).to eq('active') } it { expect(helper.active_when(false)).to eq(nil) } end + + describe '#support_url' do + context 'when alternate support url is specified' do + let(:alternate_url) { 'http://company.example.com/getting-help' } + + before do + allow(current_application_settings).to receive(:help_page_support_url) { alternate_url } + end + + it 'returns the alternate support url' do + expect(helper.support_url).to eq(alternate_url) + end + end + + context 'when alternate support url is not specified' do + it 'builds the support url from the promo_url' do + expect(helper.support_url).to eq(helper.promo_url + '/getting-help/') + end + end + end end diff --git a/spec/helpers/diff_helper_spec.rb b/spec/helpers/diff_helper_spec.rb index a74615e07f9..0ac030d3171 100644 --- a/spec/helpers/diff_helper_spec.rb +++ b/spec/helpers/diff_helper_spec.rb @@ -8,7 +8,7 @@ describe DiffHelper do let(:commit) { project.commit(sample_commit.id) } let(:diffs) { commit.raw_diffs } let(:diff) { diffs.first } - let(:diff_refs) { [commit.parent, commit] } + let(:diff_refs) { commit.diff_refs } let(:diff_file) { Gitlab::Diff::File.new(diff, diff_refs: diff_refs, repository: repository) } describe 'diff_view' do @@ -207,4 +207,41 @@ describe DiffHelper do expect(output).not_to have_css 'td:nth-child(3)' end end + + context 'viewer related' do + let(:viewer) { diff_file.simple_viewer } + + before do + assign(:project, project) + end + + describe '#diff_render_error_reason' do + context 'for error :too_large' do + before do + expect(viewer).to receive(:render_error).and_return(:too_large) + end + + it 'returns an error message' do + expect(helper.diff_render_error_reason(viewer)).to eq('it is too large') + end + end + + context 'for error :server_side_but_stored_externally' do + before do + expect(viewer).to receive(:render_error).and_return(:server_side_but_stored_externally) + expect(diff_file).to receive(:external_storage).and_return(:lfs) + end + + it 'returns an error message' do + expect(helper.diff_render_error_reason(viewer)).to eq('it is stored in LFS') + end + end + end + + describe '#diff_render_error_options' do + it 'includes a "view the blob" link' do + expect(helper.diff_render_error_options(viewer)).to include(/view the blob/) + end + end + end end diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index a695621b87a..9a4086725d2 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -250,7 +250,9 @@ describe ProjectsHelper do end context "when project is private" do - before { project.update_attributes(visibility_level: Gitlab::VisibilityLevel::PRIVATE) } + before do + project.update_attributes(visibility_level: Gitlab::VisibilityLevel::PRIVATE) + end it "shows only allowed options" do helper.instance_variable_set(:@project, project) @@ -300,4 +302,37 @@ describe ProjectsHelper do expect(helper.send(:visibility_select_options, project, Gitlab::VisibilityLevel::PRIVATE)).to include('Private') end end + + describe '#get_project_nav_tabs' do + let(:project) { create(:empty_project) } + let(:user) { create(:user) } + + before do + allow(helper).to receive(:can?) { true } + end + + subject do + helper.send(:get_project_nav_tabs, project, user) + end + + context 'when builds feature is enabled' do + before do + allow(project).to receive(:builds_enabled?).and_return(true) + end + + it "does include pipelines tab" do + is_expected.to include(:pipelines) + end + end + + context 'when builds feature is disabled' do + before do + allow(project).to receive(:builds_enabled?).and_return(false) + end + + it "do not include pipelines tab" do + is_expected.not_to include(:pipelines) + end + end + end end diff --git a/spec/initializers/8_metrics_spec.rb b/spec/initializers/8_metrics_spec.rb index 570754621f3..a507d7f7f2b 100644 --- a/spec/initializers/8_metrics_spec.rb +++ b/spec/initializers/8_metrics_spec.rb @@ -7,6 +7,7 @@ describe 'instrument_classes', lib: true do before do allow(config).to receive(:instrument_method) allow(config).to receive(:instrument_methods) + allow(config).to receive(:instrument_instance_method) allow(config).to receive(:instrument_instance_methods) end diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js index bfd8b8648a6..c6f218e4dac 100644 --- a/spec/javascripts/notes_spec.js +++ b/spec/javascripts/notes_spec.js @@ -126,6 +126,7 @@ import '~/notes'; const deferred = $.Deferred(); spyOn($, 'ajax').and.returnValue(deferred.promise()); spyOn(this.notes, 'revertNoteEditForm'); + spyOn(this.notes, 'setupNewNote'); $('.js-comment-button').click(); deferred.resolve(noteEntity); @@ -136,6 +137,46 @@ import '~/notes'; this.notes.updateNote(updatedNote, $targetNote); expect(this.notes.revertNoteEditForm).toHaveBeenCalledWith($targetNote); + expect(this.notes.setupNewNote).toHaveBeenCalled(); + }); + }); + + describe('updateNoteTargetSelector', () => { + const hash = 'note_foo'; + let $note; + + beforeEach(() => { + $note = $(`<div id="${hash}"></div>`); + spyOn($note, 'filter').and.callThrough(); + spyOn($note, 'toggleClass').and.callThrough(); + }); + + it('sets target when hash matches', () => { + spyOn(gl.utils, 'getLocationHash'); + gl.utils.getLocationHash.and.returnValue(hash); + + Notes.updateNoteTargetSelector($note); + + expect($note.filter).toHaveBeenCalledWith(`#${hash}`); + expect($note.toggleClass).toHaveBeenCalledWith('target', true); + }); + + it('unsets target when hash does not match', () => { + spyOn(gl.utils, 'getLocationHash'); + gl.utils.getLocationHash.and.returnValue('note_doesnotexist'); + + Notes.updateNoteTargetSelector($note); + + expect($note.toggleClass).toHaveBeenCalledWith('target', false); + }); + + it('unsets target when there is not a hash fragment anymore', () => { + spyOn(gl.utils, 'getLocationHash'); + gl.utils.getLocationHash.and.returnValue(null); + + Notes.updateNoteTargetSelector($note); + + expect($note.toggleClass).toHaveBeenCalledWith('target', null); }); }); @@ -189,9 +230,13 @@ import '~/notes'; Notes.isUpdatedNote.and.returnValue(true); const $note = $('<div>'); $notesList.find.and.returnValue($note); + const $newNote = $(note.html); + Notes.animateUpdateNote.and.returnValue($newNote); + Notes.prototype.renderNote.call(notes, note, null, $notesList); expect(Notes.animateUpdateNote).toHaveBeenCalledWith(note.html, $note); + expect(notes.setupNewNote).toHaveBeenCalledWith($newNote); }); describe('while editing', () => { @@ -378,6 +423,23 @@ import '~/notes'; }); }); + describe('putEditFormInPlace', () => { + it('should call gl.GLForm with GFM parameter passed through', () => { + spyOn(gl, 'GLForm'); + + const $el = jasmine.createSpyObj('$form', ['find', 'closest']); + $el.find.and.returnValue($('<div>')); + $el.closest.and.returnValue($('<div>')); + + Notes.prototype.putEditFormInPlace.call({ + getEditFormSelector: () => '', + enableGFM: true + }, $el); + + expect(gl.GLForm).toHaveBeenCalledWith(jasmine.any(Object), true); + }); + }); + describe('postComment & updateComment', () => { const sampleComment = 'foo'; const updatedComment = 'bar'; diff --git a/spec/javascripts/pipelines/pipelines_actions_spec.js b/spec/javascripts/pipelines/pipelines_actions_spec.js index c89dacbcd93..8a58b77f1e3 100644 --- a/spec/javascripts/pipelines/pipelines_actions_spec.js +++ b/spec/javascripts/pipelines/pipelines_actions_spec.js @@ -1,5 +1,5 @@ import Vue from 'vue'; -import pipelinesActionsComp from '~/pipelines/components/pipelines_actions'; +import pipelinesActionsComp from '~/pipelines/components/pipelines_actions.vue'; describe('Pipelines Actions dropdown', () => { let component; diff --git a/spec/javascripts/pipelines/pipelines_artifacts_spec.js b/spec/javascripts/pipelines/pipelines_artifacts_spec.js index 9724b63d957..acb67d0ec21 100644 --- a/spec/javascripts/pipelines/pipelines_artifacts_spec.js +++ b/spec/javascripts/pipelines/pipelines_artifacts_spec.js @@ -1,5 +1,5 @@ import Vue from 'vue'; -import artifactsComp from '~/pipelines/components/pipelines_artifacts'; +import artifactsComp from '~/pipelines/components/pipelines_artifacts.vue'; describe('Pipelines Artifacts dropdown', () => { let component; diff --git a/spec/javascripts/pipelines/pipelines_spec.js b/spec/javascripts/pipelines/pipelines_spec.js index 3a56156358b..c30abb2edb0 100644 --- a/spec/javascripts/pipelines/pipelines_spec.js +++ b/spec/javascripts/pipelines/pipelines_spec.js @@ -1,5 +1,5 @@ import Vue from 'vue'; -import pipelinesComp from '~/pipelines/pipelines'; +import pipelinesComp from '~/pipelines/components/pipelines.vue'; import Store from '~/pipelines/stores/pipelines_store'; describe('Pipelines', () => { diff --git a/spec/javascripts/pipelines/time_ago_spec.js b/spec/javascripts/pipelines/time_ago_spec.js index 24581e8c672..42b34c82f89 100644 --- a/spec/javascripts/pipelines/time_ago_spec.js +++ b/spec/javascripts/pipelines/time_ago_spec.js @@ -1,5 +1,5 @@ import Vue from 'vue'; -import timeAgo from '~/pipelines/components/time_ago'; +import timeAgo from '~/pipelines/components/time_ago.vue'; describe('Timeago component', () => { let TimeAgo; diff --git a/spec/javascripts/vue_shared/components/commit_spec.js b/spec/javascripts/vue_shared/components/commit_spec.js index 540245fe71e..1c3188cdda2 100644 --- a/spec/javascripts/vue_shared/components/commit_spec.js +++ b/spec/javascripts/vue_shared/components/commit_spec.js @@ -1,5 +1,5 @@ import Vue from 'vue'; -import commitComp from '~/vue_shared/components/commit'; +import commitComp from '~/vue_shared/components/commit.vue'; describe('Commit component', () => { let props; diff --git a/spec/javascripts/vue_shared/components/pipelines_table_row_spec.js b/spec/javascripts/vue_shared/components/pipelines_table_row_spec.js index 67419cfcbea..346fd0ae010 100644 --- a/spec/javascripts/vue_shared/components/pipelines_table_row_spec.js +++ b/spec/javascripts/vue_shared/components/pipelines_table_row_spec.js @@ -1,5 +1,5 @@ import Vue from 'vue'; -import tableRowComp from '~/vue_shared/components/pipelines_table_row'; +import tableRowComp from '~/vue_shared/components/pipelines_table_row.vue'; describe('Pipelines Table Row', () => { const jsonFixtureName = 'pipelines/pipelines.json'; diff --git a/spec/javascripts/vue_shared/components/pipelines_table_spec.js b/spec/javascripts/vue_shared/components/pipelines_table_spec.js index 6cc178b8f1d..c362cfb7a96 100644 --- a/spec/javascripts/vue_shared/components/pipelines_table_spec.js +++ b/spec/javascripts/vue_shared/components/pipelines_table_spec.js @@ -1,5 +1,5 @@ import Vue from 'vue'; -import pipelinesTableComp from '~/vue_shared/components/pipelines_table'; +import pipelinesTableComp from '~/vue_shared/components/pipelines_table.vue'; import '~/lib/utils/datetime_utility'; describe('Pipelines Table', () => { diff --git a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb index fbf7a461fa5..76cefe112fb 100644 --- a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb @@ -82,7 +82,9 @@ describe Banzai::Filter::ExternalIssueReferenceFilter, lib: true do context 'with RequestStore enabled' do let(:reference_filter) { HTML::Pipeline.new([described_class]) } - before { allow(RequestStore).to receive(:active?).and_return(true) } + before do + allow(RequestStore).to receive(:active?).and_return(true) + end it 'queries the collection on the first call' do expect_any_instance_of(Project).to receive(:default_issues_tracker?).once.and_call_original diff --git a/spec/lib/banzai/filter/redactor_filter_spec.rb b/spec/lib/banzai/filter/redactor_filter_spec.rb index 7c4a0f32c7b..97504aebed5 100644 --- a/spec/lib/banzai/filter/redactor_filter_spec.rb +++ b/spec/lib/banzai/filter/redactor_filter_spec.rb @@ -39,7 +39,9 @@ describe Banzai::Filter::RedactorFilter, lib: true do end context 'valid projects' do - before { allow_any_instance_of(Banzai::ReferenceParser::BaseParser).to receive(:can_read_reference?).and_return(true) } + before do + allow_any_instance_of(Banzai::ReferenceParser::BaseParser).to receive(:can_read_reference?).and_return(true) + end it 'allows permitted Project references' do user = create(:user) @@ -54,7 +56,9 @@ describe Banzai::Filter::RedactorFilter, lib: true do end context 'invalid projects' do - before { allow_any_instance_of(Banzai::ReferenceParser::BaseParser).to receive(:can_read_reference?).and_return(false) } + before do + allow_any_instance_of(Banzai::ReferenceParser::BaseParser).to receive(:can_read_reference?).and_return(false) + end it 'removes unpermitted references' do user = create(:user) diff --git a/spec/lib/banzai/reference_parser/base_parser_spec.rb b/spec/lib/banzai/reference_parser/base_parser_spec.rb index f4f42bfc3ed..76fab93821a 100644 --- a/spec/lib/banzai/reference_parser/base_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/base_parser_spec.rb @@ -114,7 +114,7 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do expect(hash).to eq({ link => user }) end - it 'returns an empty Hash when entry does not exist in the database' do + it 'returns an empty Hash when entry does not exist in the database', :request_store do link = double(:link) expect(link).to receive(:has_attribute?). diff --git a/spec/lib/banzai/reference_parser/commit_parser_spec.rb b/spec/lib/banzai/reference_parser/commit_parser_spec.rb index 412ffa77c36..583ce63a8ab 100644 --- a/spec/lib/banzai/reference_parser/commit_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/commit_parser_spec.rb @@ -10,7 +10,9 @@ describe Banzai::ReferenceParser::CommitParser, lib: true do describe '#nodes_visible_to_user' do context 'when the link has a data-issue attribute' do - before { link['data-commit'] = 123 } + before do + link['data-commit'] = 123 + end it_behaves_like "referenced feature visibility", "repository" end diff --git a/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb b/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb index 96e55b0997a..8c0f5d7df97 100644 --- a/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb @@ -10,7 +10,9 @@ describe Banzai::ReferenceParser::CommitRangeParser, lib: true do describe '#nodes_visible_to_user' do context 'when the link has a data-issue attribute' do - before { link['data-commit-range'] = '123..456' } + before do + link['data-commit-range'] = '123..456' + end it_behaves_like "referenced feature visibility", "repository" end diff --git a/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb b/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb index 0af36776a54..d212bbac619 100644 --- a/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb @@ -10,7 +10,9 @@ describe Banzai::ReferenceParser::ExternalIssueParser, lib: true do describe '#nodes_visible_to_user' do context 'when the link has a data-issue attribute' do - before { link['data-external-issue'] = 123 } + before do + link['data-external-issue'] = 123 + end levels = [ProjectFeature::DISABLED, ProjectFeature::PRIVATE, ProjectFeature::ENABLED] diff --git a/spec/lib/banzai/reference_parser/label_parser_spec.rb b/spec/lib/banzai/reference_parser/label_parser_spec.rb index 8c540d35ddd..ddd699f3c25 100644 --- a/spec/lib/banzai/reference_parser/label_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/label_parser_spec.rb @@ -11,7 +11,9 @@ describe Banzai::ReferenceParser::LabelParser, lib: true do describe '#nodes_visible_to_user' do context 'when the link has a data-issue attribute' do - before { link['data-label'] = label.id.to_s } + before do + link['data-label'] = label.id.to_s + end it_behaves_like "referenced feature visibility", "issues", "merge_requests" end diff --git a/spec/lib/banzai/reference_parser/milestone_parser_spec.rb b/spec/lib/banzai/reference_parser/milestone_parser_spec.rb index 2d4d589ae34..72d4f3bc18e 100644 --- a/spec/lib/banzai/reference_parser/milestone_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/milestone_parser_spec.rb @@ -11,7 +11,9 @@ describe Banzai::ReferenceParser::MilestoneParser, lib: true do describe '#nodes_visible_to_user' do context 'when the link has a data-issue attribute' do - before { link['data-milestone'] = milestone.id.to_s } + before do + link['data-milestone'] = milestone.id.to_s + end it_behaves_like "referenced feature visibility", "issues", "merge_requests" end diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index 2ca0773ad1d..af0e7855a9b 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -596,62 +596,117 @@ module Ci end describe "Image and service handling" do - it "returns image and service when defined" do - config = YAML.dump({ - image: "ruby:2.1", - services: ["mysql"], - before_script: ["pwd"], - rspec: { script: "rspec" } - }) + context "when extended docker configuration is used" do + it "returns image and service when defined" do + config = YAML.dump({ image: { name: "ruby:2.1" }, + services: ["mysql", { name: "docker:dind", alias: "docker" }], + before_script: ["pwd"], + rspec: { script: "rspec" } }) - config_processor = GitlabCiYamlProcessor.new(config, path) + config_processor = GitlabCiYamlProcessor.new(config, path) - expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1) - expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({ - stage: "test", - stage_idx: 1, - name: "rspec", - commands: "pwd\nrspec", - coverage_regex: nil, - tag_list: [], - options: { - image: "ruby:2.1", - services: ["mysql"] - }, - allow_failure: false, - when: "on_success", - environment: nil, - yaml_variables: [] - }) + expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1) + expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({ + stage: "test", + stage_idx: 1, + name: "rspec", + commands: "pwd\nrspec", + coverage_regex: nil, + tag_list: [], + options: { + image: { name: "ruby:2.1" }, + services: [{ name: "mysql" }, { name: "docker:dind", alias: "docker" }] + }, + allow_failure: false, + when: "on_success", + environment: nil, + yaml_variables: [] + }) + end + + it "returns image and service when overridden for job" do + config = YAML.dump({ image: "ruby:2.1", + services: ["mysql"], + before_script: ["pwd"], + rspec: { image: { name: "ruby:2.5" }, + services: [{ name: "postgresql", alias: "db-pg" }, "docker:dind"], script: "rspec" } }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1) + expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({ + stage: "test", + stage_idx: 1, + name: "rspec", + commands: "pwd\nrspec", + coverage_regex: nil, + tag_list: [], + options: { + image: { name: "ruby:2.5" }, + services: [{ name: "postgresql", alias: "db-pg" }, { name: "docker:dind" }] + }, + allow_failure: false, + when: "on_success", + environment: nil, + yaml_variables: [] + }) + end end - it "returns image and service when overridden for job" do - config = YAML.dump({ - image: "ruby:2.1", - services: ["mysql"], - before_script: ["pwd"], - rspec: { image: "ruby:2.5", services: ["postgresql"], script: "rspec" } - }) + context "when etended docker configuration is not used" do + it "returns image and service when defined" do + config = YAML.dump({ image: "ruby:2.1", + services: ["mysql", "docker:dind"], + before_script: ["pwd"], + rspec: { script: "rspec" } }) - config_processor = GitlabCiYamlProcessor.new(config, path) + config_processor = GitlabCiYamlProcessor.new(config, path) - expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1) - expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({ - stage: "test", - stage_idx: 1, - name: "rspec", - commands: "pwd\nrspec", - coverage_regex: nil, - tag_list: [], - options: { - image: "ruby:2.5", - services: ["postgresql"] - }, - allow_failure: false, - when: "on_success", - environment: nil, - yaml_variables: [] - }) + expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1) + expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({ + stage: "test", + stage_idx: 1, + name: "rspec", + commands: "pwd\nrspec", + coverage_regex: nil, + tag_list: [], + options: { + image: { name: "ruby:2.1" }, + services: [{ name: "mysql" }, { name: "docker:dind" }] + }, + allow_failure: false, + when: "on_success", + environment: nil, + yaml_variables: [] + }) + end + + it "returns image and service when overridden for job" do + config = YAML.dump({ image: "ruby:2.1", + services: ["mysql"], + before_script: ["pwd"], + rspec: { image: "ruby:2.5", services: ["postgresql", "docker:dind"], script: "rspec" } }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1) + expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({ + stage: "test", + stage_idx: 1, + name: "rspec", + commands: "pwd\nrspec", + coverage_regex: nil, + tag_list: [], + options: { + image: { name: "ruby:2.5" }, + services: [{ name: "postgresql" }, { name: "docker:dind" }] + }, + allow_failure: false, + when: "on_success", + environment: nil, + yaml_variables: [] + }) + end end end @@ -884,8 +939,8 @@ module Ci coverage_regex: nil, tag_list: [], options: { - image: "ruby:2.1", - services: ["mysql"], + image: { name: "ruby:2.1" }, + services: [{ name: "mysql" }], artifacts: { name: "custom_name", paths: ["logs/", "binaries/"], @@ -1261,7 +1316,7 @@ EOT config = YAML.dump({ image: ["test"], rspec: { script: "test" } }) expect do GitlabCiYamlProcessor.new(config, path) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "image config should be a string") + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "image config should be a hash or a string") end it "returns errors if job name is blank" do @@ -1282,35 +1337,35 @@ EOT config = YAML.dump({ rspec: { script: "test", image: ["test"] } }) expect do GitlabCiYamlProcessor.new(config, path) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:image config should be a string") + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:image config should be a hash or a string") end it "returns errors if services parameter is not an array" do config = YAML.dump({ services: "test", rspec: { script: "test" } }) expect do GitlabCiYamlProcessor.new(config, path) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services config should be an array of strings") + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services config should be a array") end it "returns errors if services parameter is not an array of strings" do config = YAML.dump({ services: [10, "test"], rspec: { script: "test" } }) expect do GitlabCiYamlProcessor.new(config, path) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services config should be an array of strings") + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "service config should be a hash or a string") end it "returns errors if job services parameter is not an array" do config = YAML.dump({ rspec: { script: "test", services: "test" } }) expect do GitlabCiYamlProcessor.new(config, path) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:services config should be an array of strings") + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:services config should be a array") end it "returns errors if job services parameter is not an array of strings" do config = YAML.dump({ rspec: { script: "test", services: [10, "test"] } }) expect do GitlabCiYamlProcessor.new(config, path) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:services config should be an array of strings") + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "service config should be a hash or a string") end it "returns error if job configuration is invalid" do @@ -1324,7 +1379,7 @@ EOT config = YAML.dump({ extra: { script: 'rspec', services: "test" } }) expect do GitlabCiYamlProcessor.new(config, path) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:extra:services config should be an array of strings") + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:extra:services config should be a array") end it "returns errors if there are no jobs defined" do diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb index 33ab005667a..2b26a318583 100644 --- a/spec/lib/extracts_path_spec.rb +++ b/spec/lib/extracts_path_spec.rb @@ -77,7 +77,10 @@ describe ExtractsPath, lib: true do context 'without a path' do let(:params) { { ref: 'v1.0.0.atom' } } - before { assign_ref_vars } + + before do + assign_ref_vars + end it 'sets the un-suffixed version as @ref' do expect(@ref).to eq('v1.0.0') diff --git a/spec/lib/gitlab/auth/unique_ips_limiter_spec.rb b/spec/lib/gitlab/auth/unique_ips_limiter_spec.rb index 94dcddcc30c..fc72df575be 100644 --- a/spec/lib/gitlab/auth/unique_ips_limiter_spec.rb +++ b/spec/lib/gitlab/auth/unique_ips_limiter_spec.rb @@ -40,7 +40,9 @@ describe Gitlab::Auth::UniqueIpsLimiter, :redis, lib: true do end context 'allow 2 unique ips' do - before { current_application_settings.update!(unique_ips_limit_per_user: 2) } + before do + current_application_settings.update!(unique_ips_limit_per_user: 2) + end it 'blocks user trying to login from third ip' do change_ip('ip1') diff --git a/spec/lib/gitlab/badge/build/status_spec.rb b/spec/lib/gitlab/badge/build/status_spec.rb index 3c5414701a7..6abf4ca46a9 100644 --- a/spec/lib/gitlab/badge/build/status_spec.rb +++ b/spec/lib/gitlab/badge/build/status_spec.rb @@ -29,7 +29,9 @@ describe Gitlab::Badge::Build::Status do let!(:build) { create_build(project, sha, branch) } context 'build success' do - before { build.success! } + before do + build.success! + end describe '#status' do it 'is successful' do @@ -39,7 +41,9 @@ describe Gitlab::Badge::Build::Status do end context 'build failed' do - before { build.drop! } + before do + build.drop! + end describe '#status' do it 'failed' do diff --git a/spec/lib/gitlab/chat_commands/presenters/issue_search_spec.rb b/spec/lib/gitlab/chat_commands/presenters/issue_search_spec.rb index ec6d3e34a96..3799a324db4 100644 --- a/spec/lib/gitlab/chat_commands/presenters/issue_search_spec.rb +++ b/spec/lib/gitlab/chat_commands/presenters/issue_search_spec.rb @@ -4,7 +4,9 @@ describe Gitlab::ChatCommands::Presenters::IssueSearch do let(:project) { create(:empty_project) } let(:message) { subject[:text] } - before { create_list(:issue, 2, project: project) } + before do + create_list(:issue, 2, project: project) + end subject { described_class.new(project.issues).present } diff --git a/spec/lib/gitlab/checks/change_access_spec.rb b/spec/lib/gitlab/checks/change_access_spec.rb index c0c309d8179..643e590438a 100644 --- a/spec/lib/gitlab/checks/change_access_spec.rb +++ b/spec/lib/gitlab/checks/change_access_spec.rb @@ -20,7 +20,9 @@ describe Gitlab::Checks::ChangeAccess, lib: true do ).exec end - before { project.add_developer(user) } + before do + project.add_developer(user) + end context 'without failed checks' do it "doesn't raise an error" do @@ -50,7 +52,9 @@ describe Gitlab::Checks::ChangeAccess, lib: true do let!(:protected_tag) { create(:protected_tag, project: project, name: 'v*') } context 'as master' do - before { project.add_master(user) } + before do + project.add_master(user) + end context 'deletion' do let(:oldrev) { 'be93687618e4b132087f430a4d8fc3a609c9b77c' } diff --git a/spec/lib/gitlab/ci/build/image_spec.rb b/spec/lib/gitlab/ci/build/image_spec.rb index 382385dfd6b..773a52cdfbc 100644 --- a/spec/lib/gitlab/ci/build/image_spec.rb +++ b/spec/lib/gitlab/ci/build/image_spec.rb @@ -10,12 +10,28 @@ describe Gitlab::Ci::Build::Image do let(:image_name) { 'ruby:2.1' } let(:job) { create(:ci_build, options: { image: image_name } ) } - it 'fabricates an object of the proper class' do - is_expected.to be_kind_of(described_class) + context 'when image is defined as string' do + it 'fabricates an object of the proper class' do + is_expected.to be_kind_of(described_class) + end + + it 'populates fabricated object with the proper name attribute' do + expect(subject.name).to eq(image_name) + end end - it 'populates fabricated object with the proper name attribute' do - expect(subject.name).to eq(image_name) + context 'when image is defined as hash' do + let(:entrypoint) { '/bin/sh' } + let(:job) { create(:ci_build, options: { image: { name: image_name, entrypoint: entrypoint } } ) } + + it 'fabricates an object of the proper class' do + is_expected.to be_kind_of(described_class) + end + + it 'populates fabricated object with the proper attributes' do + expect(subject.name).to eq(image_name) + expect(subject.entrypoint).to eq(entrypoint) + end end context 'when image name is empty' do @@ -41,10 +57,39 @@ describe Gitlab::Ci::Build::Image do let(:service_image_name) { 'postgres' } let(:job) { create(:ci_build, options: { services: [service_image_name] }) } - it 'fabricates an non-empty array of objects' do - is_expected.to be_kind_of(Array) - is_expected.not_to be_empty - expect(subject.first.name).to eq(service_image_name) + context 'when service is defined as string' do + it 'fabricates an non-empty array of objects' do + is_expected.to be_kind_of(Array) + is_expected.not_to be_empty + end + + it 'populates fabricated objects with the proper name attributes' do + expect(subject.first).to be_kind_of(described_class) + expect(subject.first.name).to eq(service_image_name) + end + end + + context 'when service is defined as hash' do + let(:service_entrypoint) { '/bin/sh' } + let(:service_alias) { 'db' } + let(:service_command) { 'sleep 30' } + let(:job) do + create(:ci_build, options: { services: [{ name: service_image_name, entrypoint: service_entrypoint, + alias: service_alias, command: service_command }] }) + end + + it 'fabricates an non-empty array of objects' do + is_expected.to be_kind_of(Array) + is_expected.not_to be_empty + expect(subject.first).to be_kind_of(described_class) + end + + it 'populates fabricated objects with the proper attributes' do + expect(subject.first.name).to eq(service_image_name) + expect(subject.first.entrypoint).to eq(service_entrypoint) + expect(subject.first.alias).to eq(service_alias) + expect(subject.first.command).to eq(service_command) + end end context 'when service image name is empty' do diff --git a/spec/lib/gitlab/ci/config/entry/cache_spec.rb b/spec/lib/gitlab/ci/config/entry/cache_spec.rb index 2ed120f356a..878b1d6b862 100644 --- a/spec/lib/gitlab/ci/config/entry/cache_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/cache_spec.rb @@ -4,7 +4,9 @@ describe Gitlab::Ci::Config::Entry::Cache do let(:entry) { described_class.new(config) } describe 'validations' do - before { entry.compose! } + before do + entry.compose! + end context 'when entry config value is correct' do let(:config) do diff --git a/spec/lib/gitlab/ci/config/entry/environment_spec.rb b/spec/lib/gitlab/ci/config/entry/environment_spec.rb index c330e609337..3c0007f4d57 100644 --- a/spec/lib/gitlab/ci/config/entry/environment_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/environment_spec.rb @@ -3,7 +3,9 @@ require 'spec_helper' describe Gitlab::Ci::Config::Entry::Environment do let(:entry) { described_class.new(config) } - before { entry.compose! } + before do + entry.compose! + end context 'when configuration is a string' do let(:config) { 'production' } diff --git a/spec/lib/gitlab/ci/config/entry/global_spec.rb b/spec/lib/gitlab/ci/config/entry/global_spec.rb index 23270ad5053..293f112b2b0 100644 --- a/spec/lib/gitlab/ci/config/entry/global_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/global_spec.rb @@ -33,7 +33,9 @@ describe Gitlab::Ci::Config::Entry::Global do end describe '#compose!' do - before { global.compose! } + before do + global.compose! + end it 'creates nodes hash' do expect(global.descendants).to be_an Array @@ -79,7 +81,9 @@ describe Gitlab::Ci::Config::Entry::Global do end context 'when composed' do - before { global.compose! } + before do + global.compose! + end describe '#errors' do it 'has no errors' do @@ -95,13 +99,13 @@ describe Gitlab::Ci::Config::Entry::Global do describe '#image_value' do it 'returns valid image' do - expect(global.image_value).to eq 'ruby:2.2' + expect(global.image_value).to eq(name: 'ruby:2.2') end end describe '#services_value' do it 'returns array of services' do - expect(global.services_value).to eq ['postgres:9.1', 'mysql:5.5'] + expect(global.services_value).to eq [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }] end end @@ -150,8 +154,8 @@ describe Gitlab::Ci::Config::Entry::Global do script: %w[rspec ls], before_script: %w(ls pwd), commands: "ls\npwd\nrspec\nls", - image: 'ruby:2.2', - services: ['postgres:9.1', 'mysql:5.5'], + image: { name: 'ruby:2.2' }, + services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }], stage: 'test', cache: { key: 'k', untracked: true, paths: ['public/'] }, variables: { 'VAR' => 'value' }, @@ -161,8 +165,8 @@ describe Gitlab::Ci::Config::Entry::Global do before_script: [], script: %w[spinach], commands: 'spinach', - image: 'ruby:2.2', - services: ['postgres:9.1', 'mysql:5.5'], + image: { name: 'ruby:2.2' }, + services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }], stage: 'test', cache: { key: 'k', untracked: true, paths: ['public/'] }, variables: {}, @@ -175,7 +179,9 @@ describe Gitlab::Ci::Config::Entry::Global do end context 'when most of entires not defined' do - before { global.compose! } + before do + global.compose! + end let(:hash) do { cache: { key: 'a' }, rspec: { script: %w[ls] } } @@ -218,7 +224,9 @@ describe Gitlab::Ci::Config::Entry::Global do # details. # context 'when entires specified but not defined' do - before { global.compose! } + before do + global.compose! + end let(:hash) do { variables: nil, rspec: { script: 'rspec' } } @@ -233,7 +241,9 @@ describe Gitlab::Ci::Config::Entry::Global do end context 'when configuration is not valid' do - before { global.compose! } + before do + global.compose! + end context 'when before script is not an array' do let(:hash) do @@ -297,7 +307,9 @@ describe Gitlab::Ci::Config::Entry::Global do end describe '#[]' do - before { global.compose! } + before do + global.compose! + end let(:hash) do { cache: { key: 'a' }, rspec: { script: 'ls' } } diff --git a/spec/lib/gitlab/ci/config/entry/image_spec.rb b/spec/lib/gitlab/ci/config/entry/image_spec.rb index 3c99cb0a1ee..bca22e39500 100644 --- a/spec/lib/gitlab/ci/config/entry/image_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/image_spec.rb @@ -3,43 +3,104 @@ require 'spec_helper' describe Gitlab::Ci::Config::Entry::Image do let(:entry) { described_class.new(config) } - describe 'validation' do - context 'when entry config value is correct' do - let(:config) { 'ruby:2.2' } + context 'when configuration is a string' do + let(:config) { 'ruby:2.2' } - describe '#value' do - it 'returns image string' do - expect(entry.value).to eq 'ruby:2.2' - end + describe '#value' do + it 'returns image hash' do + expect(entry.value).to eq({ name: 'ruby:2.2' }) end + end + + describe '#errors' do + it 'does not append errors' do + expect(entry.errors).to be_empty + end + end + + describe '#valid?' do + it 'is valid' do + expect(entry).to be_valid + end + end + + describe '#image' do + it "returns image's name" do + expect(entry.name).to eq 'ruby:2.2' + end + end - describe '#errors' do - it 'does not append errors' do - expect(entry.errors).to be_empty - end + describe '#entrypoint' do + it "returns image's entrypoint" do + expect(entry.entrypoint).to be_nil end + end + end - describe '#valid?' do - it 'is valid' do - expect(entry).to be_valid - end + context 'when configuration is a hash' do + let(:config) { { name: 'ruby:2.2', entrypoint: '/bin/sh' } } + + describe '#value' do + it 'returns image hash' do + expect(entry.value).to eq(config) end end - context 'when entry value is not correct' do - let(:config) { ['ruby:2.2'] } + describe '#errors' do + it 'does not append errors' do + expect(entry.errors).to be_empty + end + end + + describe '#valid?' do + it 'is valid' do + expect(entry).to be_valid + end + end - describe '#errors' do - it 'saves errors' do - expect(entry.errors) - .to include 'image config should be a string' - end + describe '#image' do + it "returns image's name" do + expect(entry.name).to eq 'ruby:2.2' end + end + + describe '#entrypoint' do + it "returns image's entrypoint" do + expect(entry.entrypoint).to eq '/bin/sh' + end + end + end + + context 'when entry value is not correct' do + let(:config) { ['ruby:2.2'] } + + describe '#errors' do + it 'saves errors' do + expect(entry.errors) + .to include 'image config should be a hash or a string' + end + end + + describe '#valid?' do + it 'is not valid' do + expect(entry).not_to be_valid + end + end + end + + context 'when unexpected key is specified' do + let(:config) { { name: 'ruby:2.2', non_existing: 'test' } } + + describe '#errors' do + it 'saves errors' do + expect(entry.errors) + .to include 'image config contains unknown keys: non_existing' + end + end - describe '#valid?' do - it 'is not valid' do - expect(entry).not_to be_valid - end + describe '#valid?' do + it 'is not valid' do + expect(entry).not_to be_valid end end end diff --git a/spec/lib/gitlab/ci/config/entry/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb index 9249bb9c172..92cba689f47 100644 --- a/spec/lib/gitlab/ci/config/entry/job_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb @@ -18,7 +18,9 @@ describe Gitlab::Ci::Config::Entry::Job do end describe 'validations' do - before { entry.compose! } + before do + entry.compose! + end context 'when entry config value is correct' do let(:config) { { script: 'rspec' } } @@ -97,14 +99,16 @@ describe Gitlab::Ci::Config::Entry::Job do let(:deps) { double('deps', '[]' => unspecified) } context 'when job config overrides global config' do - before { entry.compose!(deps) } + before do + entry.compose!(deps) + end let(:config) do { script: 'rspec', image: 'some_image', cache: { key: 'test' } } end it 'overrides global config' do - expect(entry[:image].value).to eq 'some_image' + expect(entry[:image].value).to eq(name: 'some_image') expect(entry[:cache].value).to eq(key: 'test') end end @@ -125,10 +129,14 @@ describe Gitlab::Ci::Config::Entry::Job do end context 'when composed' do - before { entry.compose! } + before do + entry.compose! + end describe '#value' do - before { entry.compose! } + before do + entry.compose! + end context 'when entry is correct' do let(:config) do diff --git a/spec/lib/gitlab/ci/config/entry/jobs_spec.rb b/spec/lib/gitlab/ci/config/entry/jobs_spec.rb index 7d104372ac6..c0a2b6517e3 100644 --- a/spec/lib/gitlab/ci/config/entry/jobs_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/jobs_spec.rb @@ -4,7 +4,9 @@ describe Gitlab::Ci::Config::Entry::Jobs do let(:entry) { described_class.new(config) } describe 'validations' do - before { entry.compose! } + before do + entry.compose! + end context 'when entry config value is correct' do let(:config) { { rspec: { script: 'rspec' } } } @@ -48,7 +50,9 @@ describe Gitlab::Ci::Config::Entry::Jobs do end context 'when valid job entries composed' do - before { entry.compose! } + before do + entry.compose! + end let(:config) do { rspec: { script: 'rspec' }, diff --git a/spec/lib/gitlab/ci/config/entry/service_spec.rb b/spec/lib/gitlab/ci/config/entry/service_spec.rb new file mode 100644 index 00000000000..7202fe525e4 --- /dev/null +++ b/spec/lib/gitlab/ci/config/entry/service_spec.rb @@ -0,0 +1,119 @@ +require 'spec_helper' + +describe Gitlab::Ci::Config::Entry::Service do + let(:entry) { described_class.new(config) } + + before do + entry.compose! + end + + context 'when configuration is a string' do + let(:config) { 'postgresql:9.5' } + + describe '#valid?' do + it 'is valid' do + expect(entry).to be_valid + end + end + + describe '#value' do + it 'returns valid hash' do + expect(entry.value).to include(name: 'postgresql:9.5') + end + end + + describe '#image' do + it "returns service's image name" do + expect(entry.name).to eq 'postgresql:9.5' + end + end + + describe '#alias' do + it "returns service's alias" do + expect(entry.alias).to be_nil + end + end + + describe '#command' do + it "returns service's command" do + expect(entry.command).to be_nil + end + end + end + + context 'when configuration is a hash' do + let(:config) do + { name: 'postgresql:9.5', alias: 'db', command: 'cmd', entrypoint: '/bin/sh' } + end + + describe '#valid?' do + it 'is valid' do + expect(entry).to be_valid + end + end + + describe '#value' do + it 'returns valid hash' do + expect(entry.value).to eq config + end + end + + describe '#image' do + it "returns service's image name" do + expect(entry.name).to eq 'postgresql:9.5' + end + end + + describe '#alias' do + it "returns service's alias" do + expect(entry.alias).to eq 'db' + end + end + + describe '#command' do + it "returns service's command" do + expect(entry.command).to eq 'cmd' + end + end + + describe '#entrypoint' do + it "returns service's entrypoint" do + expect(entry.entrypoint).to eq '/bin/sh' + end + end + end + + context 'when entry value is not correct' do + let(:config) { ['postgresql:9.5'] } + + describe '#errors' do + it 'saves errors' do + expect(entry.errors) + .to include 'service config should be a hash or a string' + end + end + + describe '#valid?' do + it 'is not valid' do + expect(entry).not_to be_valid + end + end + end + + context 'when unexpected key is specified' do + let(:config) { { name: 'postgresql:9.5', non_existing: 'test' } } + + describe '#errors' do + it 'saves errors' do + expect(entry.errors) + .to include 'service config contains unknown keys: non_existing' + end + end + + describe '#valid?' do + it 'is not valid' do + expect(entry).not_to be_valid + end + end + end +end diff --git a/spec/lib/gitlab/ci/config/entry/services_spec.rb b/spec/lib/gitlab/ci/config/entry/services_spec.rb index 66fad3b6b16..7c4319aee63 100644 --- a/spec/lib/gitlab/ci/config/entry/services_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/services_spec.rb @@ -3,37 +3,32 @@ require 'spec_helper' describe Gitlab::Ci::Config::Entry::Services do let(:entry) { described_class.new(config) } - describe 'validations' do - context 'when entry config value is correct' do - let(:config) { ['postgres:9.1', 'mysql:5.5'] } + before do + entry.compose! + end - describe '#value' do - it 'returns array of services as is' do - expect(entry.value).to eq config - end - end + context 'when configuration is valid' do + let(:config) { ['postgresql:9.5', { name: 'postgresql:9.1', alias: 'postgres_old' }] } - describe '#valid?' do - it 'is valid' do - expect(entry).to be_valid - end + describe '#valid?' do + it 'is valid' do + expect(entry).to be_valid end end - context 'when entry value is not correct' do - let(:config) { 'ls' } - - describe '#errors' do - it 'saves errors' do - expect(entry.errors) - .to include 'services config should be an array of strings' - end + describe '#value' do + it 'returns valid array' do + expect(entry.value).to eq([{ name: 'postgresql:9.5' }, { name: 'postgresql:9.1', alias: 'postgres_old' }]) end + end + end + + context 'when configuration is invalid' do + let(:config) { 'postgresql:9.5' } - describe '#valid?' do - it 'is not valid' do - expect(entry).not_to be_valid - end + describe '#valid?' do + it 'is invalid' do + expect(entry).not_to be_valid end end end diff --git a/spec/lib/gitlab/ci/status/build/cancelable_spec.rb b/spec/lib/gitlab/ci/status/build/cancelable_spec.rb index 8ad9b7cdf07..114d2490490 100644 --- a/spec/lib/gitlab/ci/status/build/cancelable_spec.rb +++ b/spec/lib/gitlab/ci/status/build/cancelable_spec.rb @@ -47,7 +47,9 @@ describe Gitlab::Ci::Status::Build::Cancelable do describe '#has_action?' do context 'when user is allowed to update build' do - before { build.project.team << [user, :developer] } + before do + build.project.team << [user, :developer] + end it { is_expected.to have_action } end diff --git a/spec/lib/gitlab/ci/status/build/common_spec.rb b/spec/lib/gitlab/ci/status/build/common_spec.rb index 72bd7c4eb93..03d1f46b517 100644 --- a/spec/lib/gitlab/ci/status/build/common_spec.rb +++ b/spec/lib/gitlab/ci/status/build/common_spec.rb @@ -17,13 +17,17 @@ describe Gitlab::Ci::Status::Build::Common do describe '#has_details?' do context 'when user has access to read build' do - before { project.team << [user, :developer] } + before do + project.team << [user, :developer] + end it { is_expected.to have_details } end context 'when user does not have access to read build' do - before { project.update(public_builds: false) } + before do + project.update(public_builds: false) + end it { is_expected.not_to have_details } end diff --git a/spec/lib/gitlab/ci/status/build/factory_spec.rb b/spec/lib/gitlab/ci/status/build/factory_spec.rb index 3f30b2c38f2..c8a97016f20 100644 --- a/spec/lib/gitlab/ci/status/build/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/build/factory_spec.rb @@ -6,7 +6,9 @@ describe Gitlab::Ci::Status::Build::Factory do let(:status) { factory.fabricate! } let(:factory) { described_class.new(build, user) } - before { project.team << [user, :developer] } + before do + project.team << [user, :developer] + end context 'when build is successful' do let(:build) { create(:ci_build, :success) } diff --git a/spec/lib/gitlab/ci/status/build/play_spec.rb b/spec/lib/gitlab/ci/status/build/play_spec.rb index 0e15a5f3c6b..32b2e62e4e0 100644 --- a/spec/lib/gitlab/ci/status/build/play_spec.rb +++ b/spec/lib/gitlab/ci/status/build/play_spec.rb @@ -28,7 +28,9 @@ describe Gitlab::Ci::Status::Build::Play do end context 'when user can not push to the branch' do - before { build.project.add_developer(user) } + before do + build.project.add_developer(user) + end it { is_expected.not_to have_action } end diff --git a/spec/lib/gitlab/ci/status/build/retryable_spec.rb b/spec/lib/gitlab/ci/status/build/retryable_spec.rb index 2db0f8d29bd..099d873fc01 100644 --- a/spec/lib/gitlab/ci/status/build/retryable_spec.rb +++ b/spec/lib/gitlab/ci/status/build/retryable_spec.rb @@ -47,7 +47,9 @@ describe Gitlab::Ci::Status::Build::Retryable do describe '#has_action?' do context 'when user is allowed to update build' do - before { build.project.team << [user, :developer] } + before do + build.project.team << [user, :developer] + end it { is_expected.to have_action } end diff --git a/spec/lib/gitlab/ci/status/build/stop_spec.rb b/spec/lib/gitlab/ci/status/build/stop_spec.rb index 8d021c35a69..23902f26b1a 100644 --- a/spec/lib/gitlab/ci/status/build/stop_spec.rb +++ b/spec/lib/gitlab/ci/status/build/stop_spec.rb @@ -19,7 +19,9 @@ describe Gitlab::Ci::Status::Build::Stop do describe '#has_action?' do context 'when user is allowed to update build' do - before { build.project.team << [user, :developer] } + before do + build.project.team << [user, :developer] + end it { is_expected.to have_action } end diff --git a/spec/lib/gitlab/ci/status/external/common_spec.rb b/spec/lib/gitlab/ci/status/external/common_spec.rb index 5a97d98b55f..b38fbee2486 100644 --- a/spec/lib/gitlab/ci/status/external/common_spec.rb +++ b/spec/lib/gitlab/ci/status/external/common_spec.rb @@ -4,9 +4,10 @@ describe Gitlab::Ci::Status::External::Common do let(:user) { create(:user) } let(:project) { external_status.project } let(:external_target_url) { 'http://example.gitlab.com/status' } + let(:external_description) { 'my description' } let(:external_status) do - create(:generic_commit_status, target_url: external_target_url) + create(:generic_commit_status, target_url: external_target_url, description: external_description) end subject do @@ -15,13 +16,21 @@ describe Gitlab::Ci::Status::External::Common do .extend(described_class) end + describe '#label' do + it 'returns description' do + expect(subject.label).to eq external_description + end + end + describe '#has_action?' do it { is_expected.not_to have_action } end describe '#has_details?' do context 'when user has access to read commit status' do - before { project.team << [user, :developer] } + before do + project.team << [user, :developer] + end it { is_expected.to have_details } end diff --git a/spec/lib/gitlab/ci/status/pipeline/common_spec.rb b/spec/lib/gitlab/ci/status/pipeline/common_spec.rb index d665674bf70..f5fd31e8d03 100644 --- a/spec/lib/gitlab/ci/status/pipeline/common_spec.rb +++ b/spec/lib/gitlab/ci/status/pipeline/common_spec.rb @@ -17,7 +17,9 @@ describe Gitlab::Ci::Status::Pipeline::Common do describe '#has_details?' do context 'when user has access to read pipeline' do - before { project.team << [user, :developer] } + before do + project.team << [user, :developer] + end it { is_expected.to have_details } end diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb index 9b1d66a1b1c..26e5d73d333 100644 --- a/spec/lib/gitlab/database_spec.rb +++ b/spec/lib/gitlab/database_spec.rb @@ -53,14 +53,18 @@ describe Gitlab::Database, lib: true do describe '.nulls_last_order' do context 'when using PostgreSQL' do - before { expect(described_class).to receive(:postgresql?).and_return(true) } + before do + expect(described_class).to receive(:postgresql?).and_return(true) + end it { expect(described_class.nulls_last_order('column', 'ASC')).to eq 'column ASC NULLS LAST'} it { expect(described_class.nulls_last_order('column', 'DESC')).to eq 'column DESC NULLS LAST'} end context 'when using MySQL' do - before { expect(described_class).to receive(:postgresql?).and_return(false) } + before do + expect(described_class).to receive(:postgresql?).and_return(false) + end it { expect(described_class.nulls_last_order('column', 'ASC')).to eq 'column IS NULL, column ASC'} it { expect(described_class.nulls_last_order('column', 'DESC')).to eq 'column DESC'} @@ -69,14 +73,18 @@ describe Gitlab::Database, lib: true do describe '.nulls_first_order' do context 'when using PostgreSQL' do - before { expect(described_class).to receive(:postgresql?).and_return(true) } + before do + expect(described_class).to receive(:postgresql?).and_return(true) + end it { expect(described_class.nulls_first_order('column', 'ASC')).to eq 'column ASC NULLS FIRST'} it { expect(described_class.nulls_first_order('column', 'DESC')).to eq 'column DESC NULLS FIRST'} end context 'when using MySQL' do - before { expect(described_class).to receive(:postgresql?).and_return(false) } + before do + expect(described_class).to receive(:postgresql?).and_return(false) + end it { expect(described_class.nulls_first_order('column', 'ASC')).to eq 'column ASC'} it { expect(described_class.nulls_first_order('column', 'DESC')).to eq 'column IS NULL, column DESC'} diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb index a9953bb0d01..f289131cc3a 100644 --- a/spec/lib/gitlab/diff/file_spec.rb +++ b/spec/lib/gitlab/diff/file_spec.rb @@ -92,4 +92,305 @@ describe Gitlab::Diff::File, lib: true do expect(diff_file.diffable?).to be_falsey end end + + describe '#content_changed?' do + context 'when created' do + let(:commit) { project.commit('33f3729a45c02fc67d00adb1b8bca394b0e761d9') } + let(:diff_file) { commit.diffs.diff_file_with_new_path('files/images/6049019_460s.jpg') } + + it 'returns false' do + expect(diff_file.content_changed?).to be_falsey + end + end + + context 'when deleted' do + let(:commit) { project.commit('d59c60028b053793cecfb4022de34602e1a9218e') } + let(:diff_file) { commit.diffs.diff_file_with_old_path('files/js/commit.js.coffee') } + + it 'returns false' do + expect(diff_file.content_changed?).to be_falsey + end + end + + context 'when renamed' do + let(:commit) { project.commit('6907208d755b60ebeacb2e9dfea74c92c3449a1f') } + let(:diff_file) { commit.diffs.diff_file_with_new_path('files/js/commit.coffee') } + + before do + allow(diff_file.new_blob).to receive(:id).and_return(diff_file.old_blob.id) + end + + it 'returns false' do + expect(diff_file.content_changed?).to be_falsey + end + end + + context 'when content changed' do + context 'when binary' do + let(:commit) { project.commit('2f63565e7aac07bcdadb654e253078b727143ec4') } + let(:diff_file) { commit.diffs.diff_file_with_new_path('files/images/6049019_460s.jpg') } + + it 'returns true' do + expect(diff_file.content_changed?).to be_truthy + end + end + + context 'when not binary' do + let(:commit) { project.commit('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') } + let(:diff_file) { commit.diffs.diff_file_with_new_path('files/ruby/popen.rb') } + + it 'returns true' do + expect(diff_file.content_changed?).to be_truthy + end + end + end + end + + describe '#simple_viewer' do + context 'when the file is not diffable' do + before do + allow(diff_file).to receive(:diffable?).and_return(false) + end + + it 'returns a Not Diffable viewer' do + expect(diff_file.simple_viewer).to be_a(DiffViewer::NotDiffable) + end + end + + context 'when the content changed' do + context 'when the file represented by the diff file is binary' do + before do + allow(diff_file).to receive(:raw_binary?).and_return(true) + end + + it 'returns a No Preview viewer' do + expect(diff_file.simple_viewer).to be_a(DiffViewer::NoPreview) + end + end + + context 'when the diff file old and new blob types are different' do + before do + allow(diff_file).to receive(:different_type?).and_return(true) + end + + it 'returns a No Preview viewer' do + expect(diff_file.simple_viewer).to be_a(DiffViewer::NoPreview) + end + end + + context 'when the file represented by the diff file is text-based' do + it 'returns a text viewer' do + expect(diff_file.simple_viewer).to be_a(DiffViewer::Text) + end + end + end + + context 'when created' do + let(:commit) { project.commit('913c66a37b4a45b9769037c55c2d238bd0942d2e') } + let(:diff_file) { commit.diffs.diff_file_with_new_path('files/ruby/popen.rb') } + + before do + allow(diff_file).to receive(:content_changed?).and_return(nil) + end + + context 'when the file represented by the diff file is binary' do + before do + allow(diff_file).to receive(:raw_binary?).and_return(true) + end + + it 'returns an Added viewer' do + expect(diff_file.simple_viewer).to be_a(DiffViewer::Added) + end + end + + context 'when the diff file old and new blob types are different' do + before do + allow(diff_file).to receive(:different_type?).and_return(true) + end + + it 'returns an Added viewer' do + expect(diff_file.simple_viewer).to be_a(DiffViewer::Added) + end + end + + context 'when the file represented by the diff file is text-based' do + it 'returns a text viewer' do + expect(diff_file.simple_viewer).to be_a(DiffViewer::Text) + end + end + end + + context 'when deleted' do + let(:commit) { project.commit('d59c60028b053793cecfb4022de34602e1a9218e') } + let(:diff_file) { commit.diffs.diff_file_with_old_path('files/js/commit.js.coffee') } + + before do + allow(diff_file).to receive(:content_changed?).and_return(nil) + end + + context 'when the file represented by the diff file is binary' do + before do + allow(diff_file).to receive(:raw_binary?).and_return(true) + end + + it 'returns a Deleted viewer' do + expect(diff_file.simple_viewer).to be_a(DiffViewer::Deleted) + end + end + + context 'when the diff file old and new blob types are different' do + before do + allow(diff_file).to receive(:different_type?).and_return(true) + end + + it 'returns a Deleted viewer' do + expect(diff_file.simple_viewer).to be_a(DiffViewer::Deleted) + end + end + + context 'when the file represented by the diff file is text-based' do + it 'returns a text viewer' do + expect(diff_file.simple_viewer).to be_a(DiffViewer::Text) + end + end + end + + context 'when renamed' do + let(:commit) { project.commit('6907208d755b60ebeacb2e9dfea74c92c3449a1f') } + let(:diff_file) { commit.diffs.diff_file_with_new_path('files/js/commit.coffee') } + + before do + allow(diff_file).to receive(:content_changed?).and_return(nil) + end + + it 'returns a Renamed viewer' do + expect(diff_file.simple_viewer).to be_a(DiffViewer::Renamed) + end + end + + context 'when mode changed' do + before do + allow(diff_file).to receive(:content_changed?).and_return(nil) + allow(diff_file).to receive(:mode_changed?).and_return(true) + end + + it 'returns a Mode Changed viewer' do + expect(diff_file.simple_viewer).to be_a(DiffViewer::ModeChanged) + end + end + end + + describe '#rich_viewer' do + let(:commit) { project.commit('2f63565e7aac07bcdadb654e253078b727143ec4') } + let(:diff_file) { commit.diffs.diff_file_with_new_path('files/images/6049019_460s.jpg') } + + context 'when the diff file has a matching viewer' do + context 'when the diff file content did not change' do + before do + allow(diff_file).to receive(:content_changed?).and_return(false) + end + + it 'returns nil' do + expect(diff_file.rich_viewer).to be_nil + end + end + + context 'when the diff file is not diffable' do + before do + allow(diff_file).to receive(:diffable?).and_return(false) + end + + it 'returns nil' do + expect(diff_file.rich_viewer).to be_nil + end + end + + context 'when the diff file old and new blob types are different' do + before do + allow(diff_file).to receive(:different_type?).and_return(true) + end + + it 'returns nil' do + expect(diff_file.rich_viewer).to be_nil + end + end + + context 'when the diff file has an external storage error' do + before do + allow(diff_file).to receive(:external_storage_error?).and_return(true) + end + + it 'returns nil' do + expect(diff_file.rich_viewer).to be_nil + end + end + + context 'when everything is right' do + it 'returns the viewer' do + expect(diff_file.rich_viewer).to be_a(DiffViewer::Image) + end + end + end + + context 'when the diff file does not have a matching viewer' do + let(:commit) { project.commit('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') } + let(:diff_file) { commit.diffs.diff_file_with_new_path('files/ruby/popen.rb') } + + it 'returns nil' do + expect(diff_file.rich_viewer).to be_nil + end + end + end + + describe '#rendered_as_text?' do + context 'when the simple viewer is text-based' do + let(:commit) { project.commit('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') } + let(:diff_file) { commit.diffs.diff_file_with_new_path('files/ruby/popen.rb') } + + context 'when ignoring errors' do + context 'when the viewer has render errors' do + before do + diff_file.diff.too_large! + end + + it 'returns true' do + expect(diff_file.rendered_as_text?).to be_truthy + end + end + + context "when the viewer doesn't have render errors" do + it 'returns true' do + expect(diff_file.rendered_as_text?).to be_truthy + end + end + end + + context 'when not ignoring errors' do + context 'when the viewer has render errors' do + before do + diff_file.diff.too_large! + end + + it 'returns false' do + expect(diff_file.rendered_as_text?(ignore_errors: false)).to be_falsey + end + end + + context "when the viewer doesn't have render errors" do + it 'returns true' do + expect(diff_file.rendered_as_text?(ignore_errors: false)).to be_truthy + end + end + end + end + + context 'when the simple viewer is binary' do + let(:commit) { project.commit('2f63565e7aac07bcdadb654e253078b727143ec4') } + let(:diff_file) { commit.diffs.diff_file_with_new_path('files/images/6049019_460s.jpg') } + + it 'returns false' do + expect(diff_file.rendered_as_text?).to be_falsey + end + end + end end diff --git a/spec/lib/gitlab/etag_caching/middleware_spec.rb b/spec/lib/gitlab/etag_caching/middleware_spec.rb index 3c6ef7c7ccb..4acf4f047f1 100644 --- a/spec/lib/gitlab/etag_caching/middleware_spec.rb +++ b/spec/lib/gitlab/etag_caching/middleware_spec.rb @@ -15,13 +15,13 @@ describe Gitlab::EtagCaching::Middleware do end it 'does not add ETag header' do - _, headers, _ = middleware.call(build_env(path, if_none_match)) + _, headers, _ = middleware.call(build_request(path, if_none_match)) expect(headers['ETag']).to be_nil end it 'passes status code from app' do - status, _, _ = middleware.call(build_env(path, if_none_match)) + status, _, _ = middleware.call(build_request(path, if_none_match)) expect(status).to eq app_status_code end @@ -39,7 +39,7 @@ describe Gitlab::EtagCaching::Middleware do expect_any_instance_of(Gitlab::EtagCaching::Store) .to receive(:touch).and_return('123') - middleware.call(build_env(path, if_none_match)) + middleware.call(build_request(path, if_none_match)) end context 'when If-None-Match header was specified' do @@ -51,7 +51,7 @@ describe Gitlab::EtagCaching::Middleware do expect(Gitlab::Metrics).to receive(:add_event) .with(:etag_caching_key_not_found, endpoint: 'issue_notes') - middleware.call(build_env(path, if_none_match)) + middleware.call(build_request(path, if_none_match)) end end end @@ -65,7 +65,7 @@ describe Gitlab::EtagCaching::Middleware do end it 'returns this value as header' do - _, headers, _ = middleware.call(build_env(path, if_none_match)) + _, headers, _ = middleware.call(build_request(path, if_none_match)) expect(headers['ETag']).to eq 'W/"123"' end @@ -82,17 +82,17 @@ describe Gitlab::EtagCaching::Middleware do it 'does not call app' do expect(app).not_to receive(:call) - middleware.call(build_env(path, if_none_match)) + middleware.call(build_request(path, if_none_match)) end it 'returns status code 304' do - status, _, _ = middleware.call(build_env(path, if_none_match)) + status, _, _ = middleware.call(build_request(path, if_none_match)) expect(status).to eq 304 end it 'returns empty body' do - _, _, body = middleware.call(build_env(path, if_none_match)) + _, _, body = middleware.call(build_request(path, if_none_match)) expect(body).to be_empty end @@ -103,7 +103,7 @@ describe Gitlab::EtagCaching::Middleware do expect(Gitlab::Metrics).to receive(:add_event) .with(:etag_caching_cache_hit, endpoint: 'issue_notes') - middleware.call(build_env(path, if_none_match)) + middleware.call(build_request(path, if_none_match)) end context 'when polling is disabled' do @@ -113,7 +113,7 @@ describe Gitlab::EtagCaching::Middleware do end it 'returns status code 429' do - status, _, _ = middleware.call(build_env(path, if_none_match)) + status, _, _ = middleware.call(build_request(path, if_none_match)) expect(status).to eq 429 end @@ -131,7 +131,7 @@ describe Gitlab::EtagCaching::Middleware do it 'calls app' do expect(app).to receive(:call).and_return([app_status_code, {}, ['body']]) - middleware.call(build_env(path, if_none_match)) + middleware.call(build_request(path, if_none_match)) end it 'tracks "etag_caching_resource_changed" event' do @@ -142,7 +142,7 @@ describe Gitlab::EtagCaching::Middleware do expect(Gitlab::Metrics).to receive(:add_event) .with(:etag_caching_resource_changed, endpoint: 'issue_notes') - middleware.call(build_env(path, if_none_match)) + middleware.call(build_request(path, if_none_match)) end end @@ -160,7 +160,7 @@ describe Gitlab::EtagCaching::Middleware do expect(Gitlab::Metrics).to receive(:add_event) .with(:etag_caching_header_missing, endpoint: 'issue_notes') - middleware.call(build_env(path, if_none_match)) + middleware.call(build_request(path, if_none_match)) end end @@ -192,10 +192,7 @@ describe Gitlab::EtagCaching::Middleware do .to receive(:get).and_return(value) end - def build_env(path, if_none_match) - { - 'PATH_INFO' => path, - 'HTTP_IF_NONE_MATCH' => if_none_match - } + def build_request(path, if_none_match) + { 'PATH_INFO' => path, 'HTTP_IF_NONE_MATCH' => if_none_match } end end diff --git a/spec/lib/gitlab/etag_caching/router_spec.rb b/spec/lib/gitlab/etag_caching/router_spec.rb index 2bb40827fcf..f69cb502ca6 100644 --- a/spec/lib/gitlab/etag_caching/router_spec.rb +++ b/spec/lib/gitlab/etag_caching/router_spec.rb @@ -2,115 +2,91 @@ require 'spec_helper' describe Gitlab::EtagCaching::Router do it 'matches issue notes endpoint' do - request = build_request( + result = described_class.match( '/my-group/and-subgroup/here-comes-the-project/noteable/issue/1/notes' ) - result = described_class.match(request) - expect(result).to be_present expect(result.name).to eq 'issue_notes' end it 'matches issue title endpoint' do - request = build_request( + result = described_class.match( '/my-group/my-project/issues/123/realtime_changes' ) - result = described_class.match(request) - expect(result).to be_present expect(result.name).to eq 'issue_title' end it 'matches project pipelines endpoint' do - request = build_request( + result = described_class.match( '/my-group/my-project/pipelines.json' ) - result = described_class.match(request) - expect(result).to be_present expect(result.name).to eq 'project_pipelines' end it 'matches commit pipelines endpoint' do - request = build_request( + result = described_class.match( '/my-group/my-project/commit/aa8260d253a53f73f6c26c734c72fdd600f6e6d4/pipelines.json' ) - result = described_class.match(request) - expect(result).to be_present expect(result.name).to eq 'commit_pipelines' end it 'matches new merge request pipelines endpoint' do - request = build_request( + result = described_class.match( '/my-group/my-project/merge_requests/new.json' ) - result = described_class.match(request) - expect(result).to be_present expect(result.name).to eq 'new_merge_request_pipelines' end it 'matches merge request pipelines endpoint' do - request = build_request( + result = described_class.match( '/my-group/my-project/merge_requests/234/pipelines.json' ) - result = described_class.match(request) - expect(result).to be_present expect(result.name).to eq 'merge_request_pipelines' end it 'matches build endpoint' do - request = build_request( + result = described_class.match( '/my-group/my-project/builds/234.json' ) - result = described_class.match(request) - expect(result).to be_present expect(result.name).to eq 'project_build' end it 'does not match blob with confusing name' do - request = build_request( + result = described_class.match( '/my-group/my-project/blob/master/pipelines.json' ) - result = described_class.match(request) - expect(result).to be_blank end it 'matches the environments path' do - request = build_request( + result = described_class.match( '/my-group/my-project/environments.json' ) - result = described_class.match(request) expect(result).to be_present - expect(result.name).to eq 'environments' end it 'matches pipeline#show endpoint' do - request = build_request( + result = described_class.match( '/my-group/my-project/pipelines/2.json' ) - result = described_class.match(request) - expect(result).to be_present expect(result.name).to eq 'project_pipeline' end - - def build_request(path) - double(path_info: path) - end end diff --git a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb index 5d416c9eec3..eaec699ad90 100644 --- a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb +++ b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb @@ -6,7 +6,9 @@ describe Gitlab::Gfm::ReferenceRewriter do let(:new_project) { create(:empty_project, name: 'new-project') } let(:user) { create(:user) } - before { old_project.team << [user, :reporter] } + before do + old_project.team << [user, :reporter] + end describe '#rewrite' do subject do diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index e1e4aa9fde9..eee4c9eab6d 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -16,7 +16,9 @@ describe Gitlab::Git::Repository, seed_helper: true do describe '#root_ref' do context 'with gitaly disabled' do - before { allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_return(false) } + before do + allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_return(false) + end it 'calls #discover_default_branch' do expect(repository).to receive(:discover_default_branch) @@ -25,8 +27,13 @@ describe Gitlab::Git::Repository, seed_helper: true do end context 'with gitaly enabled' do - before { stub_gitaly } - after { Gitlab::GitalyClient.clear_stubs! } + before do + stub_gitaly + end + + after do + Gitlab::GitalyClient.clear_stubs! + end it 'gets the branch name from GitalyClient' do expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:default_branch_name) @@ -120,8 +127,13 @@ describe Gitlab::Git::Repository, seed_helper: true do it { is_expected.not_to include("branch-from-space") } context 'with gitaly enabled' do - before { stub_gitaly } - after { Gitlab::GitalyClient.clear_stubs! } + before do + stub_gitaly + end + + after do + Gitlab::GitalyClient.clear_stubs! + end it 'gets the branch names from GitalyClient' do expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:branch_names) @@ -158,8 +170,13 @@ describe Gitlab::Git::Repository, seed_helper: true do it { is_expected.not_to include("v5.0.0") } context 'with gitaly enabled' do - before { stub_gitaly } - after { Gitlab::GitalyClient.clear_stubs! } + before do + stub_gitaly + end + + after do + Gitlab::GitalyClient.clear_stubs! + end it 'gets the tag names from GitalyClient' do expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:tag_names) @@ -1280,8 +1297,13 @@ describe Gitlab::Git::Repository, seed_helper: true do end context 'with gitaly enabled' do - before { stub_gitaly } - after { Gitlab::GitalyClient.clear_stubs! } + before do + stub_gitaly + end + + after do + Gitlab::GitalyClient.clear_stubs! + end it 'gets the branches from GitalyClient' do expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:local_branches). diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index 36d1d777583..3dcc20c48e8 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -60,7 +60,9 @@ describe Gitlab::GitAccess, lib: true do let(:actor) { deploy_key } context 'when the DeployKey has access to the project' do - before { deploy_key.projects << project } + before do + deploy_key.projects << project + end it 'allows pull access' do expect { pull_access_check }.not_to raise_error @@ -84,7 +86,9 @@ describe Gitlab::GitAccess, lib: true do context 'when actor is a User' do context 'when the User can read the project' do - before { project.team << [user, :master] } + before do + project.team << [user, :master] + end it 'allows pull access' do expect { pull_access_check }.not_to raise_error @@ -159,7 +163,9 @@ describe Gitlab::GitAccess, lib: true do end describe '#check_command_disabled!' do - before { project.team << [user, :master] } + before do + project.team << [user, :master] + end context 'over http' do let(:protocol) { 'http' } @@ -196,7 +202,9 @@ describe Gitlab::GitAccess, lib: true do describe '#check_download_access!' do describe 'master permissions' do - before { project.team << [user, :master] } + before do + project.team << [user, :master] + end context 'pull code' do it { expect { pull_access_check }.not_to raise_error } @@ -204,7 +212,9 @@ describe Gitlab::GitAccess, lib: true do end describe 'guest permissions' do - before { project.team << [user, :guest] } + before do + project.team << [user, :guest] + end context 'pull code' do it { expect { pull_access_check }.to raise_unauthorized('You are not allowed to download code from this project.') } @@ -253,7 +263,9 @@ describe Gitlab::GitAccess, lib: true do context 'pull code' do context 'when project is authorized' do - before { key.projects << project } + before do + key.projects << project + end it { expect { pull_access_check }.not_to raise_error } end @@ -292,7 +304,9 @@ describe Gitlab::GitAccess, lib: true do end describe 'reporter user' do - before { project.team << [user, :reporter] } + before do + project.team << [user, :reporter] + end context 'pull code' do it { expect { pull_access_check }.not_to raise_error } @@ -303,7 +317,9 @@ describe Gitlab::GitAccess, lib: true do let(:user) { create(:admin) } context 'when member of the project' do - before { project.team << [user, :reporter] } + before do + project.team << [user, :reporter] + end context 'pull code' do it { expect { pull_access_check }.not_to raise_error } @@ -328,7 +344,9 @@ describe Gitlab::GitAccess, lib: true do end describe '#check_push_access!' do - before { merge_into_protected_branch } + before do + merge_into_protected_branch + end let(:unprotected_branch) { 'unprotected_branch' } let(:changes) do @@ -457,19 +475,25 @@ describe Gitlab::GitAccess, lib: true do [%w(feature exact), ['feat*', 'wildcard']].each do |protected_branch_name, protected_branch_type| context do - before { create(:protected_branch, name: protected_branch_name, project: project) } + before do + create(:protected_branch, name: protected_branch_name, project: project) + end run_permission_checks(permissions_matrix) end context "when developers are allowed to push into the #{protected_branch_type} protected branch" do - before { create(:protected_branch, :developers_can_push, name: protected_branch_name, project: project) } + before do + create(:protected_branch, :developers_can_push, name: protected_branch_name, project: project) + end run_permission_checks(permissions_matrix.deep_merge(developer: { push_protected_branch: true, push_all: true, merge_into_protected_branch: true })) end context "developers are allowed to merge into the #{protected_branch_type} protected branch" do - before { create(:protected_branch, :developers_can_merge, name: protected_branch_name, project: project) } + before do + create(:protected_branch, :developers_can_merge, name: protected_branch_name, project: project) + end context "when a merge request exists for the given source/target branch" do context "when the merge request is in progress" do @@ -496,13 +520,17 @@ describe Gitlab::GitAccess, lib: true do end context "when developers are allowed to push and merge into the #{protected_branch_type} protected branch" do - before { create(:protected_branch, :developers_can_merge, :developers_can_push, name: protected_branch_name, project: project) } + before do + create(:protected_branch, :developers_can_merge, :developers_can_push, name: protected_branch_name, project: project) + end run_permission_checks(permissions_matrix.deep_merge(developer: { push_protected_branch: true, push_all: true, merge_into_protected_branch: true })) end context "when no one is allowed to push to the #{protected_branch_name} protected branch" do - before { create(:protected_branch, :no_one_can_push, name: protected_branch_name, project: project) } + before do + create(:protected_branch, :no_one_can_push, name: protected_branch_name, project: project) + end run_permission_checks(permissions_matrix.deep_merge(developer: { push_protected_branch: false, push_all: false, merge_into_protected_branch: false }, master: { push_protected_branch: false, push_all: false, merge_into_protected_branch: false }, @@ -515,7 +543,9 @@ describe Gitlab::GitAccess, lib: true do let(:authentication_abilities) { build_authentication_abilities } context 'when project is authorized' do - before { project.team << [user, :reporter] } + before do + project.team << [user, :reporter] + end it { expect { push_access_check }.to raise_unauthorized('You are not allowed to upload code for this project.') } end @@ -549,7 +579,9 @@ describe Gitlab::GitAccess, lib: true do let(:can_push) { true } context 'when project is authorized' do - before { key.projects << project } + before do + key.projects << project + end it { expect { push_access_check }.not_to raise_error } end @@ -579,7 +611,9 @@ describe Gitlab::GitAccess, lib: true do let(:can_push) { false } context 'when project is authorized' do - before { key.projects << project } + before do + key.projects << project + end it { expect { push_access_check }.to raise_unauthorized('This deploy key does not have write access to this project.') } end diff --git a/spec/lib/gitlab/gitaly_client_spec.rb b/spec/lib/gitlab/gitaly_client_spec.rb index 95ecba67532..ce7b18b784a 100644 --- a/spec/lib/gitlab/gitaly_client_spec.rb +++ b/spec/lib/gitlab/gitaly_client_spec.rb @@ -5,7 +5,9 @@ require 'spec_helper' describe Gitlab::GitalyClient, lib: true, skip_gitaly_mock: true do describe '.stub' do # Notice that this is referring to gRPC "stubs", not rspec stubs - before { described_class.clear_stubs! } + before do + described_class.clear_stubs! + end context 'when passed a UNIX socket address' do it 'passes the address as-is to GRPC' do @@ -41,7 +43,9 @@ describe Gitlab::GitalyClient, lib: true, skip_gitaly_mock: true do let(:real_feature_name) { "gitaly_#{feature_name}" } context 'when Gitaly is disabled' do - before { allow(described_class).to receive(:enabled?).and_return(false) } + before do + allow(described_class).to receive(:enabled?).and_return(false) + end it 'returns false' do expect(described_class.feature_enabled?(feature_name)).to be(false) @@ -66,7 +70,9 @@ describe Gitlab::GitalyClient, lib: true, skip_gitaly_mock: true do end context "when the feature flag is set to disable" do - before { Feature.get(real_feature_name).disable } + before do + Feature.get(real_feature_name).disable + end it 'returns false' do expect(described_class.feature_enabled?(feature_name, status: feature_status)).to be(false) @@ -74,7 +80,9 @@ describe Gitlab::GitalyClient, lib: true, skip_gitaly_mock: true do end context "when the feature flag is set to enable" do - before { Feature.get(real_feature_name).enable } + before do + Feature.get(real_feature_name).enable + end it 'returns true' do expect(described_class.feature_enabled?(feature_name, status: feature_status)).to be(true) @@ -82,7 +90,9 @@ describe Gitlab::GitalyClient, lib: true, skip_gitaly_mock: true do end context "when the feature flag is set to a percentage of time" do - before { Feature.get(real_feature_name).enable_percentage_of_time(70) } + before do + Feature.get(real_feature_name).enable_percentage_of_time(70) + end it 'bases the result on pseudo-random numbers' do expect(Random).to receive(:rand).and_return(0.3) @@ -104,7 +114,9 @@ describe Gitlab::GitalyClient, lib: true, skip_gitaly_mock: true do end context "when the feature flag is set to disable" do - before { Feature.get(real_feature_name).disable } + before do + Feature.get(real_feature_name).disable + end it 'returns false' do expect(described_class.feature_enabled?(feature_name, status: feature_status)).to be(false) diff --git a/spec/lib/gitlab/highlight_spec.rb b/spec/lib/gitlab/highlight_spec.rb index c2bb9f9a166..fdc5b484ef1 100644 --- a/spec/lib/gitlab/highlight_spec.rb +++ b/spec/lib/gitlab/highlight_spec.rb @@ -15,7 +15,9 @@ describe Gitlab::Highlight, lib: true do Gitlab::Highlight.new(blob.path, blob.data, repository: repository) end - before { project.change_head('gitattributes') } + before do + project.change_head('gitattributes') + end describe 'basic language selection' do let(:path) { 'custom-highlighting/test.gitlab-custom' } diff --git a/spec/lib/gitlab/i18n_spec.rb b/spec/lib/gitlab/i18n_spec.rb index a3dbeaa3753..0dba4132101 100644 --- a/spec/lib/gitlab/i18n_spec.rb +++ b/spec/lib/gitlab/i18n_spec.rb @@ -4,7 +4,9 @@ describe Gitlab::I18n, lib: true do let(:user) { create(:user, preferred_language: 'es') } describe '.locale=' do - after { described_class.use_default_locale } + after do + described_class.use_default_locale + end it 'sets the locale based on current user preferred language' do described_class.locale = user.preferred_language diff --git a/spec/lib/gitlab/kubernetes_spec.rb b/spec/lib/gitlab/kubernetes_spec.rb index 91f9d06b85a..e8c599a95ee 100644 --- a/spec/lib/gitlab/kubernetes_spec.rb +++ b/spec/lib/gitlab/kubernetes_spec.rb @@ -1,6 +1,7 @@ require 'spec_helper' describe Gitlab::Kubernetes do + include KubernetesHelpers include described_class describe '#container_exec_url' do @@ -36,4 +37,13 @@ describe Gitlab::Kubernetes do it { expect(result.query).to match(/\Acontainer=container\+1&/) } end end + + describe '#filter_by_label' do + it 'returns matching labels' do + matching_items = [kube_pod(app: 'foo')] + items = matching_items + [kube_pod] + + expect(filter_by_label(items, app: 'foo')).to eq(matching_items) + end + end end diff --git a/spec/lib/gitlab/ldap/adapter_spec.rb b/spec/lib/gitlab/ldap/adapter_spec.rb index 563c074017a..9454878b057 100644 --- a/spec/lib/gitlab/ldap/adapter_spec.rb +++ b/spec/lib/gitlab/ldap/adapter_spec.rb @@ -74,13 +74,17 @@ describe Gitlab::LDAP::Adapter, lib: true do subject { adapter.dn_matches_filter?(:dn, :filter) } context "when the search result is non-empty" do - before { allow(adapter).to receive(:ldap_search).and_return([:foo]) } + before do + allow(adapter).to receive(:ldap_search).and_return([:foo]) + end it { is_expected.to be_truthy } end context "when the search result is empty" do - before { allow(adapter).to receive(:ldap_search).and_return([]) } + before do + allow(adapter).to receive(:ldap_search).and_return([]) + end it { is_expected.to be_falsey } end @@ -91,13 +95,17 @@ describe Gitlab::LDAP::Adapter, lib: true do context "when the search is successful" do context "and the result is non-empty" do - before { allow(ldap).to receive(:search).and_return([:foo]) } + before do + allow(ldap).to receive(:search).and_return([:foo]) + end it { is_expected.to eq [:foo] } end context "and the result is empty" do - before { allow(ldap).to receive(:search).and_return([]) } + before do + allow(ldap).to receive(:search).and_return([]) + end it { is_expected.to eq [] } end diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb index a0eda685ca3..f0a1dd22fee 100644 --- a/spec/lib/gitlab/ldap/user_spec.rb +++ b/spec/lib/gitlab/ldap/user_spec.rb @@ -173,7 +173,9 @@ describe Gitlab::LDAP::User, lib: true do context 'signup' do context 'dont block on create' do - before { configure_block(false) } + before do + configure_block(false) + end it do ldap_user.save @@ -183,7 +185,9 @@ describe Gitlab::LDAP::User, lib: true do end context 'block on create' do - before { configure_block(true) } + before do + configure_block(true) + end it do ldap_user.save @@ -200,7 +204,9 @@ describe Gitlab::LDAP::User, lib: true do end context 'dont block on create' do - before { configure_block(false) } + before do + configure_block(false) + end it do ldap_user.save @@ -210,7 +216,9 @@ describe Gitlab::LDAP::User, lib: true do end context 'block on create' do - before { configure_block(true) } + before do + configure_block(true) + end it do ldap_user.save diff --git a/spec/lib/gitlab/middleware/rails_queue_duration_spec.rb b/spec/lib/gitlab/middleware/rails_queue_duration_spec.rb index 168090d5b5c..88107536c9e 100644 --- a/spec/lib/gitlab/middleware/rails_queue_duration_spec.rb +++ b/spec/lib/gitlab/middleware/rails_queue_duration_spec.rb @@ -6,7 +6,9 @@ describe Gitlab::Middleware::RailsQueueDuration do let(:env) { {} } let(:transaction) { double(:transaction) } - before { expect(app).to receive(:call).with(env).and_return('yay') } + before do + expect(app).to receive(:call).with(env).and_return('yay') + end describe '#call' do it 'calls the app when metrics are disabled' do @@ -15,7 +17,9 @@ describe Gitlab::Middleware::RailsQueueDuration do end context 'when metrics are enabled' do - before { allow(Gitlab::Metrics).to receive(:current_transaction).and_return(transaction) } + before do + allow(Gitlab::Metrics).to receive(:current_transaction).and_return(transaction) + end it 'calls the app when metrics are enabled but no timing header is found' do expect(middleware.call(env)).to eq('yay') diff --git a/spec/lib/gitlab/o_auth/auth_hash_spec.rb b/spec/lib/gitlab/o_auth/auth_hash_spec.rb index 8aaeb5779d3..19ab17419fc 100644 --- a/spec/lib/gitlab/o_auth/auth_hash_spec.rb +++ b/spec/lib/gitlab/o_auth/auth_hash_spec.rb @@ -55,7 +55,9 @@ describe Gitlab::OAuth::AuthHash, lib: true do end context 'email not provided' do - before { info_hash.delete(:email) } + before do + info_hash.delete(:email) + end it 'generates a temp email' do expect( auth_hash.email).to start_with('temp-email-for-oauth') @@ -63,7 +65,9 @@ describe Gitlab::OAuth::AuthHash, lib: true do end context 'username not provided' do - before { info_hash.delete(:nickname) } + before do + info_hash.delete(:nickname) + end it 'takes the first part of the email as username' do expect(auth_hash.username).to eql 'onur.kucuk_ABC-123' @@ -71,7 +75,9 @@ describe Gitlab::OAuth::AuthHash, lib: true do end context 'name not provided' do - before { info_hash.delete(:name) } + before do + info_hash.delete(:name) + end it 'concats first and lastname as the name' do expect(auth_hash.name).to eql name_utf8 diff --git a/spec/lib/gitlab/o_auth/user_spec.rb b/spec/lib/gitlab/o_auth/user_spec.rb index 8943d1aa488..ea29cb9caf1 100644 --- a/spec/lib/gitlab/o_auth/user_spec.rb +++ b/spec/lib/gitlab/o_auth/user_spec.rb @@ -112,7 +112,9 @@ describe Gitlab::OAuth::User, lib: true do end context 'with new allow_single_sign_on enabled syntax' do - before { stub_omniauth_config(allow_single_sign_on: ['twitter']) } + before do + stub_omniauth_config(allow_single_sign_on: ['twitter']) + end it "creates a user from Omniauth" do oauth_user.save @@ -125,7 +127,9 @@ describe Gitlab::OAuth::User, lib: true do end context "with old allow_single_sign_on enabled syntax" do - before { stub_omniauth_config(allow_single_sign_on: true) } + before do + stub_omniauth_config(allow_single_sign_on: true) + end it "creates a user from Omniauth" do oauth_user.save @@ -138,14 +142,20 @@ describe Gitlab::OAuth::User, lib: true do end context 'with new allow_single_sign_on disabled syntax' do - before { stub_omniauth_config(allow_single_sign_on: []) } + before do + stub_omniauth_config(allow_single_sign_on: []) + end + it 'throws an error' do expect{ oauth_user.save }.to raise_error StandardError end end context 'with old allow_single_sign_on disabled (Default)' do - before { stub_omniauth_config(allow_single_sign_on: false) } + before do + stub_omniauth_config(allow_single_sign_on: false) + end + it 'throws an error' do expect{ oauth_user.save }.to raise_error StandardError end @@ -153,21 +163,30 @@ describe Gitlab::OAuth::User, lib: true do end context "with auto_link_ldap_user disabled (default)" do - before { stub_omniauth_config(auto_link_ldap_user: false) } + before do + stub_omniauth_config(auto_link_ldap_user: false) + end + include_examples "to verify compliance with allow_single_sign_on" end context "with auto_link_ldap_user enabled" do - before { stub_omniauth_config(auto_link_ldap_user: true) } + before do + stub_omniauth_config(auto_link_ldap_user: true) + end context "and no LDAP provider defined" do - before { stub_ldap_config(providers: []) } + before do + stub_ldap_config(providers: []) + end include_examples "to verify compliance with allow_single_sign_on" end context "and at least one LDAP provider is defined" do - before { stub_ldap_config(providers: %w(ldapmain)) } + before do + stub_ldap_config(providers: %w(ldapmain)) + end context "and a corresponding LDAP person" do before do @@ -238,7 +257,9 @@ describe Gitlab::OAuth::User, lib: true do end context "and no corresponding LDAP person" do - before { allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(nil) } + before do + allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(nil) + end include_examples "to verify compliance with allow_single_sign_on" end @@ -248,11 +269,16 @@ describe Gitlab::OAuth::User, lib: true do describe 'blocking' do let(:provider) { 'twitter' } - before { stub_omniauth_config(allow_single_sign_on: ['twitter']) } + + before do + stub_omniauth_config(allow_single_sign_on: ['twitter']) + end context 'signup with omniauth only' do context 'dont block on create' do - before { stub_omniauth_config(block_auto_created_users: false) } + before do + stub_omniauth_config(block_auto_created_users: false) + end it do oauth_user.save @@ -262,7 +288,9 @@ describe Gitlab::OAuth::User, lib: true do end context 'block on create' do - before { stub_omniauth_config(block_auto_created_users: true) } + before do + stub_omniauth_config(block_auto_created_users: true) + end it do oauth_user.save @@ -284,7 +312,9 @@ describe Gitlab::OAuth::User, lib: true do context "and no account for the LDAP user" do context 'dont block on create (LDAP)' do - before { allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: false) } + before do + allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: false) + end it do oauth_user.save @@ -294,7 +324,9 @@ describe Gitlab::OAuth::User, lib: true do end context 'block on create (LDAP)' do - before { allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: true) } + before do + allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: true) + end it do oauth_user.save @@ -308,7 +340,9 @@ describe Gitlab::OAuth::User, lib: true do let!(:existing_user) { create(:omniauth_user, email: 'john@example.com', extern_uid: 'uid=user1,ou=People,dc=example', provider: 'ldapmain', username: 'john') } context 'dont block on create (LDAP)' do - before { allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: false) } + before do + allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: false) + end it do oauth_user.save @@ -318,7 +352,9 @@ describe Gitlab::OAuth::User, lib: true do end context 'block on create (LDAP)' do - before { allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: true) } + before do + allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: true) + end it do oauth_user.save @@ -336,7 +372,9 @@ describe Gitlab::OAuth::User, lib: true do end context 'dont block on create' do - before { stub_omniauth_config(block_auto_created_users: false) } + before do + stub_omniauth_config(block_auto_created_users: false) + end it do oauth_user.save @@ -346,7 +384,9 @@ describe Gitlab::OAuth::User, lib: true do end context 'block on create' do - before { stub_omniauth_config(block_auto_created_users: true) } + before do + stub_omniauth_config(block_auto_created_users: true) + end it do oauth_user.save @@ -356,7 +396,9 @@ describe Gitlab::OAuth::User, lib: true do end context 'dont block on create (LDAP)' do - before { allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: false) } + before do + allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: false) + end it do oauth_user.save @@ -366,7 +408,9 @@ describe Gitlab::OAuth::User, lib: true do end context 'block on create (LDAP)' do - before { allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: true) } + before do + allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: true) + end it do oauth_user.save diff --git a/spec/lib/gitlab/redis_spec.rb b/spec/lib/gitlab/redis_spec.rb index 8b77c925705..593aa5038ad 100644 --- a/spec/lib/gitlab/redis_spec.rb +++ b/spec/lib/gitlab/redis_spec.rb @@ -108,11 +108,18 @@ describe Gitlab::Redis do end describe '.with' do - before { clear_pool } - after { clear_pool } + before do + clear_pool + end + + after do + clear_pool + end context 'when running not on sidekiq workers' do - before { allow(Sidekiq).to receive(:server?).and_return(false) } + before do + allow(Sidekiq).to receive(:server?).and_return(false) + end it 'instantiates a connection pool with size 5' do expect(ConnectionPool).to receive(:new).with(size: 5).and_call_original diff --git a/spec/lib/gitlab/saml/user_spec.rb b/spec/lib/gitlab/saml/user_spec.rb index b106d156b75..a4d2367b72a 100644 --- a/spec/lib/gitlab/saml/user_spec.rb +++ b/spec/lib/gitlab/saml/user_spec.rb @@ -31,11 +31,17 @@ describe Gitlab::Saml::User, lib: true do allow(Gitlab::Saml::Config).to receive_messages({ options: { name: 'saml', groups_attribute: 'groups', external_groups: groups, args: {} } }) end - before { stub_basic_saml_config } + before do + stub_basic_saml_config + end describe 'account exists on server' do - before { stub_omniauth_config({ allow_single_sign_on: ['saml'], auto_link_saml_user: true }) } + before do + stub_omniauth_config({ allow_single_sign_on: ['saml'], auto_link_saml_user: true }) + end + let!(:existing_user) { create(:user, email: 'john@mail.com', username: 'john') } + context 'and should bind with SAML' do it 'adds the SAML identity to the existing user' do saml_user.save @@ -57,7 +63,10 @@ describe Gitlab::Saml::User, lib: true do end end - before { stub_saml_group_config(%w(Interns)) } + before do + stub_saml_group_config(%w(Interns)) + end + context 'are defined but the user does not belong there' do it 'does not mark the user as external' do saml_user.save @@ -80,7 +89,9 @@ describe Gitlab::Saml::User, lib: true do describe 'no account exists on server' do shared_examples 'to verify compliance with allow_single_sign_on' do context 'with allow_single_sign_on enabled' do - before { stub_omniauth_config(allow_single_sign_on: ['saml']) } + before do + stub_omniauth_config(allow_single_sign_on: ['saml']) + end it 'creates a user from SAML' do saml_user.save @@ -93,14 +104,20 @@ describe Gitlab::Saml::User, lib: true do end context 'with allow_single_sign_on default (["saml"])' do - before { stub_omniauth_config(allow_single_sign_on: ['saml']) } + before do + stub_omniauth_config(allow_single_sign_on: ['saml']) + end + it 'does not throw an error' do expect{ saml_user.save }.not_to raise_error end end context 'with allow_single_sign_on disabled' do - before { stub_omniauth_config(allow_single_sign_on: false) } + before do + stub_omniauth_config(allow_single_sign_on: false) + end + it 'throws an error' do expect{ saml_user.save }.to raise_error StandardError end @@ -128,15 +145,22 @@ describe Gitlab::Saml::User, lib: true do end context 'with auto_link_ldap_user disabled (default)' do - before { stub_omniauth_config({ auto_link_ldap_user: false, auto_link_saml_user: false, allow_single_sign_on: ['saml'] }) } + before do + stub_omniauth_config({ auto_link_ldap_user: false, auto_link_saml_user: false, allow_single_sign_on: ['saml'] }) + end + include_examples 'to verify compliance with allow_single_sign_on' end context 'with auto_link_ldap_user enabled' do - before { stub_omniauth_config({ auto_link_ldap_user: true, auto_link_saml_user: false }) } + before do + stub_omniauth_config({ auto_link_ldap_user: true, auto_link_saml_user: false }) + end context 'and at least one LDAP provider is defined' do - before { stub_ldap_config(providers: %w(ldapmain)) } + before do + stub_ldap_config(providers: %w(ldapmain)) + end context 'and a corresponding LDAP person' do before do @@ -239,11 +263,15 @@ describe Gitlab::Saml::User, lib: true do end describe 'blocking' do - before { stub_omniauth_config({ allow_single_sign_on: ['saml'], auto_link_saml_user: true }) } + before do + stub_omniauth_config({ allow_single_sign_on: ['saml'], auto_link_saml_user: true }) + end context 'signup with SAML only' do context 'dont block on create' do - before { stub_omniauth_config(block_auto_created_users: false) } + before do + stub_omniauth_config(block_auto_created_users: false) + end it 'does not block the user' do saml_user.save @@ -253,7 +281,9 @@ describe Gitlab::Saml::User, lib: true do end context 'block on create' do - before { stub_omniauth_config(block_auto_created_users: true) } + before do + stub_omniauth_config(block_auto_created_users: true) + end it 'blocks user' do saml_user.save @@ -270,7 +300,9 @@ describe Gitlab::Saml::User, lib: true do end context 'dont block on create' do - before { stub_omniauth_config(block_auto_created_users: false) } + before do + stub_omniauth_config(block_auto_created_users: false) + end it do saml_user.save @@ -280,7 +312,9 @@ describe Gitlab::Saml::User, lib: true do end context 'block on create' do - before { stub_omniauth_config(block_auto_created_users: true) } + before do + stub_omniauth_config(block_auto_created_users: true) + end it do saml_user.save diff --git a/spec/lib/gitlab/serializer/pagination_spec.rb b/spec/lib/gitlab/serializer/pagination_spec.rb index 519eb1b274f..1bc6536439e 100644 --- a/spec/lib/gitlab/serializer/pagination_spec.rb +++ b/spec/lib/gitlab/serializer/pagination_spec.rb @@ -22,7 +22,9 @@ describe Gitlab::Serializer::Pagination do let(:params) { { page: 1, per_page: 2 } } context 'when a multiple resources are present in relation' do - before { create_list(:user, 3) } + before do + create_list(:user, 3) + end it 'correctly paginates the resource' do expect(subject.count).to be 2 diff --git a/spec/lib/gitlab/template/issue_template_spec.rb b/spec/lib/gitlab/template/issue_template_spec.rb index 329d1d74970..bf45c8d16d6 100644 --- a/spec/lib/gitlab/template/issue_template_spec.rb +++ b/spec/lib/gitlab/template/issue_template_spec.rb @@ -52,7 +52,10 @@ describe Gitlab::Template::IssueTemplate do context 'when repo is bare or empty' do let(:empty_project) { create(:empty_project) } - before { empty_project.add_user(user, Gitlab::Access::MASTER) } + + before do + empty_project.add_user(user, Gitlab::Access::MASTER) + end it "returns empty array" do templates = subject.by_category('', empty_project) @@ -77,7 +80,9 @@ describe Gitlab::Template::IssueTemplate do context "when repo is empty" do let(:empty_project) { create(:empty_project) } - before { empty_project.add_user(user, Gitlab::Access::MASTER) } + before do + empty_project.add_user(user, Gitlab::Access::MASTER) + end it "raises file not found" do issue_template = subject.new('.gitlab/issue_templates/not_existent.md', empty_project) diff --git a/spec/lib/gitlab/template/merge_request_template_spec.rb b/spec/lib/gitlab/template/merge_request_template_spec.rb index 2b0056d9bab..8479f92c8df 100644 --- a/spec/lib/gitlab/template/merge_request_template_spec.rb +++ b/spec/lib/gitlab/template/merge_request_template_spec.rb @@ -52,7 +52,10 @@ describe Gitlab::Template::MergeRequestTemplate do context 'when repo is bare or empty' do let(:empty_project) { create(:empty_project) } - before { empty_project.add_user(user, Gitlab::Access::MASTER) } + + before do + empty_project.add_user(user, Gitlab::Access::MASTER) + end it "returns empty array" do templates = subject.by_category('', empty_project) @@ -77,7 +80,9 @@ describe Gitlab::Template::MergeRequestTemplate do context "when repo is empty" do let(:empty_project) { create(:empty_project) } - before { empty_project.add_user(user, Gitlab::Access::MASTER) } + before do + empty_project.add_user(user, Gitlab::Access::MASTER) + end it "raises file not found" do issue_template = subject.new('.gitlab/merge_request_templates/not_existent.md', empty_project) diff --git a/spec/lib/json_web_token/rsa_token_spec.rb b/spec/lib/json_web_token/rsa_token_spec.rb index 18726754517..e7022bd06f8 100644 --- a/spec/lib/json_web_token/rsa_token_spec.rb +++ b/spec/lib/json_web_token/rsa_token_spec.rb @@ -15,11 +15,15 @@ describe JSONWebToken::RSAToken do let(:rsa_token) { described_class.new(nil) } let(:rsa_encoded) { rsa_token.encoded } - before { allow_any_instance_of(described_class).to receive(:key).and_return(rsa_key) } + before do + allow_any_instance_of(described_class).to receive(:key).and_return(rsa_key) + end context 'token' do context 'for valid key to be validated' do - before { rsa_token['key'] = 'value' } + before do + rsa_token['key'] = 'value' + end subject { JWT.decode(rsa_encoded, rsa_key) } diff --git a/spec/lib/json_web_token/token_spec.rb b/spec/lib/json_web_token/token_spec.rb index 3d955e4d774..d7e7560d962 100644 --- a/spec/lib/json_web_token/token_spec.rb +++ b/spec/lib/json_web_token/token_spec.rb @@ -3,7 +3,10 @@ describe JSONWebToken::Token do context 'custom parameters' do let(:value) { 'value' } - before { token[:key] = value } + + before do + token[:key] = value + end it { expect(token[:key]).to eq(value) } it { expect(token.payload).to include(key: value) } diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index ec6f6c42eac..980b24370d0 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -130,8 +130,13 @@ describe Notify do end context 'with a preferred language' do - before { Gitlab::I18n.locale = :es } - after { Gitlab::I18n.use_default_locale } + before do + Gitlab::I18n.locale = :es + end + + after do + Gitlab::I18n.use_default_locale + end it 'always generates the email using the default language' do is_expected.to have_body_text('foo, bar, and baz') @@ -581,7 +586,9 @@ describe Notify do let(:project) { create(:project, :repository) } let(:commit) { project.commit } - before(:each) { allow(note).to receive(:noteable).and_return(commit) } + before do + allow(note).to receive(:noteable).and_return(commit) + end subject { described_class.note_commit_email(recipient.id, note.id) } @@ -603,7 +610,10 @@ describe Notify do describe 'on a merge request' do let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } let(:note_on_merge_request_path) { namespace_project_merge_request_path(project.namespace, project, merge_request, anchor: "note_#{note.id}") } - before(:each) { allow(note).to receive(:noteable).and_return(merge_request) } + + before do + allow(note).to receive(:noteable).and_return(merge_request) + end subject { described_class.note_merge_request_email(recipient.id, note.id) } @@ -625,7 +635,10 @@ describe Notify do describe 'on an issue' do let(:issue) { create(:issue, project: project) } let(:note_on_issue_path) { namespace_project_issue_path(project.namespace, project, issue, anchor: "note_#{note.id}") } - before(:each) { allow(note).to receive(:noteable).and_return(issue) } + + before do + allow(note).to receive(:noteable).and_return(issue) + end subject { described_class.note_issue_email(recipient.id, note.id) } @@ -687,7 +700,9 @@ describe Notify do let(:commit) { project.commit } let(:note) { create(:discussion_note_on_commit, commit_id: commit.id, project: project, author: note_author) } - before(:each) { allow(note).to receive(:noteable).and_return(commit) } + before do + allow(note).to receive(:noteable).and_return(commit) + end subject { described_class.note_commit_email(recipient.id, note.id) } @@ -711,7 +726,10 @@ describe Notify do let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } let(:note) { create(:discussion_note_on_merge_request, noteable: merge_request, project: project, author: note_author) } let(:note_on_merge_request_path) { namespace_project_merge_request_path(project.namespace, project, merge_request, anchor: "note_#{note.id}") } - before(:each) { allow(note).to receive(:noteable).and_return(merge_request) } + + before do + allow(note).to receive(:noteable).and_return(merge_request) + end subject { described_class.note_merge_request_email(recipient.id, note.id) } @@ -735,7 +753,10 @@ describe Notify do let(:issue) { create(:issue, project: project) } let(:note) { create(:discussion_note_on_issue, noteable: issue, project: project, author: note_author) } let(:note_on_issue_path) { namespace_project_issue_path(project.namespace, project, issue, anchor: "note_#{note.id}") } - before(:each) { allow(note).to receive(:noteable).and_return(issue) } + + before do + allow(note).to receive(:noteable).and_return(issue) + end subject { described_class.note_issue_email(recipient.id, note.id) } diff --git a/spec/migrations/rename_more_reserved_project_names_spec.rb b/spec/migrations/rename_more_reserved_project_names_spec.rb index 36e82729c23..4bd8d4ac0d1 100644 --- a/spec/migrations/rename_more_reserved_project_names_spec.rb +++ b/spec/migrations/rename_more_reserved_project_names_spec.rb @@ -17,7 +17,9 @@ describe RenameMoreReservedProjectNames, truncate: true do describe '#up' do context 'when project repository exists' do - before { project.create_repository } + before do + project.create_repository + end context 'when no exception is raised' do it 'renames project with reserved names' do diff --git a/spec/migrations/rename_reserved_project_names_spec.rb b/spec/migrations/rename_reserved_project_names_spec.rb index 4fb7ed36884..05e021c2e32 100644 --- a/spec/migrations/rename_reserved_project_names_spec.rb +++ b/spec/migrations/rename_reserved_project_names_spec.rb @@ -17,7 +17,9 @@ describe RenameReservedProjectNames, truncate: true do describe '#up' do context 'when project repository exists' do - before { project.create_repository } + before do + project.create_repository + end context 'when no exception is raised' do it 'renames project with reserved names' do diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index fa229542f70..166a4474abf 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -78,7 +78,9 @@ describe ApplicationSetting, models: true do # Upgraded databases will have this sort of content context 'repository_storages is a String, not an Array' do - before { setting.__send__(:raw_write_attribute, :repository_storages, 'default') } + before do + setting.__send__(:raw_write_attribute, :repository_storages, 'default') + end it { expect(setting.repository_storages_before_type_cast).to eq('default') } it { expect(setting.repository_storages).to eq(['default']) } diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index b0716e04d3d..3816422fec6 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -21,6 +21,18 @@ describe Ci::Build, :models do it { is_expected.to respond_to(:has_trace?) } it { is_expected.to respond_to(:trace) } + describe '.manual_actions' do + let!(:manual_but_created) { create(:ci_build, :manual, status: :created, pipeline: pipeline) } + let!(:manual_but_succeeded) { create(:ci_build, :manual, status: :success, pipeline: pipeline) } + let!(:manual_action) { create(:ci_build, :manual, pipeline: pipeline) } + + subject { described_class.manual_actions } + + it { is_expected.to include(manual_action) } + it { is_expected.to include(manual_but_succeeded) } + it { is_expected.not_to include(manual_but_created) } + end + describe '#actionize' do context 'when build is a created' do before do @@ -95,12 +107,18 @@ describe Ci::Build, :models do it { is_expected.to be_truthy } context 'is expired' do - before { build.update(artifacts_expire_at: Time.now - 7.days) } + before do + build.update(artifacts_expire_at: Time.now - 7.days) + end + it { is_expected.to be_falsy } end context 'is not expired' do - before { build.update(artifacts_expire_at: Time.now + 7.days) } + before do + build.update(artifacts_expire_at: Time.now + 7.days) + end + it { is_expected.to be_truthy } end end @@ -110,13 +128,17 @@ describe Ci::Build, :models do subject { build.artifacts_expired? } context 'is expired' do - before { build.update(artifacts_expire_at: Time.now - 7.days) } + before do + build.update(artifacts_expire_at: Time.now - 7.days) + end it { is_expected.to be_truthy } end context 'is not expired' do - before { build.update(artifacts_expire_at: Time.now + 7.days) } + before do + build.update(artifacts_expire_at: Time.now + 7.days) + end it { is_expected.to be_falsey } end @@ -141,7 +163,9 @@ describe Ci::Build, :models do context 'when artifacts_expire_at is specified' do let(:expire_at) { Time.now + 7.days } - before { build.artifacts_expire_at = expire_at } + before do + build.artifacts_expire_at = expire_at + end it { is_expected.to be_within(5).of(expire_at - Time.now) } end @@ -926,6 +950,10 @@ describe Ci::Build, :models do context 'when other build is retried' do let!(:retried_build) { Ci::Build.retry(other_build, user) } + before do + retried_build.success + end + it 'returns a retried build' do is_expected.to contain_exactly(retried_build) end @@ -1071,7 +1099,9 @@ describe Ci::Build, :models do describe '#has_expiring_artifacts?' do context 'when artifacts have expiration date set' do - before { build.update(artifacts_expire_at: 1.day.from_now) } + before do + build.update(artifacts_expire_at: 1.day.from_now) + end it 'has expiring artifacts' do expect(build).to have_expiring_artifacts @@ -1079,7 +1109,9 @@ describe Ci::Build, :models do end context 'when artifacts do not have expiration date set' do - before { build.update(artifacts_expire_at: nil) } + before do + build.update(artifacts_expire_at: nil) + end it 'does not have expiring artifacts' do expect(build).not_to have_expiring_artifacts diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index b50c7700bd3..e86cbe8498a 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -1156,7 +1156,9 @@ describe Ci::Pipeline, models: true do end context 'when pipeline is not stuck' do - before { create(:ci_runner, :shared, :online) } + before do + create(:ci_runner, :shared, :online) + end it 'is not stuck' do expect(pipeline).not_to be_stuck diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index ba247dcc5cf..6056d78da4e 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -206,19 +206,25 @@ eos it { expect(commit.reverts_commit?(another_commit, user)).to be_falsy } context 'commit has no description' do - before { allow(commit).to receive(:description?).and_return(false) } + before do + allow(commit).to receive(:description?).and_return(false) + end it { expect(commit.reverts_commit?(another_commit, user)).to be_falsy } end context "another_commit's description does not revert commit" do - before { allow(commit).to receive(:description).and_return("Foo Bar") } + before do + allow(commit).to receive(:description).and_return("Foo Bar") + end it { expect(commit.reverts_commit?(another_commit, user)).to be_falsy } end context "another_commit's description reverts commit" do - before { allow(commit).to receive(:description).and_return("Foo #{another_commit.revert_description} Bar") } + before do + allow(commit).to receive(:description).and_return("Foo #{another_commit.revert_description} Bar") + end it { expect(commit.reverts_commit?(another_commit, user)).to be_truthy } end diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb index c50b8bf7b13..9262ce08987 100644 --- a/spec/models/commit_status_spec.rb +++ b/spec/models/commit_status_spec.rb @@ -31,7 +31,10 @@ describe CommitStatus, :models do describe '#author' do subject { commit_status.author } - before { commit_status.author = User.new } + + before do + commit_status.author = User.new + end it { is_expected.to eq(commit_status.user) } end @@ -50,14 +53,18 @@ describe CommitStatus, :models do subject { commit_status.started? } context 'without started_at' do - before { commit_status.started_at = nil } + before do + commit_status.started_at = nil + end it { is_expected.to be_falsey } end %w[running success failed].each do |status| context "if commit status is #{status}" do - before { commit_status.status = status } + before do + commit_status.status = status + end it { is_expected.to be_truthy } end @@ -65,7 +72,9 @@ describe CommitStatus, :models do %w[pending canceled].each do |status| context "if commit status is #{status}" do - before { commit_status.status = status } + before do + commit_status.status = status + end it { is_expected.to be_falsey } end @@ -77,7 +86,9 @@ describe CommitStatus, :models do %w[pending running].each do |state| context "if commit_status.status is #{state}" do - before { commit_status.status = state } + before do + commit_status.status = state + end it { is_expected.to be_truthy } end @@ -85,7 +96,9 @@ describe CommitStatus, :models do %w[success failed canceled].each do |state| context "if commit_status.status is #{state}" do - before { commit_status.status = state } + before do + commit_status.status = state + end it { is_expected.to be_falsey } end @@ -97,7 +110,9 @@ describe CommitStatus, :models do %w[success failed canceled].each do |state| context "if commit_status.status is #{state}" do - before { commit_status.status = state } + before do + commit_status.status = state + end it { is_expected.to be_truthy } end @@ -105,7 +120,9 @@ describe CommitStatus, :models do %w[pending running].each do |state| context "if commit_status.status is #{state}" do - before { commit_status.status = state } + before do + commit_status.status = state + end it { is_expected.to be_falsey } end @@ -271,7 +288,9 @@ describe CommitStatus, :models do subject { commit_status.before_sha } context 'when no before_sha is set for pipeline' do - before { pipeline.before_sha = nil } + before do + pipeline.before_sha = nil + end it 'returns blank sha' do is_expected.to eq(Gitlab::Git::BLANK_SHA) @@ -280,7 +299,10 @@ describe CommitStatus, :models do context 'for before_sha set for pipeline' do let(:value) { '1234' } - before { pipeline.before_sha = value } + + before do + pipeline.before_sha = value + end it 'returns the set value' do is_expected.to eq(value) diff --git a/spec/models/concerns/access_requestable_spec.rb b/spec/models/concerns/access_requestable_spec.rb index 4829ef17a20..97b7e48bb3c 100644 --- a/spec/models/concerns/access_requestable_spec.rb +++ b/spec/models/concerns/access_requestable_spec.rb @@ -14,7 +14,9 @@ describe AccessRequestable do let(:group) { create(:group, :public, :access_requestable) } let(:user) { create(:user) } - before { group.request_access(user) } + before do + group.request_access(user) + end it { expect(group.requesters.exists?(user_id: user)).to be_truthy } end @@ -32,7 +34,9 @@ describe AccessRequestable do let(:project) { create(:empty_project, :public, :access_requestable) } let(:user) { create(:user) } - before { project.request_access(user) } + before do + project.request_access(user) + end it { expect(project.requesters.exists?(user_id: user)).to be_truthy } end diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index 27890e33b49..1a9bda64191 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -200,7 +200,9 @@ describe Issuable do let(:project) { issue.project } context 'user is not a participant in the issue' do - before { allow(issue).to receive(:participants).with(user).and_return([]) } + before do + allow(issue).to receive(:participants).with(user).and_return([]) + end it 'returns false when no subcription exists' do expect(issue.subscribed?(user, project)).to be_falsey @@ -220,7 +222,9 @@ describe Issuable do end context 'user is a participant in the issue' do - before { allow(issue).to receive(:participants).with(user).and_return([user]) } + before do + allow(issue).to receive(:participants).with(user).and_return([user]) + end it 'returns false when no subcription exists' do expect(issue.subscribed?(user, project)).to be_truthy @@ -252,7 +256,9 @@ describe Issuable do end context "issue is assigned" do - before { issue.assignees << user } + before do + issue.assignees << user + end it "returns correct hook data" do expect(data[:assignees].first).to eq(user.hook_attrs) @@ -276,7 +282,9 @@ describe Issuable do context 'issue has labels' do let(:labels) { [create(:label), create(:label)] } - before { issue.update_attribute(:labels, labels)} + before do + issue.update_attribute(:labels, labels) + end it 'includes labels in the hook data' do expect(data[:labels]).to eq(labels.map(&:hook_attrs)) diff --git a/spec/models/concerns/mentionable_spec.rb b/spec/models/concerns/mentionable_spec.rb index e382c7120de..e2a29e0ae70 100644 --- a/spec/models/concerns/mentionable_spec.rb +++ b/spec/models/concerns/mentionable_spec.rb @@ -61,7 +61,9 @@ describe Issue, "Mentionable" do end context 'when the current user can see the issue' do - before { private_project.team << [user, Gitlab::Access::DEVELOPER] } + before do + private_project.team << [user, Gitlab::Access::DEVELOPER] + end it 'includes the reference' do expect(referenced_issues(user)).to contain_exactly(private_issue, public_issue) diff --git a/spec/models/concerns/reactive_caching_spec.rb b/spec/models/concerns/reactive_caching_spec.rb index a0765a264cf..808247ebfd5 100644 --- a/spec/models/concerns/reactive_caching_spec.rb +++ b/spec/models/concerns/reactive_caching_spec.rb @@ -40,7 +40,10 @@ describe ReactiveCaching, caching: true do let(:instance) { CacheTest.new(666, &calculation) } describe '#with_reactive_cache' do - before { stub_reactive_cache } + before do + stub_reactive_cache + end + subject(:go!) { instance.result } context 'when cache is empty' do @@ -60,12 +63,17 @@ describe ReactiveCaching, caching: true do end context 'when the cache is full' do - before { stub_reactive_cache(instance, 4) } + before do + stub_reactive_cache(instance, 4) + end it { is_expected.to eq(2) } context 'and expired' do - before { invalidate_reactive_cache(instance) } + before do + invalidate_reactive_cache(instance) + end + it { is_expected.to be_nil } end end @@ -84,7 +92,9 @@ describe ReactiveCaching, caching: true do subject(:go!) { instance.exclusively_update_reactive_cache! } context 'when the lease is free and lifetime is not exceeded' do - before { stub_reactive_cache(instance, "preexisting") } + before do + stub_reactive_cache(instance, "preexisting") + end it 'takes and releases the lease' do expect_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).and_return("000000") @@ -106,7 +116,10 @@ describe ReactiveCaching, caching: true do end context 'and #calculate_reactive_cache raises an exception' do - before { stub_reactive_cache(instance, "preexisting") } + before do + stub_reactive_cache(instance, "preexisting") + end + let(:calculation) { -> { raise "foo"} } it 'leaves the cache untouched' do diff --git a/spec/models/concerns/token_authenticatable_spec.rb b/spec/models/concerns/token_authenticatable_spec.rb index 4b0bfa43abf..882afeccfc6 100644 --- a/spec/models/concerns/token_authenticatable_spec.rb +++ b/spec/models/concerns/token_authenticatable_spec.rb @@ -49,7 +49,10 @@ describe ApplicationSetting, 'TokenAuthenticatable' do end context 'token is generated' do - before { subject.send("reset_#{token_field}!") } + before do + subject.send("reset_#{token_field}!") + end + it 'persists a new token' do expect(subject.send(:read_attribute, token_field)).to be_a String end diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb index 6f0d2db23c7..aad215d5f41 100644 --- a/spec/models/deployment_spec.rb +++ b/spec/models/deployment_spec.rb @@ -102,7 +102,7 @@ describe Deployment, models: true do end context 'with other actions' do - let!(:close_action) { create(:ci_build, pipeline: build.pipeline, name: 'close_app', when: :manual) } + let!(:close_action) { create(:ci_build, :manual, pipeline: build.pipeline, name: 'close_app') } context 'when matching action is defined' do let(:deployment) { FactoryGirl.build(:deployment, deployable: build, on_stop: 'close_other_app') } @@ -130,7 +130,7 @@ describe Deployment, models: true do context 'when matching action is defined' do let(:build) { create(:ci_build) } let(:deployment) { FactoryGirl.build(:deployment, deployable: build, on_stop: 'close_app') } - let!(:close_action) { create(:ci_build, pipeline: build.pipeline, name: 'close_app', when: :manual) } + let!(:close_action) { create(:ci_build, :manual, pipeline: build.pipeline, name: 'close_app') } it { is_expected.to be_truthy } end diff --git a/spec/models/diff_viewer/base_spec.rb b/spec/models/diff_viewer/base_spec.rb new file mode 100644 index 00000000000..3755f4a56f3 --- /dev/null +++ b/spec/models/diff_viewer/base_spec.rb @@ -0,0 +1,150 @@ +require 'spec_helper' + +describe DiffViewer::Base, model: true do + include FakeBlobHelpers + + let(:project) { create(:project, :repository) } + let(:commit) { project.commit('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') } + let(:diff_file) { commit.diffs.diff_file_with_new_path('files/ruby/popen.rb') } + + let(:viewer_class) do + Class.new(described_class) do + include DiffViewer::ServerSide + + self.extensions = %w(jpg) + self.binary = true + self.collapse_limit = 1.megabyte + self.size_limit = 5.megabytes + end + end + + let(:viewer) { viewer_class.new(diff_file) } + + describe '.can_render?' do + context 'when the extension is supported' do + let(:commit) { project.commit('2f63565e7aac07bcdadb654e253078b727143ec4') } + let(:diff_file) { commit.diffs.diff_file_with_new_path('files/images/6049019_460s.jpg') } + + context 'when the binaryness matches' do + it 'returns true' do + expect(viewer_class.can_render?(diff_file)).to be_truthy + end + end + + context 'when the binaryness does not match' do + before do + allow(diff_file.old_blob).to receive(:binary?).and_return(false) + allow(diff_file.new_blob).to receive(:binary?).and_return(false) + end + + it 'returns false' do + expect(viewer_class.can_render?(diff_file)).to be_falsey + end + end + end + + context 'when the file type is supported' do + let(:commit) { project.commit('1a0b36b3cdad1d2ee32457c102a8c0b7056fa863') } + let(:diff_file) { commit.diffs.diff_file_with_new_path('LICENSE') } + + before do + viewer_class.file_types = %i(license) + viewer_class.binary = false + end + + context 'when the binaryness matches' do + it 'returns true' do + expect(viewer_class.can_render?(diff_file)).to be_truthy + end + end + + context 'when the binaryness does not match' do + before do + allow(diff_file.old_blob).to receive(:binary?).and_return(true) + allow(diff_file.new_blob).to receive(:binary?).and_return(true) + end + + it 'returns false' do + expect(viewer_class.can_render?(diff_file)).to be_falsey + end + end + end + + context 'when the extension and file type are not supported' do + it 'returns false' do + expect(viewer_class.can_render?(diff_file)).to be_falsey + end + end + + context 'when the file was renamed and only the old blob is supported' do + let(:commit) { project.commit('2f63565e7aac07bcdadb654e253078b727143ec4') } + let(:diff_file) { commit.diffs.diff_file_with_new_path('files/images/6049019_460s.jpg') } + + before do + allow(diff_file).to receive(:renamed_file?).and_return(true) + allow(diff_file.new_blob).to receive(:extension).and_return('jpeg') + end + + it 'returns false' do + expect(viewer_class.can_render?(diff_file)).to be_falsey + end + end + end + + describe '#collapsed?' do + context 'when the combined blob size is larger than the collapse limit' do + before do + allow(diff_file.old_blob).to receive(:raw_size).and_return(512.kilobytes) + allow(diff_file.new_blob).to receive(:raw_size).and_return(513.kilobytes) + end + + it 'returns true' do + expect(viewer.collapsed?).to be_truthy + end + end + + context 'when the combined blob size is smaller than the collapse limit' do + it 'returns false' do + expect(viewer.collapsed?).to be_falsey + end + end + end + + describe '#too_large?' do + context 'when the combined blob size is larger than the size limit' do + before do + allow(diff_file.old_blob).to receive(:raw_size).and_return(2.megabytes) + allow(diff_file.new_blob).to receive(:raw_size).and_return(4.megabytes) + end + + it 'returns true' do + expect(viewer.too_large?).to be_truthy + end + end + + context 'when the blob size is smaller than the size limit' do + it 'returns false' do + expect(viewer.too_large?).to be_falsey + end + end + end + + describe '#render_error' do + context 'when the combined blob size is larger than the size limit' do + before do + allow(diff_file.old_blob).to receive(:raw_size).and_return(2.megabytes) + allow(diff_file.new_blob).to receive(:raw_size).and_return(4.megabytes) + end + + it 'returns :too_large' do + expect(viewer.render_error).to eq(:too_large) + end + end + + context 'when the combined blob size is smaller than the size limit' do + it 'returns nil' do + expect(viewer.render_error).to be_nil + end + end + end +end diff --git a/spec/models/diff_viewer/server_side_spec.rb b/spec/models/diff_viewer/server_side_spec.rb new file mode 100644 index 00000000000..2d926e06936 --- /dev/null +++ b/spec/models/diff_viewer/server_side_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +describe DiffViewer::ServerSide, model: true do + let(:project) { create(:project, :repository) } + let(:commit) { project.commit('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') } + let(:diff_file) { commit.diffs.diff_file_with_new_path('files/ruby/popen.rb') } + + let(:viewer_class) do + Class.new(DiffViewer::Base) do + include DiffViewer::ServerSide + end + end + + subject { viewer_class.new(diff_file) } + + describe '#prepare!' do + it 'loads all diff file data' do + expect(diff_file.old_blob).to receive(:load_all_data!) + expect(diff_file.new_blob).to receive(:load_all_data!) + + subject.prepare! + end + end + + describe '#render_error' do + context 'when the diff file is stored externally' do + before do + allow(diff_file).to receive(:stored_externally?).and_return(true) + end + + it 'return :server_side_but_stored_externally' do + expect(subject.render_error).to eq(:server_side_but_stored_externally) + end + end + end +end diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb index fe69c8e351d..f8123cb518e 100644 --- a/spec/models/environment_spec.rb +++ b/spec/models/environment_spec.rb @@ -170,7 +170,7 @@ describe Environment, models: true do context 'when matching action is defined' do let(:build) { create(:ci_build) } let!(:deployment) { create(:deployment, environment: environment, deployable: build, on_stop: 'close_app') } - let!(:close_action) { create(:ci_build, pipeline: build.pipeline, name: 'close_app', when: :manual) } + let!(:close_action) { create(:ci_build, :manual, pipeline: build.pipeline, name: 'close_app') } context 'when environment is available' do before do diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb index f4c3e6d503f..152e97e09bf 100644 --- a/spec/models/generic_commit_status_spec.rb +++ b/spec/models/generic_commit_status_spec.rb @@ -19,7 +19,10 @@ describe GenericCommitStatus, models: true do describe '#context' do subject { generic_commit_status.context } - before { generic_commit_status.context = 'my_context' } + + before do + generic_commit_status.context = 'my_context' + end it { is_expected.to eq(generic_commit_status.name) } end @@ -39,7 +42,9 @@ describe GenericCommitStatus, models: true do end context 'when user has ability to see datails' do - before { project.team << [user, :developer] } + before do + project.team << [user, :developer] + end it 'details path points to an external URL' do expect(status).to have_details diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 3d437ca0fcc..449b7c2f7d7 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -143,14 +143,20 @@ describe Group, models: true do describe '#add_user' do let(:user) { create(:user) } - before { group.add_user(user, GroupMember::MASTER) } + + before do + group.add_user(user, GroupMember::MASTER) + end it { expect(group.group_members.masters.map(&:user)).to include(user) } end describe '#add_users' do let(:user) { create(:user) } - before { group.add_users([user.id], GroupMember::GUEST) } + + before do + group.add_users([user.id], GroupMember::GUEST) + end it "updates the group permission" do expect(group.group_members.guests.map(&:user)).to include(user) @@ -162,7 +168,10 @@ describe Group, models: true do describe '#avatar_type' do let(:user) { create(:user) } - before { group.add_user(user, GroupMember::MASTER) } + + before do + group.add_user(user, GroupMember::MASTER) + end it "is true if avatar is image" do group.update_attribute(:avatar, 'uploads/avatar.png') @@ -182,7 +191,9 @@ describe Group, models: true do let(:avatar_path) { "/uploads/system/group/avatar/#{group.id}/dk.png" } context 'when avatar file is uploaded' do - before { group.add_master(user) } + before do + group.add_master(user) + end it 'shows correct avatar url' do expect(group.avatar_url).to eq(avatar_path) @@ -222,7 +233,9 @@ describe Group, models: true do end describe '#has_owner?' do - before { @members = setup_group_members(group) } + before do + @members = setup_group_members(group) + end it { expect(group.has_owner?(@members[:owner])).to be_truthy } it { expect(group.has_owner?(@members[:master])).to be_falsey } @@ -233,7 +246,9 @@ describe Group, models: true do end describe '#has_master?' do - before { @members = setup_group_members(group) } + before do + @members = setup_group_members(group) + end it { expect(group.has_master?(@members[:owner])).to be_falsey } it { expect(group.has_master?(@members[:master])).to be_truthy } diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index bb4e70db2e9..12e7d646382 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -245,7 +245,9 @@ describe Issue, models: true do let(:project) { create(:empty_project) } let(:issue) { create(:issue, project: project) } - before { project.team << [user, :reporter] } + before do + project.team << [user, :reporter] + end it { is_expected.to eq true } @@ -259,12 +261,18 @@ describe Issue, models: true do let(:to_project) { create(:empty_project) } context 'destination project allowed' do - before { to_project.team << [user, :reporter] } + before do + to_project.team << [user, :reporter] + end + it { is_expected.to eq true } end context 'destination project not allowed' do - before { to_project.team << [user, :guest] } + before do + to_project.team << [user, :guest] + end + it { is_expected.to eq false } end end @@ -549,7 +557,9 @@ describe Issue, models: true do end context 'when the user is the project owner' do - before { project.team << [user, :master] } + before do + project.team << [user, :master] + end it 'returns true for a regular issue' do issue = build(:issue, project: project) diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb index ed9fde57bf7..25f7062860b 100644 --- a/spec/models/merge_request_diff_spec.rb +++ b/spec/models/merge_request_diff_spec.rb @@ -36,7 +36,9 @@ describe MergeRequestDiff, models: true do end context 'when the raw diffs are empty' do - before { mr_diff.update_attributes(st_diffs: '') } + before do + mr_diff.update_attributes(st_diffs: '') + end it 'returns an empty DiffCollection' do expect(mr_diff.raw_diffs).to be_a(Gitlab::Git::DiffCollection) @@ -45,7 +47,9 @@ describe MergeRequestDiff, models: true do end context 'when the raw diffs have invalid content' do - before { mr_diff.update_attributes(st_diffs: ["--broken-diff"]) } + before do + mr_diff.update_attributes(st_diffs: ["--broken-diff"]) + end it 'returns an empty DiffCollection' do expect(mr_diff.raw_diffs.to_a).to be_empty diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 060754fab63..cd2f11dec96 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -892,7 +892,9 @@ describe MergeRequest, models: true do end context 'when broken' do - before { allow(subject).to receive(:broken?) { true } } + before do + allow(subject).to receive(:broken?) { true } + end it 'becomes unmergeable' do expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('cannot_be_merged') @@ -944,7 +946,9 @@ describe MergeRequest, models: true do end context 'when not open' do - before { subject.close } + before do + subject.close + end it 'returns false' do expect(subject.mergeable_state?).to be_falsey @@ -952,7 +956,9 @@ describe MergeRequest, models: true do end context 'when working in progress' do - before { subject.title = 'WIP MR' } + before do + subject.title = 'WIP MR' + end it 'returns false' do expect(subject.mergeable_state?).to be_falsey @@ -960,7 +966,9 @@ describe MergeRequest, models: true do end context 'when broken' do - before { allow(subject).to receive(:broken?) { true } } + before do + allow(subject).to receive(:broken?) { true } + end it 'returns false' do expect(subject.mergeable_state?).to be_falsey diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 7a01cef9b4b..d4d4fc86343 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -26,14 +26,18 @@ describe Note, models: true do it { is_expected.to validate_presence_of(:project) } context 'when note is on commit' do - before { allow(subject).to receive(:for_commit?).and_return(true) } + before do + allow(subject).to receive(:for_commit?).and_return(true) + end it { is_expected.to validate_presence_of(:commit_id) } it { is_expected.not_to validate_presence_of(:noteable_id) } end context 'when note is not on commit' do - before { allow(subject).to receive(:for_commit?).and_return(false) } + before do + allow(subject).to receive(:for_commit?).and_return(false) + end it { is_expected.not_to validate_presence_of(:commit_id) } it { is_expected.to validate_presence_of(:noteable_id) } diff --git a/spec/models/project_services/bamboo_service_spec.rb b/spec/models/project_services/bamboo_service_spec.rb index 4014d6129ee..e62fd69e567 100644 --- a/spec/models/project_services/bamboo_service_spec.rb +++ b/spec/models/project_services/bamboo_service_spec.rb @@ -24,7 +24,9 @@ describe BambooService, models: true, caching: true do describe 'Validations' do context 'when service is active' do - before { subject.active = true } + before do + subject.active = true + end it { is_expected.to validate_presence_of(:build_key) } it { is_expected.to validate_presence_of(:bamboo_url) } @@ -60,7 +62,9 @@ describe BambooService, models: true, caching: true do end context 'when service is inactive' do - before { subject.active = false } + before do + subject.active = false + end it { is_expected.not_to validate_presence_of(:build_key) } it { is_expected.not_to validate_presence_of(:bamboo_url) } diff --git a/spec/models/project_services/bugzilla_service_spec.rb b/spec/models/project_services/bugzilla_service_spec.rb index 739cc72b2ff..5f17bbde390 100644 --- a/spec/models/project_services/bugzilla_service_spec.rb +++ b/spec/models/project_services/bugzilla_service_spec.rb @@ -8,7 +8,9 @@ describe BugzillaService, models: true do describe 'Validations' do context 'when service is active' do - before { subject.active = true } + before do + subject.active = true + end it { is_expected.to validate_presence_of(:project_url) } it { is_expected.to validate_presence_of(:issues_url) } @@ -19,7 +21,9 @@ describe BugzillaService, models: true do end context 'when service is inactive' do - before { subject.active = false } + before do + subject.active = false + end it { is_expected.not_to validate_presence_of(:project_url) } it { is_expected.not_to validate_presence_of(:issues_url) } diff --git a/spec/models/project_services/buildkite_service_spec.rb b/spec/models/project_services/buildkite_service_spec.rb index 05b602d8106..dd529597067 100644 --- a/spec/models/project_services/buildkite_service_spec.rb +++ b/spec/models/project_services/buildkite_service_spec.rb @@ -23,7 +23,9 @@ describe BuildkiteService, models: true, caching: true do describe 'Validations' do context 'when service is active' do - before { subject.active = true } + before do + subject.active = true + end it { is_expected.to validate_presence_of(:project_url) } it { is_expected.to validate_presence_of(:token) } @@ -31,7 +33,9 @@ describe BuildkiteService, models: true, caching: true do end context 'when service is inactive' do - before { subject.active = false } + before do + subject.active = false + end it { is_expected.not_to validate_presence_of(:project_url) } it { is_expected.not_to validate_presence_of(:token) } diff --git a/spec/models/project_services/campfire_service_spec.rb b/spec/models/project_services/campfire_service_spec.rb index 953e664fb66..de55627dd27 100644 --- a/spec/models/project_services/campfire_service_spec.rb +++ b/spec/models/project_services/campfire_service_spec.rb @@ -8,13 +8,17 @@ describe CampfireService, models: true do describe 'Validations' do context 'when service is active' do - before { subject.active = true } + before do + subject.active = true + end it { is_expected.to validate_presence_of(:token) } end context 'when service is inactive' do - before { subject.active = false } + before do + subject.active = false + end it { is_expected.not_to validate_presence_of(:token) } end diff --git a/spec/models/project_services/chat_message/wiki_page_message_spec.rb b/spec/models/project_services/chat_message/wiki_page_message_spec.rb index 4ca1b8aa7b7..17355c1e6f1 100644 --- a/spec/models/project_services/chat_message/wiki_page_message_spec.rb +++ b/spec/models/project_services/chat_message/wiki_page_message_spec.rb @@ -23,7 +23,9 @@ describe ChatMessage::WikiPageMessage, models: true do context 'without markdown' do describe '#pretext' do context 'when :action == "create"' do - before { args[:object_attributes][:action] = 'create' } + before do + args[:object_attributes][:action] = 'create' + end it 'returns a message that a new wiki page was created' do expect(subject.pretext).to eq( @@ -33,7 +35,9 @@ describe ChatMessage::WikiPageMessage, models: true do end context 'when :action == "update"' do - before { args[:object_attributes][:action] = 'update' } + before do + args[:object_attributes][:action] = 'update' + end it 'returns a message that a wiki page was updated' do expect(subject.pretext).to eq( @@ -47,7 +51,9 @@ describe ChatMessage::WikiPageMessage, models: true do let(:color) { '#345' } context 'when :action == "create"' do - before { args[:object_attributes][:action] = 'create' } + before do + args[:object_attributes][:action] = 'create' + end it 'returns the attachment for a new wiki page' do expect(subject.attachments).to eq([ @@ -60,7 +66,9 @@ describe ChatMessage::WikiPageMessage, models: true do end context 'when :action == "update"' do - before { args[:object_attributes][:action] = 'update' } + before do + args[:object_attributes][:action] = 'update' + end it 'returns the attachment for an updated wiki page' do expect(subject.attachments).to eq([ @@ -81,7 +89,9 @@ describe ChatMessage::WikiPageMessage, models: true do describe '#pretext' do context 'when :action == "create"' do - before { args[:object_attributes][:action] = 'create' } + before do + args[:object_attributes][:action] = 'create' + end it 'returns a message that a new wiki page was created' do expect(subject.pretext).to eq( @@ -90,7 +100,9 @@ describe ChatMessage::WikiPageMessage, models: true do end context 'when :action == "update"' do - before { args[:object_attributes][:action] = 'update' } + before do + args[:object_attributes][:action] = 'update' + end it 'returns a message that a wiki page was updated' do expect(subject.pretext).to eq( @@ -101,7 +113,9 @@ describe ChatMessage::WikiPageMessage, models: true do describe '#attachments' do context 'when :action == "create"' do - before { args[:object_attributes][:action] = 'create' } + before do + args[:object_attributes][:action] = 'create' + end it 'returns the attachment for a new wiki page' do expect(subject.attachments).to eq('Wiki page description') @@ -109,7 +123,9 @@ describe ChatMessage::WikiPageMessage, models: true do end context 'when :action == "update"' do - before { args[:object_attributes][:action] = 'update' } + before do + args[:object_attributes][:action] = 'update' + end it 'returns the attachment for an updated wiki page' do expect(subject.attachments).to eq('Wiki page description') @@ -119,7 +135,9 @@ describe ChatMessage::WikiPageMessage, models: true do describe '#activity' do context 'when :action == "create"' do - before { args[:object_attributes][:action] = 'create' } + before do + args[:object_attributes][:action] = 'create' + end it 'returns the attachment for a new wiki page' do expect(subject.activity).to eq({ @@ -132,7 +150,9 @@ describe ChatMessage::WikiPageMessage, models: true do end context 'when :action == "update"' do - before { args[:object_attributes][:action] = 'update' } + before do + args[:object_attributes][:action] = 'update' + end it 'returns the attachment for an updated wiki page' do expect(subject.activity).to eq({ diff --git a/spec/models/project_services/custom_issue_tracker_service_spec.rb b/spec/models/project_services/custom_issue_tracker_service_spec.rb index 63320931e76..9e574762232 100644 --- a/spec/models/project_services/custom_issue_tracker_service_spec.rb +++ b/spec/models/project_services/custom_issue_tracker_service_spec.rb @@ -8,7 +8,9 @@ describe CustomIssueTrackerService, models: true do describe 'Validations' do context 'when service is active' do - before { subject.active = true } + before do + subject.active = true + end it { is_expected.to validate_presence_of(:project_url) } it { is_expected.to validate_presence_of(:issues_url) } @@ -19,7 +21,9 @@ describe CustomIssueTrackerService, models: true do end context 'when service is inactive' do - before { subject.active = false } + before do + subject.active = false + end it { is_expected.not_to validate_presence_of(:project_url) } it { is_expected.not_to validate_presence_of(:issues_url) } diff --git a/spec/models/project_services/drone_ci_service_spec.rb b/spec/models/project_services/drone_ci_service_spec.rb index 044737c6026..1400175427f 100644 --- a/spec/models/project_services/drone_ci_service_spec.rb +++ b/spec/models/project_services/drone_ci_service_spec.rb @@ -10,7 +10,9 @@ describe DroneCiService, models: true, caching: true do describe 'validations' do context 'active' do - before { subject.active = true } + before do + subject.active = true + end it { is_expected.to validate_presence_of(:token) } it { is_expected.to validate_presence_of(:drone_url) } @@ -18,7 +20,9 @@ describe DroneCiService, models: true, caching: true do end context 'inactive' do - before { subject.active = false } + before do + subject.active = false + end it { is_expected.not_to validate_presence_of(:token) } it { is_expected.not_to validate_presence_of(:drone_url) } diff --git a/spec/models/project_services/emails_on_push_service_spec.rb b/spec/models/project_services/emails_on_push_service_spec.rb index e6f78898c82..d9b7010e5e5 100644 --- a/spec/models/project_services/emails_on_push_service_spec.rb +++ b/spec/models/project_services/emails_on_push_service_spec.rb @@ -3,13 +3,17 @@ require 'spec_helper' describe EmailsOnPushService do describe 'Validations' do context 'when service is active' do - before { subject.active = true } + before do + subject.active = true + end it { is_expected.to validate_presence_of(:recipients) } end context 'when service is inactive' do - before { subject.active = false } + before do + subject.active = false + end it { is_expected.not_to validate_presence_of(:recipients) } end diff --git a/spec/models/project_services/external_wiki_service_spec.rb b/spec/models/project_services/external_wiki_service_spec.rb index bdeea1db1e3..291fc645a1c 100644 --- a/spec/models/project_services/external_wiki_service_spec.rb +++ b/spec/models/project_services/external_wiki_service_spec.rb @@ -9,14 +9,18 @@ describe ExternalWikiService, models: true do describe 'Validations' do context 'when service is active' do - before { subject.active = true } + before do + subject.active = true + end it { is_expected.to validate_presence_of(:external_wiki_url) } it_behaves_like 'issue tracker service URL attribute', :external_wiki_url end context 'when service is inactive' do - before { subject.active = false } + before do + subject.active = false + end it { is_expected.not_to validate_presence_of(:external_wiki_url) } end diff --git a/spec/models/project_services/flowdock_service_spec.rb b/spec/models/project_services/flowdock_service_spec.rb index a97e8c6e4ce..56ace04dd58 100644 --- a/spec/models/project_services/flowdock_service_spec.rb +++ b/spec/models/project_services/flowdock_service_spec.rb @@ -8,13 +8,17 @@ describe FlowdockService, models: true do describe 'Validations' do context 'when service is active' do - before { subject.active = true } + before do + subject.active = true + end it { is_expected.to validate_presence_of(:token) } end context 'when service is inactive' do - before { subject.active = false } + before do + subject.active = false + end it { is_expected.not_to validate_presence_of(:token) } end diff --git a/spec/models/project_services/gemnasium_service_spec.rb b/spec/models/project_services/gemnasium_service_spec.rb index a13fbae03eb..65c9e714bd1 100644 --- a/spec/models/project_services/gemnasium_service_spec.rb +++ b/spec/models/project_services/gemnasium_service_spec.rb @@ -8,14 +8,18 @@ describe GemnasiumService, models: true do describe 'Validations' do context 'when service is active' do - before { subject.active = true } + before do + subject.active = true + end it { is_expected.to validate_presence_of(:token) } it { is_expected.to validate_presence_of(:api_key) } end context 'when service is inactive' do - before { subject.active = false } + before do + subject.active = false + end it { is_expected.not_to validate_presence_of(:token) } it { is_expected.not_to validate_presence_of(:api_key) } diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb index 1200ae7eb22..c7c8e9651ab 100644 --- a/spec/models/project_services/hipchat_service_spec.rb +++ b/spec/models/project_services/hipchat_service_spec.rb @@ -8,13 +8,17 @@ describe HipchatService, models: true do describe 'Validations' do context 'when service is active' do - before { subject.active = true } + before do + subject.active = true + end it { is_expected.to validate_presence_of(:token) } end context 'when service is inactive' do - before { subject.active = false } + before do + subject.active = false + end it { is_expected.not_to validate_presence_of(:token) } end diff --git a/spec/models/project_services/irker_service_spec.rb b/spec/models/project_services/irker_service_spec.rb index d5a16226d9d..a5c4938b54e 100644 --- a/spec/models/project_services/irker_service_spec.rb +++ b/spec/models/project_services/irker_service_spec.rb @@ -10,13 +10,17 @@ describe IrkerService, models: true do describe 'Validations' do context 'when service is active' do - before { subject.active = true } + before do + subject.active = true + end it { is_expected.to validate_presence_of(:recipients) } end context 'when service is inactive' do - before { subject.active = false } + before do + subject.active = false + end it { is_expected.not_to validate_presence_of(:recipients) } end diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index 0ee050196e4..e2b8226124f 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -10,7 +10,9 @@ describe JiraService, models: true do describe 'Validations' do context 'when service is active' do - before { subject.active = true } + before do + subject.active = true + end it { is_expected.to validate_presence_of(:url) } it { is_expected.to validate_presence_of(:project_key) } @@ -18,7 +20,9 @@ describe JiraService, models: true do end context 'when service is inactive' do - before { subject.active = false } + before do + subject.active = false + end it { is_expected.not_to validate_presence_of(:url) } end diff --git a/spec/models/project_services/kubernetes_service_spec.rb b/spec/models/project_services/kubernetes_service_spec.rb index 0dcf4a4b5d6..858ad595dbf 100644 --- a/spec/models/project_services/kubernetes_service_spec.rb +++ b/spec/models/project_services/kubernetes_service_spec.rb @@ -7,31 +7,15 @@ describe KubernetesService, models: true, caching: true do let(:project) { build_stubbed(:kubernetes_project) } let(:service) { project.kubernetes_service } - # We use Kubeclient to interactive with the Kubernetes API. It will - # GET /api/v1 for a list of resources the API supports. This must be stubbed - # in addition to any other HTTP requests we expect it to perform. - let(:discovery_url) { service.api_url + '/api/v1' } - let(:discovery_response) { { body: kube_discovery_body.to_json } } - - let(:pods_url) { service.api_url + "/api/v1/namespaces/#{service.actual_namespace}/pods" } - let(:pods_response) { { body: kube_pods_body(kube_pod).to_json } } - - def stub_kubeclient_discover - WebMock.stub_request(:get, discovery_url).to_return(discovery_response) - end - - def stub_kubeclient_pods - stub_kubeclient_discover - WebMock.stub_request(:get, pods_url).to_return(pods_response) - end - describe "Associations" do it { is_expected.to belong_to :project } end describe 'Validations' do context 'when service is active' do - before { subject.active = true } + before do + subject.active = true + end it { is_expected.not_to validate_presence_of(:namespace) } it { is_expected.to validate_presence_of(:api_url) } @@ -66,7 +50,9 @@ describe KubernetesService, models: true, caching: true do end context 'when service is inactive' do - before { subject.active = false } + before do + subject.active = false + end it { is_expected.not_to validate_presence_of(:api_url) } it { is_expected.not_to validate_presence_of(:token) } @@ -87,7 +73,9 @@ describe KubernetesService, models: true, caching: true do end context 'as template' do - before { subject.template = true } + before do + subject.template = true + end it 'sets the namespace to the default' do expect(kube_namespace).not_to be_nil @@ -96,7 +84,9 @@ describe KubernetesService, models: true, caching: true do end context 'with associated project' do - before { subject.project = project } + before do + subject.project = project + end it 'sets the namespace to the default' do expect(kube_namespace).not_to be_nil @@ -111,6 +101,34 @@ describe KubernetesService, models: true, caching: true do it "returns the default namespace" do is_expected.to eq(service.send(:default_namespace)) end + + context 'when namespace is specified' do + before do + service.namespace = 'my-namespace' + end + + it "returns the user-namespace" do + is_expected.to eq('my-namespace') + end + end + + context 'when service is not assigned to project' do + before do + service.project = nil + end + + it "does not return namespace" do + is_expected.to be_nil + end + end + end + + describe '#actual_namespace' do + subject { service.actual_namespace } + + it "returns the default namespace" do + is_expected.to eq(service.send(:default_namespace)) + end context 'when namespace is specified' do before do @@ -134,6 +152,8 @@ describe KubernetesService, models: true, caching: true do end describe '#test' do + let(:discovery_url) { 'https://kubernetes.example.com/api/v1' } + before do stub_kubeclient_discover end @@ -142,7 +162,8 @@ describe KubernetesService, models: true, caching: true do let(:discovery_url) { 'https://kubernetes.example.com/prefix/api/v1' } it 'tests with the prefix' do - service.api_url = 'https://kubernetes.example.com/prefix/' + service.api_url = 'https://kubernetes.example.com/prefix' + stub_kubeclient_discover expect(service.test[:success]).to be_truthy expect(WebMock).to have_requested(:get, discovery_url).once @@ -170,9 +191,9 @@ describe KubernetesService, models: true, caching: true do end context 'failure' do - let(:discovery_response) { { status: 404 } } - it 'fails to read the discovery endpoint' do + WebMock.stub_request(:get, service.api_url + '/api/v1').to_return(status: 404) + expect(service.test[:success]).to be_falsy expect(WebMock).to have_requested(:get, discovery_url).once end @@ -188,7 +209,9 @@ describe KubernetesService, models: true, caching: true do end context 'namespace is provided' do - before { subject.namespace = 'my-project' } + before do + subject.namespace = 'my-project' + end it 'sets the variables' do expect(subject.predefined_variables).to include( @@ -258,27 +281,36 @@ describe KubernetesService, models: true, caching: true do end describe '#calculate_reactive_cache' do - before { stub_kubeclient_pods } subject { service.calculate_reactive_cache } context 'when service is inactive' do - before { service.active = false } + before do + service.active = false + end it { is_expected.to be_nil } end context 'when kubernetes responds with valid pods' do + before do + stub_kubeclient_pods + end + it { is_expected.to eq(pods: [kube_pod]) } end - context 'when kubernetes responds with 500' do - let(:pods_response) { { status: 500 } } + context 'when kubernetes responds with 500s' do + before do + stub_kubeclient_pods(status: 500) + end it { expect { subject }.to raise_error(KubeException) } end - context 'when kubernetes responds with 404' do - let(:pods_response) { { status: 404 } } + context 'when kubernetes responds with 404s' do + before do + stub_kubeclient_pods(status: 404) + end it { is_expected.to eq(pods: []) } end diff --git a/spec/models/project_services/microsoft_teams_service_spec.rb b/spec/models/project_services/microsoft_teams_service_spec.rb index facc034f69c..bd50a2d1470 100644 --- a/spec/models/project_services/microsoft_teams_service_spec.rb +++ b/spec/models/project_services/microsoft_teams_service_spec.rb @@ -11,14 +11,18 @@ describe MicrosoftTeamsService, models: true do describe 'Validations' do context 'when service is active' do - before { subject.active = true } + before do + subject.active = true + end it { is_expected.to validate_presence_of(:webhook) } it_behaves_like 'issue tracker service URL attribute', :webhook end context 'when service is inactive' do - before { subject.active = false } + before do + subject.active = false + end it { is_expected.not_to validate_presence_of(:webhook) } end diff --git a/spec/models/project_services/pivotaltracker_service_spec.rb b/spec/models/project_services/pivotaltracker_service_spec.rb index a76e909d04d..f4c1a9c94b6 100644 --- a/spec/models/project_services/pivotaltracker_service_spec.rb +++ b/spec/models/project_services/pivotaltracker_service_spec.rb @@ -8,13 +8,17 @@ describe PivotaltrackerService, models: true do describe 'Validations' do context 'when service is active' do - before { subject.active = true } + before do + subject.active = true + end it { is_expected.to validate_presence_of(:token) } end context 'when service is inactive' do - before { subject.active = false } + before do + subject.active = false + end it { is_expected.not_to validate_presence_of(:token) } end diff --git a/spec/models/project_services/prometheus_service_spec.rb b/spec/models/project_services/prometheus_service_spec.rb index 1f9d3c07b51..71b53732164 100644 --- a/spec/models/project_services/prometheus_service_spec.rb +++ b/spec/models/project_services/prometheus_service_spec.rb @@ -14,13 +14,17 @@ describe PrometheusService, models: true, caching: true do describe 'Validations' do context 'when service is active' do - before { subject.active = true } + before do + subject.active = true + end it { is_expected.to validate_presence_of(:api_url) } end context 'when service is inactive' do - before { subject.active = false } + before do + subject.active = false + end it { is_expected.not_to validate_presence_of(:api_url) } end diff --git a/spec/models/project_services/pushover_service_spec.rb b/spec/models/project_services/pushover_service_spec.rb index a7e7594a7d5..9171d9604ee 100644 --- a/spec/models/project_services/pushover_service_spec.rb +++ b/spec/models/project_services/pushover_service_spec.rb @@ -8,7 +8,9 @@ describe PushoverService, models: true do describe 'Validations' do context 'when service is active' do - before { subject.active = true } + before do + subject.active = true + end it { is_expected.to validate_presence_of(:api_key) } it { is_expected.to validate_presence_of(:user_key) } @@ -16,7 +18,9 @@ describe PushoverService, models: true do end context 'when service is inactive' do - before { subject.active = false } + before do + subject.active = false + end it { is_expected.not_to validate_presence_of(:api_key) } it { is_expected.not_to validate_presence_of(:user_key) } diff --git a/spec/models/project_services/redmine_service_spec.rb b/spec/models/project_services/redmine_service_spec.rb index 0a7b237a051..6631d9040b1 100644 --- a/spec/models/project_services/redmine_service_spec.rb +++ b/spec/models/project_services/redmine_service_spec.rb @@ -8,7 +8,9 @@ describe RedmineService, models: true do describe 'Validations' do context 'when service is active' do - before { subject.active = true } + before do + subject.active = true + end it { is_expected.to validate_presence_of(:project_url) } it { is_expected.to validate_presence_of(:issues_url) } @@ -19,7 +21,9 @@ describe RedmineService, models: true do end context 'when service is inactive' do - before { subject.active = false } + before do + subject.active = false + end it { is_expected.not_to validate_presence_of(:project_url) } it { is_expected.not_to validate_presence_of(:issues_url) } diff --git a/spec/models/project_services/teamcity_service_spec.rb b/spec/models/project_services/teamcity_service_spec.rb index 77b18e1c7d0..7349eb4149a 100644 --- a/spec/models/project_services/teamcity_service_spec.rb +++ b/spec/models/project_services/teamcity_service_spec.rb @@ -24,7 +24,9 @@ describe TeamcityService, models: true, caching: true do describe 'Validations' do context 'when service is active' do - before { subject.active = true } + before do + subject.active = true + end it { is_expected.to validate_presence_of(:build_type) } it { is_expected.to validate_presence_of(:teamcity_url) } @@ -60,7 +62,9 @@ describe TeamcityService, models: true, caching: true do end context 'when service is inactive' do - before { subject.active = false } + before do + subject.active = false + end it { is_expected.not_to validate_presence_of(:build_type) } it { is_expected.not_to validate_presence_of(:teamcity_url) } diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 454eeb58ecd..63333b7af1f 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1005,13 +1005,17 @@ describe Project, models: true do subject { project.shared_runners_enabled } context 'are enabled' do - before { stub_application_setting(shared_runners_enabled: true) } + before do + stub_application_setting(shared_runners_enabled: true) + end it { is_expected.to be_truthy } end context 'are disabled' do - before { stub_application_setting(shared_runners_enabled: false) } + before do + stub_application_setting(shared_runners_enabled: false) + end it { is_expected.to be_falsey } end @@ -1107,7 +1111,9 @@ describe Project, models: true do subject { project.pages_deployed? } context 'if public folder does exist' do - before { allow(Dir).to receive(:exist?).with(project.public_pages_path).and_return(true) } + before do + allow(Dir).to receive(:exist?).with(project.public_pages_path).and_return(true) + end it { is_expected.to be_truthy } end @@ -1365,7 +1371,9 @@ describe Project, models: true do subject { project.container_registry_url } - before { stub_container_registry_config(**registry_settings) } + before do + stub_container_registry_config(**registry_settings) + end context 'for enabled registry' do let(:registry_settings) do @@ -1389,7 +1397,9 @@ describe Project, models: true do let(:project) { create(:empty_project) } context 'when container registry is enabled' do - before { stub_container_registry_config(enabled: true) } + before do + stub_container_registry_config(enabled: true) + end context 'when tags are present for multi-level registries' do before do @@ -1427,7 +1437,9 @@ describe Project, models: true do end context 'when container registry is disabled' do - before { stub_container_registry_config(enabled: false) } + before do + stub_container_registry_config(enabled: false) + end it 'should not have image tags' do expect(project).not_to have_container_registry_tags @@ -1945,7 +1957,9 @@ describe Project, models: true do describe '#parent_changed?' do let(:project) { create(:empty_project) } - before { project.namespace_id = 7 } + before do + project.namespace_id = 7 + end it { expect(project.parent_changed?).to be_truthy } end diff --git a/spec/models/project_team_spec.rb b/spec/models/project_team_spec.rb index 497e3cdf415..ea3cd5fe10a 100644 --- a/spec/models/project_team_spec.rb +++ b/spec/models/project_team_spec.rb @@ -240,7 +240,9 @@ describe ProjectTeam, models: true do it { expect(project.team.max_member_access(requester.id)).to eq(Gitlab::Access::NO_ACCESS) } context 'but share_with_group_lock is true' do - before { project.namespace.update(share_with_group_lock: true) } + before do + project.namespace.update(share_with_group_lock: true) + end it { expect(project.team.max_member_access(master.id)).to eq(Gitlab::Access::NO_ACCESS) } it { expect(project.team.max_member_access(reporter.id)).to eq(Gitlab::Access::NO_ACCESS) } diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb index 224067f58dd..3f5f4eea4a1 100644 --- a/spec/models/project_wiki_spec.rb +++ b/spec/models/project_wiki_spec.rb @@ -8,7 +8,10 @@ describe ProjectWiki, models: true do let(:project_wiki) { ProjectWiki.new(project, user) } subject { project_wiki } - before { project_wiki.wiki } + + before do + project_wiki.wiki + end describe "#path_with_namespace" do it "returns the project path with namespace with the .wiki extension" do diff --git a/spec/models/route_spec.rb b/spec/models/route_spec.rb index c1fe1b06c52..1754253e0f2 100644 --- a/spec/models/route_spec.rb +++ b/spec/models/route_spec.rb @@ -9,7 +9,10 @@ describe Route, models: true do end describe 'validations' do - before { route } + before do + expect(route).to be_persisted + end + it { is_expected.to validate_presence_of(:source) } it { is_expected.to validate_presence_of(:path) } it { is_expected.to validate_uniqueness_of(:path) } @@ -59,7 +62,9 @@ describe Route, models: true do context 'path update' do context 'when route name is set' do - before { route.update_attributes(path: 'bar') } + before do + route.update_attributes(path: 'bar') + end it 'updates children routes with new path' do expect(described_class.exists?(path: 'bar')).to be_truthy diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index d5bd9946ab6..1a1bbd60504 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1584,7 +1584,9 @@ describe User, models: true do end context 'user is member of the top group' do - before { group.add_owner(user) } + before do + group.add_owner(user) + end if Group.supports_nested_groups? it 'returns all groups' do @@ -1602,7 +1604,9 @@ describe User, models: true do end context 'user is member of the first child (internal node), branch 1', :nested_groups do - before { nested_group_1.add_owner(user) } + before do + nested_group_1.add_owner(user) + end it 'returns the groups in the hierarchy' do is_expected.to match_array [ @@ -1613,7 +1617,9 @@ describe User, models: true do end context 'user is member of the first child (internal node), branch 2', :nested_groups do - before { nested_group_2.add_owner(user) } + before do + nested_group_2.add_owner(user) + end it 'returns the groups in the hierarchy' do is_expected.to match_array [ @@ -1624,7 +1630,9 @@ describe User, models: true do end context 'user is member of the last child (leaf node)', :nested_groups do - before { nested_group_1_1.add_owner(user) } + before do + nested_group_1_1.add_owner(user) + end it 'returns the groups in the hierarchy' do is_expected.to match_array [ diff --git a/spec/policies/ci/build_policy_spec.rb b/spec/policies/ci/build_policy_spec.rb index 3f4ce222b60..48a139d4b83 100644 --- a/spec/policies/ci/build_policy_spec.rb +++ b/spec/policies/ci/build_policy_spec.rb @@ -10,7 +10,9 @@ describe Ci::BuildPolicy, :models do end shared_context 'public pipelines disabled' do - before { project.update_attribute(:public_builds, false) } + before do + project.update_attribute(:public_builds, false) + end end describe '#rules' do @@ -54,7 +56,9 @@ describe Ci::BuildPolicy, :models do let(:project) { create(:empty_project, :public) } context 'team member is a guest' do - before { project.team << [user, :guest] } + before do + project.team << [user, :guest] + end context 'when public builds are enabled' do it 'includes ability to read build' do @@ -72,7 +76,9 @@ describe Ci::BuildPolicy, :models do end context 'team member is a reporter' do - before { project.team << [user, :reporter] } + before do + project.team << [user, :reporter] + end context 'when public builds are enabled' do it 'includes ability to read build' do diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index 0d3af1f4499..848fd547e10 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -139,6 +139,18 @@ describe ProjectPolicy, models: true do is_expected.not_to include(:read_build, :read_pipeline) end end + + context 'when builds are disabled' do + before do + project.project_feature.update( + builds_access_level: ProjectFeature::DISABLED) + end + + it do + is_expected.not_to include(:read_build) + is_expected.to include(:read_pipeline) + end + end end context 'reporter' do diff --git a/spec/policies/project_snippet_policy_spec.rb b/spec/policies/project_snippet_policy_spec.rb index ddbed5f781e..d2b2528c57a 100644 --- a/spec/policies/project_snippet_policy_spec.rb +++ b/spec/policies/project_snippet_policy_spec.rb @@ -78,7 +78,9 @@ describe ProjectSnippetPolicy, models: true do context 'project team member external user' do subject { abilities(external_user, :internal) } - before { project.team << [external_user, :developer] } + before do + project.team << [external_user, :developer] + end it do is_expected.to include(:read_project_snippet) @@ -120,7 +122,9 @@ describe ProjectSnippetPolicy, models: true do context 'project team member normal user' do subject { abilities(regular_user, :private) } - before { project.team << [regular_user, :developer] } + before do + project.team << [regular_user, :developer] + end it do is_expected.to include(:read_project_snippet) @@ -131,7 +135,9 @@ describe ProjectSnippetPolicy, models: true do context 'project team member external user' do subject { abilities(external_user, :private) } - before { project.team << [external_user, :developer] } + before do + project.team << [external_user, :developer] + end it do is_expected.to include(:read_project_snippet) diff --git a/spec/requests/api/award_emoji_spec.rb b/spec/requests/api/award_emoji_spec.rb index bbdef0aeb1b..6d822b5cb4f 100644 --- a/spec/requests/api/award_emoji_spec.rb +++ b/spec/requests/api/award_emoji_spec.rb @@ -9,7 +9,9 @@ describe API::AwardEmoji do let!(:downvote) { create(:award_emoji, :downvote, awardable: merge_request, user: user) } let!(:note) { create(:note, project: project, noteable: issue) } - before { project.team << [user, :master] } + before do + project.team << [user, :master] + end describe "GET /projects/:id/awardable/:awardable_id/award_emoji" do context 'on an issue' do diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb index 6b637a03b6f..b8ca73c321c 100644 --- a/spec/requests/api/commit_statuses_spec.rb +++ b/spec/requests/api/commit_statuses_spec.rb @@ -34,7 +34,9 @@ describe API::CommitStatuses do let!(:status6) { create_status(master, status: 'success') } context 'latest commit statuses' do - before { get api(get_url, reporter) } + before do + get api(get_url, reporter) + end it 'returns latest commit statuses' do expect(response).to have_http_status(200) @@ -48,7 +50,9 @@ describe API::CommitStatuses do end context 'all commit statuses' do - before { get api(get_url, reporter), all: 1 } + before do + get api(get_url, reporter), all: 1 + end it 'returns all commit statuses' do expect(response).to have_http_status(200) @@ -61,7 +65,9 @@ describe API::CommitStatuses do end context 'latest commit statuses for specific ref' do - before { get api(get_url, reporter), ref: 'develop' } + before do + get api(get_url, reporter), ref: 'develop' + end it 'returns latest commit statuses for specific ref' do expect(response).to have_http_status(200) @@ -72,7 +78,9 @@ describe API::CommitStatuses do end context 'latest commit statues for specific name' do - before { get api(get_url, reporter), name: 'coverage' } + before do + get api(get_url, reporter), name: 'coverage' + end it 'return latest commit statuses for specific name' do expect(response).to have_http_status(200) @@ -85,7 +93,9 @@ describe API::CommitStatuses do end context 'ci commit does not exist' do - before { get api(get_url, reporter) } + before do + get api(get_url, reporter) + end it 'returns empty array' do expect(response.status).to eq 200 @@ -95,7 +105,9 @@ describe API::CommitStatuses do end context "guest user" do - before { get api(get_url, guest) } + before do + get api(get_url, guest) + end it "does not return project commits" do expect(response).to have_http_status(403) @@ -103,7 +115,9 @@ describe API::CommitStatuses do end context "unauthorized user" do - before { get api(get_url) } + before do + get api(get_url) + end it "does not return project commits" do expect(response).to have_http_status(401) @@ -209,7 +223,9 @@ describe API::CommitStatuses do end context 'when status is invalid' do - before { post api(post_url, developer), state: 'invalid' } + before do + post api(post_url, developer), state: 'invalid' + end it 'does not create commit status' do expect(response).to have_http_status(400) @@ -217,7 +233,9 @@ describe API::CommitStatuses do end context 'when request without a state made' do - before { post api(post_url, developer) } + before do + post api(post_url, developer) + end it 'does not create commit status' do expect(response).to have_http_status(400) @@ -226,7 +244,10 @@ describe API::CommitStatuses do context 'when commit SHA is invalid' do let(:sha) { 'invalid_sha' } - before { post api(post_url, developer), state: 'running' } + + before do + post api(post_url, developer), state: 'running' + end it 'returns not found error' do expect(response).to have_http_status(404) @@ -248,7 +269,9 @@ describe API::CommitStatuses do end context 'reporter user' do - before { post api(post_url, reporter), state: 'running' } + before do + post api(post_url, reporter), state: 'running' + end it 'does not create commit status' do expect(response).to have_http_status(403) @@ -256,7 +279,9 @@ describe API::CommitStatuses do end context 'guest user' do - before { post api(post_url, guest), state: 'running' } + before do + post api(post_url, guest), state: 'running' + end it 'does not create commit status' do expect(response).to have_http_status(403) @@ -264,7 +289,9 @@ describe API::CommitStatuses do end context 'unauthorized user' do - before { post api(post_url) } + before do + post api(post_url) + end it 'does not create commit status' do expect(response).to have_http_status(401) diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index b0c265b6453..0dad547735d 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -9,11 +9,15 @@ describe API::Commits do let!(:note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'a comment on a commit') } let!(:another_note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'another comment on a commit') } - before { project.team << [user, :reporter] } + before do + project.team << [user, :reporter] + end describe "List repository commits" do context "authorized user" do - before { project.team << [user2, :reporter] } + before do + project.team << [user2, :reporter] + end it "returns project commits" do commit = project.repository.commit @@ -514,7 +518,9 @@ describe API::Commits do describe "Get the diff of a commit" do context "authorized user" do - before { project.team << [user2, :reporter] } + before do + project.team << [user2, :reporter] + end it "returns the diff of the selected commit" do get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/diff", user) diff --git a/spec/requests/api/deploy_keys_spec.rb b/spec/requests/api/deploy_keys_spec.rb index 4d9cd5f3a27..9c260f88f56 100644 --- a/spec/requests/api/deploy_keys_spec.rb +++ b/spec/requests/api/deploy_keys_spec.rb @@ -41,7 +41,9 @@ describe API::DeployKeys do end describe 'GET /projects/:id/deploy_keys' do - before { deploy_key } + before do + deploy_key + end it 'returns array of ssh keys' do get api("/projects/#{project.id}/deploy_keys", admin) @@ -161,7 +163,9 @@ describe API::DeployKeys do end describe 'DELETE /projects/:id/deploy_keys/:key_id' do - before { deploy_key } + before do + deploy_key + end it 'deletes existing key' do expect do diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb index d325c6eff9d..c5ec8be4f21 100644 --- a/spec/requests/api/files_spec.rb +++ b/spec/requests/api/files_spec.rb @@ -13,7 +13,9 @@ describe API::Files do let(:author_email) { 'user@example.org' } let(:author_name) { 'John Doe' } - before { project.team << [user, :developer] } + before do + project.team << [user, :developer] + end def route(file_path = nil) "/projects/#{project.id}/repository/files/#{file_path}" diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb index ed392acc607..191c60aba31 100644 --- a/spec/requests/api/helpers_spec.rb +++ b/spec/requests/api/helpers_spec.rb @@ -55,40 +55,62 @@ describe API::Helpers do subject { current_user } describe "Warden authentication" do - before { doorkeeper_guard_returns false } + before do + doorkeeper_guard_returns false + end context "with invalid credentials" do context "GET request" do - before { env['REQUEST_METHOD'] = 'GET' } + before do + env['REQUEST_METHOD'] = 'GET' + end + it { is_expected.to be_nil } end end context "with valid credentials" do - before { warden_authenticate_returns user } + before do + warden_authenticate_returns user + end context "GET request" do - before { env['REQUEST_METHOD'] = 'GET' } + before do + env['REQUEST_METHOD'] = 'GET' + end + it { is_expected.to eq(user) } end context "HEAD request" do - before { env['REQUEST_METHOD'] = 'HEAD' } + before do + env['REQUEST_METHOD'] = 'HEAD' + end + it { is_expected.to eq(user) } end context "PUT request" do - before { env['REQUEST_METHOD'] = 'PUT' } + before do + env['REQUEST_METHOD'] = 'PUT' + end + it { is_expected.to be_nil } end context "POST request" do - before { env['REQUEST_METHOD'] = 'POST' } + before do + env['REQUEST_METHOD'] = 'POST' + end + it { is_expected.to be_nil } end context "DELETE request" do - before { env['REQUEST_METHOD'] = 'DELETE' } + before do + env['REQUEST_METHOD'] = 'DELETE' + end + it { is_expected.to be_nil } end end diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb index e5e5872dc1f..8d647eb1c7e 100644 --- a/spec/requests/api/jobs_spec.rb +++ b/spec/requests/api/jobs_spec.rb @@ -11,7 +11,7 @@ describe API::Jobs, :api do ref: project.default_branch) end - let!(:build) { create(:ci_build, pipeline: pipeline) } + let!(:job) { create(:ci_build, pipeline: pipeline) } let(:user) { create(:user) } let(:api_user) { user } @@ -42,13 +42,13 @@ describe API::Jobs, :api do end it 'returns pipeline data' do - json_build = json_response.first + json_job = json_response.first - expect(json_build['pipeline']).not_to be_empty - expect(json_build['pipeline']['id']).to eq build.pipeline.id - expect(json_build['pipeline']['ref']).to eq build.pipeline.ref - expect(json_build['pipeline']['sha']).to eq build.pipeline.sha - expect(json_build['pipeline']['status']).to eq build.pipeline.status + expect(json_job['pipeline']).not_to be_empty + expect(json_job['pipeline']['id']).to eq job.pipeline.id + expect(json_job['pipeline']['ref']).to eq job.pipeline.ref + expect(json_job['pipeline']['sha']).to eq job.pipeline.sha + expect(json_job['pipeline']['status']).to eq job.pipeline.status end context 'filter project with one scope element' do @@ -79,7 +79,7 @@ describe API::Jobs, :api do context 'unauthorized user' do let(:api_user) { nil } - it 'does not return project builds' do + it 'does not return project jobs' do expect(response).to have_http_status(401) end end @@ -105,13 +105,13 @@ describe API::Jobs, :api do end it 'returns pipeline data' do - json_build = json_response.first + json_job = json_response.first - expect(json_build['pipeline']).not_to be_empty - expect(json_build['pipeline']['id']).to eq build.pipeline.id - expect(json_build['pipeline']['ref']).to eq build.pipeline.ref - expect(json_build['pipeline']['sha']).to eq build.pipeline.sha - expect(json_build['pipeline']['status']).to eq build.pipeline.status + expect(json_job['pipeline']).not_to be_empty + expect(json_job['pipeline']['id']).to eq job.pipeline.id + expect(json_job['pipeline']['ref']).to eq job.pipeline.ref + expect(json_job['pipeline']['sha']).to eq job.pipeline.sha + expect(json_job['pipeline']['status']).to eq job.pipeline.status end context 'filter jobs with one scope element' do @@ -140,7 +140,7 @@ describe API::Jobs, :api do context 'jobs in different pipelines' do let!(:pipeline2) { create(:ci_empty_pipeline, project: project) } - let!(:build2) { create(:ci_build, pipeline: pipeline2) } + let!(:job2) { create(:ci_build, pipeline: pipeline2) } it 'excludes jobs from other pipelines' do json_response.each { |job| expect(job['pipeline']['id']).to eq(pipeline.id) } @@ -159,7 +159,7 @@ describe API::Jobs, :api do describe 'GET /projects/:id/jobs/:job_id' do before do - get api("/projects/#{project.id}/jobs/#{build.id}", api_user) + get api("/projects/#{project.id}/jobs/#{job.id}", api_user) end context 'authorized user' do @@ -169,12 +169,13 @@ describe API::Jobs, :api do end it 'returns pipeline data' do - json_build = json_response - expect(json_build['pipeline']).not_to be_empty - expect(json_build['pipeline']['id']).to eq build.pipeline.id - expect(json_build['pipeline']['ref']).to eq build.pipeline.ref - expect(json_build['pipeline']['sha']).to eq build.pipeline.sha - expect(json_build['pipeline']['status']).to eq build.pipeline.status + json_job = json_response + + expect(json_job['pipeline']).not_to be_empty + expect(json_job['pipeline']['id']).to eq job.pipeline.id + expect(json_job['pipeline']['ref']).to eq job.pipeline.ref + expect(json_job['pipeline']['sha']).to eq job.pipeline.sha + expect(json_job['pipeline']['status']).to eq job.pipeline.status end end @@ -189,11 +190,11 @@ describe API::Jobs, :api do describe 'GET /projects/:id/jobs/:job_id/artifacts' do before do - get api("/projects/#{project.id}/jobs/#{build.id}/artifacts", api_user) + get api("/projects/#{project.id}/jobs/#{job.id}/artifacts", api_user) end context 'job with artifacts' do - let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) } + let(:job) { create(:ci_build, :artifacts, pipeline: pipeline) } context 'authorized user' do let(:download_headers) do @@ -204,7 +205,7 @@ describe API::Jobs, :api do it 'returns specific job artifacts' do expect(response).to have_http_status(200) expect(response.headers).to include(download_headers) - expect(response.body).to match_file(build.artifacts_file.file.file) + expect(response.body).to match_file(job.artifacts_file.file.file) end end @@ -224,14 +225,14 @@ describe API::Jobs, :api do describe 'GET /projects/:id/artifacts/:ref_name/download?job=name' do let(:api_user) { reporter } - let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) } + let(:job) { create(:ci_build, :artifacts, pipeline: pipeline) } before do - build.success + job.success end - def get_for_ref(ref = pipeline.ref, job = build.name) - get api("/projects/#{project.id}/jobs/artifacts/#{ref}/download", api_user), job: job + def get_for_ref(ref = pipeline.ref, job_name = job.name) + get api("/projects/#{project.id}/jobs/artifacts/#{ref}/download", api_user), job: job_name end context 'when not logged in' do @@ -285,7 +286,7 @@ describe API::Jobs, :api do let(:download_headers) do { 'Content-Transfer-Encoding' => 'binary', 'Content-Disposition' => - "attachment; filename=#{build.artifacts_file.filename}" } + "attachment; filename=#{job.artifacts_file.filename}" } end it { expect(response).to have_http_status(200) } @@ -321,16 +322,16 @@ describe API::Jobs, :api do end describe 'GET /projects/:id/jobs/:job_id/trace' do - let(:build) { create(:ci_build, :trace, pipeline: pipeline) } + let(:job) { create(:ci_build, :trace, pipeline: pipeline) } before do - get api("/projects/#{project.id}/jobs/#{build.id}/trace", api_user) + get api("/projects/#{project.id}/jobs/#{job.id}/trace", api_user) end context 'authorized user' do it 'returns specific job trace' do expect(response).to have_http_status(200) - expect(response.body).to eq(build.trace.raw) + expect(response.body).to eq(job.trace.raw) end end @@ -345,7 +346,7 @@ describe API::Jobs, :api do describe 'POST /projects/:id/jobs/:job_id/cancel' do before do - post api("/projects/#{project.id}/jobs/#{build.id}/cancel", api_user) + post api("/projects/#{project.id}/jobs/#{job.id}/cancel", api_user) end context 'authorized user' do @@ -375,10 +376,10 @@ describe API::Jobs, :api do end describe 'POST /projects/:id/jobs/:job_id/retry' do - let(:build) { create(:ci_build, :canceled, pipeline: pipeline) } + let(:job) { create(:ci_build, :canceled, pipeline: pipeline) } before do - post api("/projects/#{project.id}/jobs/#{build.id}/retry", api_user) + post api("/projects/#{project.id}/jobs/#{job.id}/retry", api_user) end context 'authorized user' do @@ -410,28 +411,29 @@ describe API::Jobs, :api do describe 'POST /projects/:id/jobs/:job_id/erase' do before do - post api("/projects/#{project.id}/jobs/#{build.id}/erase", user) + post api("/projects/#{project.id}/jobs/#{job.id}/erase", user) end context 'job is erasable' do - let(:build) { create(:ci_build, :trace, :artifacts, :success, project: project, pipeline: pipeline) } + let(:job) { create(:ci_build, :trace, :artifacts, :success, project: project, pipeline: pipeline) } it 'erases job content' do expect(response).to have_http_status(201) - expect(build).not_to have_trace - expect(build.artifacts_file.exists?).to be_falsy - expect(build.artifacts_metadata.exists?).to be_falsy + expect(job).not_to have_trace + expect(job.artifacts_file.exists?).to be_falsy + expect(job.artifacts_metadata.exists?).to be_falsy end it 'updates job' do - build.reload - expect(build.erased_at).to be_truthy - expect(build.erased_by).to eq(user) + job.reload + + expect(job.erased_at).to be_truthy + expect(job.erased_by).to eq(user) end end context 'job is not erasable' do - let(:build) { create(:ci_build, :trace, project: project, pipeline: pipeline) } + let(:job) { create(:ci_build, :trace, project: project, pipeline: pipeline) } it 'responds with forbidden' do expect(response).to have_http_status(403) @@ -439,25 +441,25 @@ describe API::Jobs, :api do end end - describe 'POST /projects/:id/jobs/:build_id/artifacts/keep' do + describe 'POST /projects/:id/jobs/:job_id/artifacts/keep' do before do - post api("/projects/#{project.id}/jobs/#{build.id}/artifacts/keep", user) + post api("/projects/#{project.id}/jobs/#{job.id}/artifacts/keep", user) end context 'artifacts did not expire' do - let(:build) do + let(:job) do create(:ci_build, :trace, :artifacts, :success, project: project, pipeline: pipeline, artifacts_expire_at: Time.now + 7.days) end it 'keeps artifacts' do expect(response).to have_http_status(200) - expect(build.reload.artifacts_expire_at).to be_nil + expect(job.reload.artifacts_expire_at).to be_nil end end context 'no artifacts' do - let(:build) { create(:ci_build, project: project, pipeline: pipeline) } + let(:job) { create(:ci_build, project: project, pipeline: pipeline) } it 'responds with not found' do expect(response).to have_http_status(404) @@ -467,18 +469,18 @@ describe API::Jobs, :api do describe 'POST /projects/:id/jobs/:job_id/play' do before do - post api("/projects/#{project.id}/jobs/#{build.id}/play", api_user) + post api("/projects/#{project.id}/jobs/#{job.id}/play", api_user) end context 'on an playable job' do - let(:build) { create(:ci_build, :manual, project: project, pipeline: pipeline) } + let(:job) { create(:ci_build, :manual, project: project, pipeline: pipeline) } context 'when user is authorized to trigger a manual action' do it 'plays the job' do expect(response).to have_http_status(200) expect(json_response['user']['id']).to eq(user.id) - expect(json_response['id']).to eq(build.id) - expect(build.reload).to be_pending + expect(json_response['id']).to eq(job.id) + expect(job.reload).to be_pending end end @@ -487,7 +489,7 @@ describe API::Jobs, :api do let(:api_user) { create(:user) } it 'does not trigger a manual action' do - expect(build.reload).to be_manual + expect(job.reload).to be_manual expect(response).to have_http_status(404) end end @@ -496,7 +498,7 @@ describe API::Jobs, :api do let(:api_user) { reporter } it 'does not trigger a manual action' do - expect(build.reload).to be_manual + expect(job.reload).to be_manual expect(response).to have_http_status(403) end end diff --git a/spec/requests/api/keys_spec.rb b/spec/requests/api/keys_spec.rb index ab957c72984..f534332ca6c 100644 --- a/spec/requests/api/keys_spec.rb +++ b/spec/requests/api/keys_spec.rb @@ -4,11 +4,9 @@ describe API::Keys do let(:user) { create(:user) } let(:admin) { create(:admin) } let(:key) { create(:key, user: user) } - let(:email) { create(:email, user: user) } + let(:email) { create(:email, user: user) } describe 'GET /keys/:uid' do - before { admin } - context 'when unauthenticated' do it 'returns authentication error' do get api("/keys/#{key.id}") diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb index 0c6b55c1630..f7e2f1908bb 100644 --- a/spec/requests/api/labels_spec.rb +++ b/spec/requests/api/labels_spec.rb @@ -339,7 +339,9 @@ describe API::Labels do end context "when user is already subscribed to label" do - before { label1.subscribe(user, project) } + before do + label1.subscribe(user, project) + end it "returns 304" do post api("/projects/#{project.id}/labels/#{label1.id}/subscribe", user) @@ -358,7 +360,9 @@ describe API::Labels do end describe "POST /projects/:id/labels/:label_id/unsubscribe" do - before { label1.subscribe(user, project) } + before do + label1.subscribe(user, project) + end context "when label_id is a label title" do it "unsubscribes from the label" do @@ -381,7 +385,9 @@ describe API::Labels do end context "when user is already unsubscribed from label" do - before { label1.unsubscribe(user, project) } + before do + label1.unsubscribe(user, project) + end it "returns 304" do post api("/projects/#{project.id}/labels/#{label1.id}/unsubscribe", user) diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb index dd74351a2b1..40934c25afc 100644 --- a/spec/requests/api/milestones_spec.rb +++ b/spec/requests/api/milestones_spec.rb @@ -6,7 +6,9 @@ describe API::Milestones do let!(:closed_milestone) { create(:closed_milestone, project: project, title: 'version1', description: 'closed milestone') } let!(:milestone) { create(:milestone, project: project, title: 'version2', description: 'open milestone') } - before { project.team << [user, :developer] } + before do + project.team << [user, :developer] + end describe 'GET /projects/:id/milestones' do it 'returns project milestones' do diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb index 6afcd237c3c..03f2b5950ee 100644 --- a/spec/requests/api/notes_spec.rb +++ b/spec/requests/api/notes_spec.rb @@ -28,7 +28,9 @@ describe API::Notes do system: true end - before { project.team << [user, :reporter] } + before do + project.team << [user, :reporter] + end describe "GET /projects/:id/noteable/:noteable_id/notes" do context "when noteable is an Issue" do @@ -58,7 +60,9 @@ describe API::Notes do end context "and issue is confidential" do - before { ext_issue.update_attributes(confidential: true) } + before do + ext_issue.update_attributes(confidential: true) + end it "returns 404" do get api("/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes", user) @@ -150,7 +154,9 @@ describe API::Notes do end context "when issue is confidential" do - before { issue.update_attributes(confidential: true) } + before do + issue.update_attributes(confidential: true) + end it "returns 404" do get api("/projects/#{project.id}/issues/#{issue.iid}/notes/#{issue_note.id}", private_user) diff --git a/spec/requests/api/pipelines_spec.rb b/spec/requests/api/pipelines_spec.rb index 9e6957e9922..258085e503f 100644 --- a/spec/requests/api/pipelines_spec.rb +++ b/spec/requests/api/pipelines_spec.rb @@ -10,7 +10,9 @@ describe API::Pipelines do ref: project.default_branch, user: user) end - before { project.team << [user, :master] } + before do + project.team << [user, :master] + end describe 'GET /projects/:id/pipelines ' do context 'authorized user' do @@ -285,7 +287,9 @@ describe API::Pipelines do describe 'POST /projects/:id/pipeline ' do context 'authorized user' do context 'with gitlab-ci.yml' do - before { stub_ci_pipeline_to_return_yaml_file } + before do + stub_ci_pipeline_to_return_yaml_file + end it 'creates and returns a new pipeline' do expect do @@ -419,7 +423,9 @@ describe API::Pipelines do context 'user without proper access rights' do let!(:reporter) { create(:user) } - before { project.team << [reporter, :reporter] } + before do + project.team << [reporter, :reporter] + end it 'rejects the action' do post api("/projects/#{project.id}/pipelines/#{pipeline.id}/cancel", reporter) diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 3e831373514..d92262a4c99 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -476,8 +476,9 @@ describe API::Projects do end describe 'POST /projects/user/:id' do - before { project } - before { admin } + before do + expect(project).to be_persisted + end it 'creates new project without path but with name and return 201' do expect { post api("/projects/user/#{user.id}", admin), name: 'Foo Project' }.to change {Project.count}.by(1) @@ -581,7 +582,9 @@ describe API::Projects do end describe "POST /projects/:id/uploads" do - before { project } + before do + project + end it "uploads the file and returns its info" do post api("/projects/#{project.id}/uploads", user), file: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png") @@ -729,7 +732,9 @@ describe API::Projects do describe 'permissions' do context 'all projects' do - before { project.team << [user, :master] } + before do + project.team << [user, :master] + end it 'contains permission information' do get api("/projects", user) @@ -756,7 +761,9 @@ describe API::Projects do context 'group project' do let(:project2) { create(:empty_project, group: create(:group)) } - before { project2.group.add_owner(user) } + before do + project2.group.add_owner(user) + end it 'sets the owner and return 200' do get api("/projects/#{project2.id}", user) @@ -822,7 +829,9 @@ describe API::Projects do end describe 'GET /projects/:id/snippets' do - before { snippet } + before do + snippet + end it 'returns an array of project snippets' do get api("/projects/#{project.id}/snippets", user) @@ -879,7 +888,9 @@ describe API::Projects do end describe 'DELETE /projects/:id/snippets/:snippet_id' do - before { snippet } + before do + snippet + end it 'deletes existing project snippet' do expect do @@ -1074,14 +1085,16 @@ describe API::Projects do end describe 'PUT /projects/:id' do - before { project } - before { user } - before { user3 } - before { user4 } - before { project3 } - before { project4 } - before { project_member2 } - before { project_member } + before do + expect(project).to be_persisted + expect(user).to be_persisted + expect(user3).to be_persisted + expect(user4).to be_persisted + expect(project3).to be_persisted + expect(project4).to be_persisted + expect(project_member2).to be_persisted + expect(project_member).to be_persisted + end it 'returns 400 when nothing sent' do project_param = {} diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb index 9556c99dea1..d554c242916 100644 --- a/spec/requests/api/runner_spec.rb +++ b/spec/requests/api/runner_spec.rb @@ -190,17 +190,23 @@ describe API::Runner do pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0, commands: "ls\ndate") end - before { project.runners << runner } + before do + project.runners << runner + end describe 'POST /api/v4/jobs/request' do let!(:last_update) {} let!(:new_update) { } let(:user_agent) { 'gitlab-runner 9.0.0 (9-0-stable; go1.7.4; linux/amd64)' } - before { stub_container_registry_config(enabled: false) } + before do + stub_container_registry_config(enabled: false) + end shared_examples 'no jobs available' do - before { request_job } + before do + request_job + end context 'when runner sends version in User-Agent' do context 'for stable version' do @@ -277,7 +283,9 @@ describe API::Runner do end context 'when jobs are finished' do - before { job.success } + before do + job.success + end it_behaves_like 'no jobs available' end @@ -356,8 +364,11 @@ describe API::Runner do expect(json_response['token']).to eq(job.token) expect(json_response['job_info']).to eq(expected_job_info) expect(json_response['git_info']).to eq(expected_git_info) - expect(json_response['image']).to eq({ 'name' => 'ruby:2.1' }) - expect(json_response['services']).to eq([{ 'name' => 'postgres' }]) + expect(json_response['image']).to eq({ 'name' => 'ruby:2.1', 'entrypoint' => '/bin/sh' }) + expect(json_response['services']).to eq([{ 'name' => 'postgres', 'entrypoint' => nil, + 'alias' => nil, 'command' => nil }, + { 'name' => 'docker:dind', 'entrypoint' => '/bin/sh', + 'alias' => 'docker', 'command' => 'sleep 30' }]) expect(json_response['steps']).to eq(expected_steps) expect(json_response['artifacts']).to eq(expected_artifacts) expect(json_response['cache']).to eq(expected_cache) @@ -505,10 +516,14 @@ describe API::Runner do end context 'when job has no tags' do - before { job.update(tags: []) } + before do + job.update(tags: []) + end context 'when runner is allowed to pick untagged jobs' do - before { runner.update_column(:run_untagged, true) } + before do + runner.update_column(:run_untagged, true) + end it 'picks job' do request_job @@ -518,7 +533,9 @@ describe API::Runner do end context 'when runner is not allowed to pick untagged jobs' do - before { runner.update_column(:run_untagged, false) } + before do + runner.update_column(:run_untagged, false) + end it_behaves_like 'no jobs available' end @@ -558,7 +575,9 @@ describe API::Runner do end context 'when registry is enabled' do - before { stub_container_registry_config(enabled: true, host_port: registry_url) } + before do + stub_container_registry_config(enabled: true, host_port: registry_url) + end it 'sends registry credentials key' do request_job @@ -569,7 +588,9 @@ describe API::Runner do end context 'when registry is disabled' do - before { stub_container_registry_config(enabled: false, host_port: registry_url) } + before do + stub_container_registry_config(enabled: false, host_port: registry_url) + end it 'does not send registry credentials' do request_job @@ -591,7 +612,9 @@ describe API::Runner do describe 'PUT /api/v4/jobs/:id' do let(:job) { create(:ci_build, :pending, :trace, pipeline: pipeline, runner_id: runner.id) } - before { job.run! } + before do + job.run! + end context 'when status is given' do it 'mark job as succeeded' do @@ -646,7 +669,9 @@ describe API::Runner do let(:headers_with_range) { headers.merge({ 'Content-Range' => '11-20' }) } let(:update_interval) { 10.seconds.to_i } - before { initial_patch_the_trace } + before do + initial_patch_the_trace + end context 'when request is valid' do it 'gets correct response' do @@ -788,7 +813,9 @@ describe API::Runner do let(:file_upload) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } let(:file_upload2) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'image/gif') } - before { job.run! } + before do + job.run! + end describe 'POST /api/v4/jobs/:id/artifacts/authorize' do context 'when using token as parameter' do @@ -894,13 +921,17 @@ describe API::Runner do end context 'when uses regular file post' do - before { upload_artifacts(file_upload, headers_with_token, false) } + before do + upload_artifacts(file_upload, headers_with_token, false) + end it_behaves_like 'successful artifacts upload' end context 'when uses accelerated file post' do - before { upload_artifacts(file_upload, headers_with_token, true) } + before do + upload_artifacts(file_upload, headers_with_token, true) + end it_behaves_like 'successful artifacts upload' end @@ -1054,7 +1085,9 @@ describe API::Runner do allow(ArtifactUploader).to receive(:artifacts_upload_path).and_return(@tmpdir) end - after { FileUtils.remove_entry @tmpdir } + after do + FileUtils.remove_entry @tmpdir + end it' "fails to post artifacts for outside of tmp path"' do upload_artifacts(file_upload, headers_with_token) @@ -1076,7 +1109,9 @@ describe API::Runner do describe 'GET /api/v4/jobs/:id/artifacts' do let(:token) { job.token } - before { download_artifact } + before do + download_artifact + end context 'when job has artifacts' do let(:job) { create(:ci_build, :artifacts) } diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb index 2398ae6219c..ede48b1c888 100644 --- a/spec/requests/api/settings_spec.rb +++ b/spec/requests/api/settings_spec.rb @@ -40,7 +40,10 @@ describe API::Settings, 'Settings' do plantuml_url: 'http://plantuml.example.com', default_snippet_visibility: 'internal', restricted_visibility_levels: ['public'], - default_artifacts_expire_in: '2 days' + default_artifacts_expire_in: '2 days', + help_page_text: 'custom help text', + help_page_hide_commercial_content: true, + help_page_support_url: 'http://example.com/help' expect(response).to have_http_status(200) expect(json_response['default_projects_limit']).to eq(3) expect(json_response['signin_enabled']).to be_falsey @@ -53,6 +56,9 @@ describe API::Settings, 'Settings' do expect(json_response['default_snippet_visibility']).to eq('internal') expect(json_response['restricted_visibility_levels']).to eq(['public']) expect(json_response['default_artifacts_expire_in']).to eq('2 days') + expect(json_response['help_page_text']).to eq('custom help text') + expect(json_response['help_page_hide_commercial_content']).to be_truthy + expect(json_response['help_page_support_url']).to eq('http://example.com/help') end end diff --git a/spec/requests/api/system_hooks_spec.rb b/spec/requests/api/system_hooks_spec.rb index 2eb191d6049..f65b475fe44 100644 --- a/spec/requests/api/system_hooks_spec.rb +++ b/spec/requests/api/system_hooks_spec.rb @@ -5,7 +5,9 @@ describe API::SystemHooks do let(:admin) { create(:admin) } let!(:hook) { create(:system_hook, url: "http://example.com") } - before { stub_request(:post, hook.url) } + before do + stub_request(:post, hook.url) + end describe "GET /hooks" do context "when no user" do diff --git a/spec/requests/api/templates_spec.rb b/spec/requests/api/templates_spec.rb index cb55985e3f5..f8af9295842 100644 --- a/spec/requests/api/templates_spec.rb +++ b/spec/requests/api/templates_spec.rb @@ -2,14 +2,18 @@ require 'spec_helper' describe API::Templates do context 'the Template Entity' do - before { get api('/templates/gitignores/Ruby') } + before do + get api('/templates/gitignores/Ruby') + end it { expect(json_response['name']).to eq('Ruby') } it { expect(json_response['content']).to include('*.gem') } end context 'the TemplateList Entity' do - before { get api('/templates/gitignores') } + before do + get api('/templates/gitignores') + end it { expect(json_response.first['name']).not_to be_nil } it { expect(json_response.first['content']).to be_nil } @@ -47,7 +51,9 @@ describe API::Templates do end context 'the License Template Entity' do - before { get api('/templates/licenses/mit') } + before do + get api('/templates/licenses/mit') + end it 'returns a license template' do expect(json_response['key']).to eq('mit') diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index ec51b96c86b..9dc4b6972a6 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -160,7 +160,9 @@ describe API::Users do end describe "POST /users" do - before { admin } + before do + admin + end it "creates user" do expect do @@ -349,7 +351,9 @@ describe API::Users do describe "PUT /users/:id" do let!(:admin_user) { create(:admin) } - before { admin } + before do + admin + end it "updates user with new bio" do put api("/users/#{user.id}", admin), { bio: 'new test bio' } @@ -502,7 +506,9 @@ describe API::Users do end describe "POST /users/:id/keys" do - before { admin } + before do + admin + end it "does not create invalid ssh key" do post api("/users/#{user.id}/keys", admin), { title: "invalid key" } @@ -532,7 +538,9 @@ describe API::Users do end describe 'GET /user/:id/keys' do - before { admin } + before do + admin + end context 'when unauthenticated' do it 'returns authentication error' do @@ -563,7 +571,9 @@ describe API::Users do end describe 'DELETE /user/:id/keys/:key_id' do - before { admin } + before do + admin + end context 'when unauthenticated' do it 'returns authentication error' do @@ -601,7 +611,9 @@ describe API::Users do end describe "POST /users/:id/emails" do - before { admin } + before do + admin + end it "does not create invalid email" do post api("/users/#{user.id}/emails", admin), {} @@ -625,7 +637,9 @@ describe API::Users do end describe 'GET /user/:id/emails' do - before { admin } + before do + admin + end context 'when unauthenticated' do it 'returns authentication error' do @@ -662,7 +676,9 @@ describe API::Users do end describe 'DELETE /user/:id/emails/:email_id' do - before { admin } + before do + admin + end context 'when unauthenticated' do it 'returns authentication error' do @@ -708,7 +724,10 @@ describe API::Users do describe "DELETE /users/:id" do let!(:namespace) { user.namespace } let!(:issue) { create(:issue, author: user) } - before { admin } + + before do + admin + end it "deletes user" do Sidekiq::Testing.inline! { delete api("/users/#{user.id}", admin) } @@ -1068,7 +1087,10 @@ describe API::Users do end describe 'POST /users/:id/block' do - before { admin } + before do + admin + end + it 'blocks existing user' do post api("/users/#{user.id}/block", admin) expect(response).to have_http_status(201) @@ -1096,7 +1118,10 @@ describe API::Users do describe 'POST /users/:id/unblock' do let(:blocked_user) { create(:user, state: 'blocked') } - before { admin } + + before do + admin + end it 'unblocks existing user' do post api("/users/#{user.id}/unblock", admin) diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index 286de277ae7..83c675792f4 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -137,6 +137,18 @@ describe Ci::API::Builds do end end end + + context 'when docker configuration options are used' do + let!(:build) { create(:ci_build, :extended_options, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) } + + it 'starts a build' do + register_builds info: { platform: :darwin } + + expect(response).to have_http_status(201) + expect(json_response['options']['image']).to eq('ruby:2.1') + expect(json_response['options']['services']).to eq(['postgres', 'docker:dind']) + end + end end context 'when builds are finished' do @@ -229,7 +241,9 @@ describe Ci::API::Builds do end context 'when runner is allowed to pick untagged builds' do - before { runner.update_column(:run_untagged, true) } + before do + runner.update_column(:run_untagged, true) + end it 'picks build' do register_builds @@ -455,7 +469,9 @@ describe Ci::API::Builds do let(:token) { build.token } let(:headers_with_token) { headers.merge(Ci::API::Helpers::BUILD_TOKEN_HEADER => token) } - before { build.run! } + before do + build.run! + end describe "POST /builds/:id/artifacts/authorize" do context "authorizes posting artifact to running build" do @@ -511,7 +527,9 @@ describe Ci::API::Builds do end context 'authorization token is invalid' do - before { post authorize_url, { token: 'invalid', filesize: 100 } } + before do + post authorize_url, { token: 'invalid', filesize: 100 } + end it 'responds with forbidden' do expect(response).to have_http_status(403) diff --git a/spec/requests/ci/api/runners_spec.rb b/spec/requests/ci/api/runners_spec.rb index 0b9733221d8..78b2be350cd 100644 --- a/spec/requests/ci/api/runners_spec.rb +++ b/spec/requests/ci/api/runners_spec.rb @@ -12,7 +12,9 @@ describe Ci::API::Runners do describe "POST /runners/register" do context 'when runner token is provided' do - before { post ci_api("/runners/register"), token: registration_token } + before do + post ci_api("/runners/register"), token: registration_token + end it 'creates runner with default values' do expect(response).to have_http_status 201 @@ -69,7 +71,10 @@ describe Ci::API::Runners do context 'when project token is provided' do let(:project) { FactoryGirl.create(:empty_project) } - before { post ci_api("/runners/register"), token: project.runners_token } + + before do + post ci_api("/runners/register"), token: project.runners_token + end it 'creates runner' do expect(response).to have_http_status 201 diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb index 6a83024d0d5..dce78faefc9 100644 --- a/spec/requests/git_http_spec.rb +++ b/spec/requests/git_http_spec.rb @@ -627,7 +627,9 @@ describe 'Git HTTP requests', lib: true do let(:path) { "/#{project.path_with_namespace}/info/refs" } context "when no params are added" do - before { get path } + before do + get path + end it "redirects to the .git suffix version" do expect(response).to redirect_to("/#{project.path_with_namespace}.git/info/refs") @@ -636,7 +638,10 @@ describe 'Git HTTP requests', lib: true do context "when the upload-pack service is requested" do let(:params) { { service: 'git-upload-pack' } } - before { get path, params } + + before do + get path, params + end it "redirects to the .git suffix version" do expect(response).to redirect_to("/#{project.path_with_namespace}.git/info/refs?service=#{params[:service]}") @@ -645,7 +650,10 @@ describe 'Git HTTP requests', lib: true do context "when the receive-pack service is requested" do let(:params) { { service: 'git-receive-pack' } } - before { get path, params } + + before do + get path, params + end it "redirects to the .git suffix version" do expect(response).to redirect_to("/#{project.path_with_namespace}.git/info/refs?service=#{params[:service]}") @@ -654,7 +662,10 @@ describe 'Git HTTP requests', lib: true do context "when the params are anything else" do let(:params) { { service: 'git-implode-pack' } } - before { get path, params } + + before do + get path, params + end it "redirects to the sign-in page" do expect(response).to redirect_to(new_user_session_path) @@ -695,7 +706,9 @@ describe 'Git HTTP requests', lib: true do end context "when the file does not exist" do - before { get "/#{project.path_with_namespace}/blob/master/info/refs" } + before do + get "/#{project.path_with_namespace}/blob/master/info/refs" + end it "returns not found" do expect(response).to have_http_status(:not_found) diff --git a/spec/requests/jwt_controller_spec.rb b/spec/requests/jwt_controller_spec.rb index 54d7cf5f10d..5e4cf05748e 100644 --- a/spec/requests/jwt_controller_spec.rb +++ b/spec/requests/jwt_controller_spec.rb @@ -6,7 +6,9 @@ describe JwtController do let(:service_name) { 'test' } let(:parameters) { { service: service_name } } - before { stub_const('JwtController::SERVICES', service_name => service_class) } + before do + stub_const('JwtController::SERVICES', service_name => service_class) + end context 'existing service' do subject! { get '/jwt/auth', parameters } diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index 0a6778ae2ef..95d40138fea 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -93,13 +93,17 @@ describe 'project routing' do end context 'name with dot' do - before { allow(Project).to receive(:find_by_full_path).with('gitlab/gitlabhq.keys', any_args).and_return(true) } + before do + allow(Project).to receive(:find_by_full_path).with('gitlab/gitlabhq.keys', any_args).and_return(true) + end it { expect(get('/gitlab/gitlabhq.keys')).to route_to('projects#show', namespace_id: 'gitlab', id: 'gitlabhq.keys') } end context 'with nested group' do - before { allow(Project).to receive(:find_by_full_path).with('gitlab/subgroup/gitlabhq', any_args).and_return(true) } + before do + allow(Project).to receive(:find_by_full_path).with('gitlab/subgroup/gitlabhq', any_args).and_return(true) + end it { expect(get('/gitlab/subgroup/gitlabhq')).to route_to('projects#show', namespace_id: 'gitlab/subgroup', id: 'gitlabhq') } end diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb index a62af13cf0c..a45839b16f5 100644 --- a/spec/routing/routing_spec.rb +++ b/spec/routing/routing_spec.rb @@ -286,7 +286,9 @@ end describe "Groups", "routing" do let(:name) { 'complex.group-namegit' } - before { allow_any_instance_of(GroupUrlConstrainer).to receive(:matches?).and_return(true) } + before do + allow_any_instance_of(GroupUrlConstrainer).to receive(:matches?).and_return(true) + end it "to #show" do expect(get("/groups/#{name}")).to route_to('groups#show', id: name) diff --git a/spec/rubocop/cop/rspec/single_line_hook_spec.rb b/spec/rubocop/cop/rspec/single_line_hook_spec.rb new file mode 100644 index 00000000000..6cf0831d3ad --- /dev/null +++ b/spec/rubocop/cop/rspec/single_line_hook_spec.rb @@ -0,0 +1,66 @@ +require 'spec_helper' + +require 'rubocop' +require 'rubocop/rspec/support' + +require_relative '../../../../rubocop/cop/rspec/single_line_hook' + +describe RuboCop::Cop::RSpec::SingleLineHook do + include CopHelper + + subject(:cop) { described_class.new } + + # Override `CopHelper#inspect_source` to always appear to be in a spec file, + # so that our RSpec-only cop actually runs + def inspect_source(*args) + super(*args, 'foo_spec.rb') + end + + it 'registers an offense for a single-line `before` block' do + inspect_source(cop, 'before { do_something }') + + expect(cop.offenses.size).to eq(1) + expect(cop.offenses.map(&:line)).to eq([1]) + expect(cop.highlights).to eq(['before { do_something }']) + end + + it 'registers an offense for a single-line `after` block' do + inspect_source(cop, 'after(:each) { undo_something }') + + expect(cop.offenses.size).to eq(1) + expect(cop.offenses.map(&:line)).to eq([1]) + expect(cop.highlights).to eq(['after(:each) { undo_something }']) + end + + it 'registers an offense for a single-line `around` block' do + inspect_source(cop, 'around { |ex| do_something_else }') + + expect(cop.offenses.size).to eq(1) + expect(cop.offenses.map(&:line)).to eq([1]) + expect(cop.highlights).to eq(['around { |ex| do_something_else }']) + end + + it 'ignores a multi-line `before` block' do + inspect_source(cop, ['before do', + ' do_something', + 'end']) + + expect(cop.offenses.size).to eq(0) + end + + it 'ignores a multi-line `after` block' do + inspect_source(cop, ['after(:each) do', + ' undo_something', + 'end']) + + expect(cop.offenses.size).to eq(0) + end + + it 'ignores a multi-line `around` block' do + inspect_source(cop, ['around do |ex|', + ' do_something_else', + 'end']) + + expect(cop.offenses.size).to eq(0) + end +end diff --git a/spec/serializers/build_details_entity_spec.rb b/spec/serializers/build_details_entity_spec.rb index 396ba96e9b3..b92c1c28ba8 100644 --- a/spec/serializers/build_details_entity_spec.rb +++ b/spec/serializers/build_details_entity_spec.rb @@ -3,8 +3,8 @@ require 'spec_helper' describe BuildDetailsEntity do set(:user) { create(:admin) } - it 'inherits from BuildEntity' do - expect(described_class).to be < BuildEntity + it 'inherits from JobEntity' do + expect(described_class).to be < JobEntity end describe '#as_json' do diff --git a/spec/serializers/environment_serializer_spec.rb b/spec/serializers/environment_serializer_spec.rb index d2ad6c44702..4c52a00b442 100644 --- a/spec/serializers/environment_serializer_spec.rb +++ b/spec/serializers/environment_serializer_spec.rb @@ -62,7 +62,9 @@ describe EnvironmentSerializer do subject { serializer.represent(resource) } context 'when there is a single environment' do - before { create(:environment, name: 'staging') } + before do + create(:environment, name: 'staging') + end it 'represents one standalone environment' do expect(subject.count).to eq 1 @@ -138,7 +140,9 @@ describe EnvironmentSerializer do context 'when resource is paginatable relation' do context 'when there is a single environment object in relation' do - before { create(:environment) } + before do + create(:environment) + end it 'serializes environments' do expect(subject.first).to have_key :id @@ -146,7 +150,9 @@ describe EnvironmentSerializer do end context 'when multiple environment objects are serialized' do - before { create_list(:environment, 3) } + before do + create_list(:environment, 3) + end it 'serializes appropriate number of objects' do expect(subject.count).to be 2 diff --git a/spec/serializers/build_entity_spec.rb b/spec/serializers/job_entity_spec.rb index e51ff9fc709..5ca7bf2fcaf 100644 --- a/spec/serializers/build_entity_spec.rb +++ b/spec/serializers/job_entity_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe BuildEntity do +describe JobEntity do let(:user) { create(:user) } - let(:build) { create(:ci_build) } - let(:project) { build.project } + let(:job) { create(:ci_build) } + let(:project) { job.project } let(:request) { double('request') } before do @@ -12,12 +12,12 @@ describe BuildEntity do end let(:entity) do - described_class.new(build, request: request) + described_class.new(job, request: request) end subject { entity.as_json } - it 'contains paths to build page action' do + it 'contains paths to job page action' do expect(subject).to include(:build_path) end @@ -27,7 +27,7 @@ describe BuildEntity do end it 'contains whether it is playable' do - expect(subject[:playable]).to eq build.playable? + expect(subject[:playable]).to eq job.playable? end it 'contains timestamps' do @@ -39,9 +39,9 @@ describe BuildEntity do expect(subject[:status]).to include :icon, :favicon, :text, :label end - context 'when build is retryable' do + context 'when job is retryable' do before do - build.update(status: :failed) + job.update(status: :failed) end it 'contains cancel path' do @@ -49,9 +49,9 @@ describe BuildEntity do end end - context 'when build is cancelable' do + context 'when job is cancelable' do before do - build.update(status: :running) + job.update(status: :running) end it 'contains cancel path' do @@ -59,7 +59,7 @@ describe BuildEntity do end end - context 'when build is a regular build' do + context 'when job is a regular job' do it 'does not contain path to play action' do expect(subject).not_to include(:play_path) end @@ -69,8 +69,8 @@ describe BuildEntity do end end - context 'when build is a manual action' do - let(:build) { create(:ci_build, :manual) } + context 'when job is a manual action' do + let(:job) { create(:ci_build, :manual) } context 'when user is allowed to trigger action' do before do @@ -99,4 +99,25 @@ describe BuildEntity do end end end + + context 'when job is generic commit status' do + let(:job) { create(:generic_commit_status, target_url: 'http://google.com') } + + it 'contains paths to target action' do + expect(subject).to include(:build_path) + end + + it 'does not contain paths to other action paths' do + expect(subject).not_to include(:retry_path, :cancel_path, :play_path) + end + + it 'contains timestamps' do + expect(subject).to include(:created_at, :updated_at) + end + + it 'contains details' do + expect(subject).to include :status + expect(subject[:status]).to include :icon, :favicon, :text, :label + end + end end diff --git a/spec/serializers/pipeline_details_entity_spec.rb b/spec/serializers/pipeline_details_entity_spec.rb index 03cc5ae9b63..d28dec9592a 100644 --- a/spec/serializers/pipeline_details_entity_spec.rb +++ b/spec/serializers/pipeline_details_entity_spec.rb @@ -51,7 +51,9 @@ describe PipelineDetailsEntity do end context 'user has ability to retry pipeline' do - before { project.team << [user, :developer] } + before do + project.team << [user, :developer] + end it 'retryable flag is true' do expect(subject[:flags][:retryable]).to eq true @@ -77,7 +79,9 @@ describe PipelineDetailsEntity do end context 'user has ability to cancel pipeline' do - before { project.add_developer(user) } + before do + project.add_developer(user) + end it 'cancelable flag is true' do expect(subject[:flags][:cancelable]).to eq true @@ -91,6 +95,20 @@ describe PipelineDetailsEntity do end end + context 'when pipeline has commit statuses' do + let(:pipeline) { create(:ci_empty_pipeline) } + + before do + create(:generic_commit_status, pipeline: pipeline) + end + + it 'contains stages' do + expect(subject).to include(:details) + expect(subject[:details]).to include(:stages) + expect(subject[:details][:stages].first).to include(name: 'external') + end + end + context 'when pipeline has YAML errors' do let(:pipeline) do create(:ci_pipeline, config: { rspec: { invalid: :value } }) diff --git a/spec/serializers/pipeline_entity_spec.rb b/spec/serializers/pipeline_entity_spec.rb index a059c2cc736..46650f3a80d 100644 --- a/spec/serializers/pipeline_entity_spec.rb +++ b/spec/serializers/pipeline_entity_spec.rb @@ -51,7 +51,9 @@ describe PipelineEntity do end context 'user has ability to retry pipeline' do - before { project.team << [user, :developer] } + before do + project.team << [user, :developer] + end it 'contains retry path' do expect(subject[:retry_path]).to be_present @@ -77,7 +79,9 @@ describe PipelineEntity do end context 'user has ability to cancel pipeline' do - before { project.add_developer(user) } + before do + project.add_developer(user) + end it 'contains cancel path' do expect(subject[:cancel_path]).to be_present diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb index 34b19fb9fc4..44813656aff 100644 --- a/spec/serializers/pipeline_serializer_spec.rb +++ b/spec/serializers/pipeline_serializer_spec.rb @@ -69,7 +69,9 @@ describe PipelineSerializer do let(:pagination) { { page: 1, per_page: 2 } } context 'when a single pipeline object is present in relation' do - before { create(:ci_empty_pipeline) } + before do + create(:ci_empty_pipeline) + end it 'serializes pipeline relation' do expect(subject.first).to have_key :id @@ -77,7 +79,9 @@ describe PipelineSerializer do end context 'when a multiple pipeline objects are being serialized' do - before { create_list(:ci_empty_pipeline, 3) } + before do + create_list(:ci_empty_pipeline, 3) + end it 'serializes appropriate number of objects' do expect(subject.count).to be 2 diff --git a/spec/serializers/stage_entity_spec.rb b/spec/serializers/stage_entity_spec.rb index 64b3217b809..40e303f7b89 100644 --- a/spec/serializers/stage_entity_spec.rb +++ b/spec/serializers/stage_entity_spec.rb @@ -54,6 +54,17 @@ describe StageEntity do it 'exposes the group key' do expect(subject).to include :groups end + + context 'and contains commit status' do + before do + create(:generic_commit_status, pipeline: pipeline, stage: 'test') + end + + it 'contains commit status' do + groups = subject[:groups].map { |group| group[:name] } + expect(groups).to include('generic') + end + end end end end diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb index e273dfe1552..60cb7a9440f 100644 --- a/spec/services/auth/container_registry_authentication_service_spec.rb +++ b/spec/services/auth/container_registry_authentication_service_spec.rb @@ -34,7 +34,9 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do end context 'for changed configuration' do - before { stub_application_setting(container_registry_token_expire_delay: expire_delay) } + before do + stub_application_setting(container_registry_token_expire_delay: expire_delay) + end it { expect(expires_at).to be_within(2.seconds).of(Time.now + expire_delay.minutes) } end @@ -117,7 +119,9 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do end context 'allow developer to push images' do - before { project.team << [current_user, :developer] } + before do + project.team << [current_user, :developer] + end let(:current_params) do { scope: "repository:#{project.path_with_namespace}:push" } @@ -128,7 +132,9 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do end context 'allow reporter to pull images' do - before { project.team << [current_user, :reporter] } + before do + project.team << [current_user, :reporter] + end context 'when pulling from root level repository' do let(:current_params) do @@ -141,7 +147,9 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do end context 'return a least of privileges' do - before { project.team << [current_user, :reporter] } + before do + project.team << [current_user, :reporter] + end let(:current_params) do { scope: "repository:#{project.path_with_namespace}:push,pull" } @@ -152,7 +160,9 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do end context 'disallow guest to pull or push images' do - before { project.team << [current_user, :guest] } + before do + project.team << [current_user, :guest] + end let(:current_params) do { scope: "repository:#{project.path_with_namespace}:pull,push" } @@ -355,7 +365,9 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do context 'for project without container registry' do let(:project) { create(:empty_project, :public, container_registry_enabled: false) } - before { project.update(container_registry_enabled: false) } + before do + project.update(container_registry_enabled: false) + end context 'disallow when pulling' do let(:current_params) do diff --git a/spec/services/ci/update_build_queue_service_spec.rb b/spec/services/ci/update_build_queue_service_spec.rb index c44e6b2a48b..efefa8e8eca 100644 --- a/spec/services/ci/update_build_queue_service_spec.rb +++ b/spec/services/ci/update_build_queue_service_spec.rb @@ -9,7 +9,9 @@ describe Ci::UpdateBuildQueueService, :services do let(:runner) { create(:ci_runner) } context 'when there are runner that can pick build' do - before { build.project.runners << runner } + before do + build.project.runners << runner + end it 'ticks runner queue value' do expect { subject.execute(build) } @@ -36,7 +38,9 @@ describe Ci::UpdateBuildQueueService, :services do end context 'when there are no runners that can pick build' do - before { build.tag_list = [:docker] } + before do + build.tag_list = [:docker] + end it 'does not tick runner queue value' do expect { subject.execute(build) } diff --git a/spec/services/create_deployment_service_spec.rb b/spec/services/create_deployment_service_spec.rb index 5398b5c3f7e..6cf4342ad4c 100644 --- a/spec/services/create_deployment_service_spec.rb +++ b/spec/services/create_deployment_service_spec.rb @@ -204,7 +204,9 @@ describe CreateDeploymentService, services: true do let(:merge_request) { create(:merge_request, target_branch: 'master', source_branch: 'feature', source_project: project) } context "while updating the 'first_deployed_to_production_at' time" do - before { merge_request.mark_as_merged } + before do + merge_request.mark_as_merged + end context "for merge requests merged before the current deploy" do it "sets the time if the deploy's environment is 'production'" do diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index bcb62429275..fbd9026640c 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -14,7 +14,9 @@ describe Groups::CreateService, '#execute', services: true do end context "cannot create group with restricted visibility level" do - before { allow_any_instance_of(ApplicationSetting).to receive(:restricted_visibility_levels).and_return([Gitlab::VisibilityLevel::PUBLIC]) } + before do + allow_any_instance_of(ApplicationSetting).to receive(:restricted_visibility_levels).and_return([Gitlab::VisibilityLevel::PUBLIC]) + end it { is_expected.not_to be_persisted } end @@ -25,7 +27,9 @@ describe Groups::CreateService, '#execute', services: true do let!(:service) { described_class.new(user, group_params.merge(parent_id: group.id)) } context 'as group owner' do - before { group.add_owner(user) } + before do + group.add_owner(user) + end it { is_expected.to be_persisted } end diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb index dab1a3469f7..370bd352200 100644 --- a/spec/services/issues/create_service_spec.rb +++ b/spec/services/issues/create_service_spec.rb @@ -155,7 +155,9 @@ describe Issues::CreateService, services: true do context 'issue create service' do context 'assignees' do - before { project.team << [user, :master] } + before do + project.team << [user, :master] + end it 'removes assignee when user id is invalid' do opts = { title: 'Title', description: 'Description', assignee_ids: [-1] } diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb index 9f8346d52bb..d1dd1466d95 100644 --- a/spec/services/issues/move_service_spec.rb +++ b/spec/services/issues/move_service_spec.rb @@ -251,12 +251,18 @@ describe Issues::MoveService, services: true do end context 'user is reporter only in new project' do - before { new_project.team << [user, :reporter] } + before do + new_project.team << [user, :reporter] + end + it { expect { move }.to raise_error(StandardError, /permissions/) } end context 'user is reporter only in old project' do - before { old_project.team << [user, :reporter] } + before do + old_project.team << [user, :reporter] + end + it { expect { move }.to raise_error(StandardError, /permissions/) } end diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index a78866a2c32..c26642f5015 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -295,7 +295,9 @@ describe Issues::UpdateService, services: true do end context 'when issue has the `label` label' do - before { issue.labels << label } + before do + issue.labels << label + end it 'does not send notifications for existing labels' do opts = { label_ids: [label.id, label2.id] } @@ -329,7 +331,9 @@ describe Issues::UpdateService, services: true do it { expect(issue.tasks?).to eq(true) } context 'when tasks are marked as completed' do - before { update_issue(description: "- [x] Task 1\n- [X] Task 2") } + before do + update_issue(description: "- [x] Task 1\n- [X] Task 2") + end it 'creates system note about task status change' do note1 = find_note('marked the task **Task 1** as completed') @@ -417,7 +421,9 @@ describe Issues::UpdateService, services: true do context 'when remove_label_ids and label_ids are passed' do let(:params) { { label_ids: [], remove_label_ids: [label.id] } } - before { issue.update_attributes(labels: [label, label3]) } + before do + issue.update_attributes(labels: [label, label3]) + end it 'ignores the label_ids parameter' do expect(result.label_ids).not_to be_empty @@ -431,7 +437,9 @@ describe Issues::UpdateService, services: true do context 'when add_label_ids and remove_label_ids are passed' do let(:params) { { add_label_ids: [label3.id], remove_label_ids: [label.id] } } - before { issue.update_attributes(labels: [label]) } + before do + issue.update_attributes(labels: [label]) + end it 'adds the passed labels' do expect(result.label_ids).to include(label3.id) diff --git a/spec/services/members/create_service_spec.rb b/spec/services/members/create_service_spec.rb index 5ce8e17976b..5a05ab3ea50 100644 --- a/spec/services/members/create_service_spec.rb +++ b/spec/services/members/create_service_spec.rb @@ -5,7 +5,9 @@ describe Members::CreateService, services: true do let(:user) { create(:user) } let(:project_user) { create(:user) } - before { project.team << [user, :master] } + before do + project.team << [user, :master] + end it 'adds user to members' do params = { user_ids: project_user.id.to_s, access_level: Gitlab::Access::GUEST } diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb index 6f9d1208b1d..01ef52396d7 100644 --- a/spec/services/merge_requests/build_service_spec.rb +++ b/spec/services/merge_requests/build_service_spec.rb @@ -206,7 +206,9 @@ describe MergeRequests::BuildService, services: true do context 'branch starts with external issue IID followed by a hyphen' do let(:source_branch) { '12345-fix-issue' } - before { allow(project).to receive(:default_issues_tracker?).and_return(false) } + before do + allow(project).to receive(:default_issues_tracker?).and_return(false) + end it 'sets the title to: Resolves External Issue $issue-iid' do expect(merge_request.title).to eq('Resolve External Issue 12345') diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb index 2963f62cc7d..13fee953e41 100644 --- a/spec/services/merge_requests/create_service_spec.rb +++ b/spec/services/merge_requests/create_service_spec.rb @@ -150,7 +150,9 @@ describe MergeRequests::CreateService, services: true do context 'asssignee_id' do let(:assignee) { create(:user) } - before { project.team << [user, :master] } + before do + project.team << [user, :master] + end it 'removes assignee_id when user id is invalid' do opts = { title: 'Title', description: 'Description', assignee_id: -1 } diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb index d96f819e66a..b3b188a805f 100644 --- a/spec/services/merge_requests/merge_service_spec.rb +++ b/spec/services/merge_requests/merge_service_spec.rb @@ -81,7 +81,9 @@ describe MergeRequests::MergeService, services: true do end context "when jira_issue_transition_id is not present" do - before { allow_any_instance_of(JIRA::Resource::Issue).to receive(:resolution).and_return(nil) } + before do + allow_any_instance_of(JIRA::Resource::Issue).to receive(:resolution).and_return(nil) + end it "does not close issue" do allow(jira_tracker).to receive_messages(jira_issue_transition_id: nil) diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb index 091c193aaa6..fd46020bbdb 100644 --- a/spec/services/merge_requests/update_service_spec.rb +++ b/spec/services/merge_requests/update_service_spec.rb @@ -356,7 +356,9 @@ describe MergeRequests::UpdateService, services: true do end context 'when issue has the `label` label' do - before { merge_request.labels << label } + before do + merge_request.labels << label + end it 'does not send notifications for existing labels' do opts = { label_ids: [label.id, label2.id] } @@ -388,12 +390,16 @@ describe MergeRequests::UpdateService, services: true do end context 'when MergeRequest has tasks' do - before { update_merge_request({ description: "- [ ] Task 1\n- [ ] Task 2" }) } + before do + update_merge_request({ description: "- [ ] Task 1\n- [ ] Task 2" }) + end it { expect(@merge_request.tasks?).to eq(true) } context 'when tasks are marked as completed' do - before { update_merge_request({ description: "- [x] Task 1\n- [X] Task 2" }) } + before do + update_merge_request({ description: "- [x] Task 1\n- [X] Task 2" }) + end it 'creates system note about task status change' do note1 = find_note('marked the task **Task 1** as completed') diff --git a/spec/services/notes/slash_commands_service_spec.rb b/spec/services/notes/slash_commands_service_spec.rb index c9954dc3603..d5ffc1908a9 100644 --- a/spec/services/notes/slash_commands_service_spec.rb +++ b/spec/services/notes/slash_commands_service_spec.rb @@ -6,7 +6,9 @@ describe Notes::SlashCommandsService, services: true do let(:master) { create(:user).tap { |u| project.team << [u, :master] } } let(:assignee) { create(:user) } - before { project.team << [assignee, :master] } + before do + project.team << [assignee, :master] + end end shared_examples 'note on noteable that does not support slash commands' do diff --git a/spec/services/pages_service_spec.rb b/spec/services/pages_service_spec.rb index aa63fe3a5c1..cf38c7c75e5 100644 --- a/spec/services/pages_service_spec.rb +++ b/spec/services/pages_service_spec.rb @@ -10,10 +10,14 @@ describe PagesService, services: true do end context 'execute asynchronously for pages job' do - before { build.name = 'pages' } + before do + build.name = 'pages' + end context 'on success' do - before { build.success } + before do + build.success + end it 'executes worker' do expect(PagesWorker).to receive(:perform_async) @@ -23,7 +27,9 @@ describe PagesService, services: true do %w(pending running failed canceled).each do |status| context "on #{status}" do - before { build.status = status } + before do + build.status = status + end it 'does not execute worker' do expect(PagesWorker).not_to receive(:perform_async) diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index b957517c715..5d2f4cf17fb 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -59,12 +59,16 @@ describe Projects::TransferService, services: true do context 'visibility level' do let(:internal_group) { create(:group, :internal) } - before { internal_group.add_owner(user) } + before do + internal_group.add_owner(user) + end context 'when namespace visibility level < project visibility level' do let(:public_project) { create(:project, :public, :repository, namespace: user.namespace) } - before { transfer_project(public_project, user, internal_group) } + before do + transfer_project(public_project, user, internal_group) + end it { expect(public_project.visibility_level).to eq(internal_group.visibility_level) } end @@ -72,7 +76,9 @@ describe Projects::TransferService, services: true do context 'when namespace visibility level > project visibility level' do let(:private_project) { create(:project, :private, :repository, namespace: user.namespace) } - before { transfer_project(private_project, user, internal_group) } + before do + transfer_project(private_project, user, internal_group) + end it { expect(private_project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE) } end diff --git a/spec/services/slash_commands/interpret_service_spec.rb b/spec/services/slash_commands/interpret_service_spec.rb index 4db491fd5f3..c12fb1a6e53 100644 --- a/spec/services/slash_commands/interpret_service_spec.rb +++ b/spec/services/slash_commands/interpret_service_spec.rb @@ -378,7 +378,9 @@ describe SlashCommands::InterpretService, services: true do context 'assign command with multiple assignees' do let(:content) { "/assign @#{developer.username} @#{developer2.username}" } - before{ project.team << [developer2, :developer] } + before do + project.team << [developer2, :developer] + end context 'Issue' do it 'fetches assignee and populates assignee_id if content contains /assign' do @@ -798,7 +800,11 @@ describe SlashCommands::InterpretService, services: true do context 'if the project has multiple boards' do let(:issuable) { issue } - before { create(:board, project: project) } + + before do + create(:board, project: project) + end + it_behaves_like 'empty command' end diff --git a/spec/services/spam_service_spec.rb b/spec/services/spam_service_spec.rb index 74cba8c014b..5e6e43b7a90 100644 --- a/spec/services/spam_service_spec.rb +++ b/spec/services/spam_service_spec.rb @@ -70,7 +70,9 @@ describe SpamService, services: true do end context 'when not indicated as spam by akismet' do - before { allow(AkismetService).to receive(:new).and_return(double(is_spam?: false)) } + before do + allow(AkismetService).to receive(:new).and_return(double(is_spam?: false)) + end it 'returns false' do expect(check_spam(issue, request, false)).to be_falsey diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a81d3573f8d..01ac3cbd3f6 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -56,6 +56,7 @@ RSpec.configure do |config| config.include StubGitlabCalls config.include StubGitlabData config.include ApiHelpers, :api + config.include Rails.application.routes.url_helpers, type: :routing config.include MigrationsHelpers, :migration config.infer_spec_type_from_file_location! diff --git a/spec/support/kubernetes_helpers.rb b/spec/support/kubernetes_helpers.rb index 9280fad4ace..c92f78b324c 100644 --- a/spec/support/kubernetes_helpers.rb +++ b/spec/support/kubernetes_helpers.rb @@ -1,7 +1,26 @@ module KubernetesHelpers include Gitlab::Kubernetes - def kube_discovery_body + def kube_response(body) + { body: body.to_json } + end + + def kube_pods_response + kube_response(kube_pods_body) + end + + def stub_kubeclient_discover + WebMock.stub_request(:get, service.api_url + '/api/v1').to_return(kube_response(kube_v1_discovery_body)) + end + + def stub_kubeclient_pods(response = nil) + stub_kubeclient_discover + pods_url = service.api_url + "/api/v1/namespaces/#{service.actual_namespace}/pods" + + WebMock.stub_request(:get, pods_url).to_return(response || kube_pods_response) + end + + def kube_v1_discovery_body { "kind" => "APIResourceList", "resources" => [ @@ -10,17 +29,19 @@ module KubernetesHelpers } end - def kube_pods_body(*pods) - { "kind" => "PodList", - "items" => [kube_pod] } + def kube_pods_body + { + "kind" => "PodList", + "items" => [kube_pod] + } end # This is a partial response, it will have many more elements in reality but # these are the ones we care about at the moment - def kube_pod(app: "valid-pod-label") + def kube_pod(name: "kube-pod", app: "valid-pod-label") { "metadata" => { - "name" => "kube-pod", + "name" => name, "creationTimestamp" => "2016-11-25T19:55:19Z", "labels" => { "app" => app } }, diff --git a/spec/support/milestone_tabs_examples.rb b/spec/support/milestone_tabs_examples.rb index 7cfc1e06975..70b499198bf 100644 --- a/spec/support/milestone_tabs_examples.rb +++ b/spec/support/milestone_tabs_examples.rb @@ -15,7 +15,9 @@ shared_examples 'milestone tabs' do describe '#merge_requests' do context 'as html' do - before { go(:merge_requests, format: 'html') } + before do + go(:merge_requests, format: 'html') + end it 'redirects to milestone#show' do expect(response).to redirect_to(milestone_path) @@ -23,7 +25,9 @@ shared_examples 'milestone tabs' do end context 'as json' do - before { go(:merge_requests, format: 'json') } + before do + go(:merge_requests, format: 'json') + end it 'renders the merge requests tab template to a string' do expect(response).to render_template('shared/milestones/_merge_requests_tab') @@ -34,7 +38,9 @@ shared_examples 'milestone tabs' do describe '#participants' do context 'as html' do - before { go(:participants, format: 'html') } + before do + go(:participants, format: 'html') + end it 'redirects to milestone#show' do expect(response).to redirect_to(milestone_path) @@ -42,7 +48,9 @@ shared_examples 'milestone tabs' do end context 'as json' do - before { go(:participants, format: 'json') } + before do + go(:participants, format: 'json') + end it 'renders the participants tab template to a string' do expect(response).to render_template('shared/milestones/_participants_tab') @@ -53,7 +61,9 @@ shared_examples 'milestone tabs' do describe '#labels' do context 'as html' do - before { go(:labels, format: 'html') } + before do + go(:labels, format: 'html') + end it 'redirects to milestone#show' do expect(response).to redirect_to(milestone_path) @@ -61,7 +71,9 @@ shared_examples 'milestone tabs' do end context 'as json' do - before { go(:labels, format: 'json') } + before do + go(:labels, format: 'json') + end it 'renders the labels tab template to a string' do expect(response).to render_template('shared/milestones/_labels_tab') diff --git a/spec/support/reference_parser_shared_examples.rb b/spec/support/reference_parser_shared_examples.rb index 8eb74635a60..bd83cb88058 100644 --- a/spec/support/reference_parser_shared_examples.rb +++ b/spec/support/reference_parser_shared_examples.rb @@ -3,7 +3,9 @@ RSpec.shared_examples "referenced feature visibility" do |*related_features| related_features.map { |feature| (feature + "_access_level").to_sym } end - before { link['data-project'] = project.id.to_s } + before do + link['data-project'] = project.id.to_s + end context "when feature is disabled" do it "does not create reference" do @@ -13,7 +15,9 @@ RSpec.shared_examples "referenced feature visibility" do |*related_features| end context "when feature is enabled only for team members" do - before { set_features_fields_to(ProjectFeature::PRIVATE) } + before do + set_features_fields_to(ProjectFeature::PRIVATE) + end it "does not create reference for non member" do non_member = create(:user) diff --git a/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb b/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb index 1dd3663b944..5e6f9f323a1 100644 --- a/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb +++ b/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb @@ -11,7 +11,9 @@ shared_examples 'new issuable record that supports slash commands' do let(:params) { base_params.merge(defined?(default_params) ? default_params : {}).merge(example_params) } let(:issuable) { described_class.new(project, user, params).execute } - before { project.team << [assignee, :master] } + before do + project.team << [assignee, :master] + end context 'with labels in command only' do let(:example_params) do diff --git a/spec/support/services/issuable_update_service_shared_examples.rb b/spec/support/services/issuable_update_service_shared_examples.rb index 8947f20562f..ffbce6c42bf 100644 --- a/spec/support/services/issuable_update_service_shared_examples.rb +++ b/spec/support/services/issuable_update_service_shared_examples.rb @@ -4,7 +4,9 @@ shared_examples 'issuable update service' do end context 'changing state' do - before { expect(project).to receive(:execute_hooks).once } + before do + expect(project).to receive(:execute_hooks).once + end context 'to reopened' do it 'executes hooks only once' do diff --git a/spec/support/slack_mattermost_notifications_shared_examples.rb b/spec/support/slack_mattermost_notifications_shared_examples.rb index 7e35ebb6c97..a7deb038703 100644 --- a/spec/support/slack_mattermost_notifications_shared_examples.rb +++ b/spec/support/slack_mattermost_notifications_shared_examples.rb @@ -11,14 +11,18 @@ RSpec.shared_examples 'slack or mattermost notifications' do describe 'Validations' do context 'when service is active' do - before { subject.active = true } + before do + subject.active = true + end it { is_expected.to validate_presence_of(:webhook) } it_behaves_like 'issue tracker service URL attribute', :webhook end context 'when service is inactive' do - before { subject.active = false } + before do + subject.active = false + end it { is_expected.not_to validate_presence_of(:webhook) } end diff --git a/spec/support/unique_ip_check_shared_examples.rb b/spec/support/unique_ip_check_shared_examples.rb index 7cf5a65eeed..1986d202c4a 100644 --- a/spec/support/unique_ip_check_shared_examples.rb +++ b/spec/support/unique_ip_check_shared_examples.rb @@ -31,7 +31,9 @@ end shared_examples 'user login operation with unique ip limit' do include_context 'unique ips sign in limit' do - before { current_application_settings.update!(unique_ips_limit_per_user: 1) } + before do + current_application_settings.update!(unique_ips_limit_per_user: 1) + end it 'allows user authenticating from the same ip' do expect { operation_from_ip('ip') }.not_to raise_error @@ -47,7 +49,9 @@ end shared_examples 'user login request with unique ip limit' do |success_status = 200| include_context 'unique ips sign in limit' do - before { current_application_settings.update!(unique_ips_limit_per_user: 1) } + before do + current_application_settings.update!(unique_ips_limit_per_user: 1) + end it 'allows user authenticating from the same ip' do expect(request_from_ip('ip')).to have_http_status(success_status) diff --git a/spec/support/updating_mentions_shared_examples.rb b/spec/support/updating_mentions_shared_examples.rb index e0c59a5c280..eeec3e1d79b 100644 --- a/spec/support/updating_mentions_shared_examples.rb +++ b/spec/support/updating_mentions_shared_examples.rb @@ -2,7 +2,9 @@ RSpec.shared_examples 'updating mentions' do |service_class| let(:mentioned_user) { create(:user) } let(:service_class) { service_class } - before { project.team << [mentioned_user, :developer] } + before do + project.team << [mentioned_user, :developer] + end def update_mentionable(opts) reset_delivered_emails! @@ -15,7 +17,9 @@ RSpec.shared_examples 'updating mentions' do |service_class| end context 'in title' do - before { update_mentionable(title: mentioned_user.to_reference) } + before do + update_mentionable(title: mentioned_user.to_reference) + end it 'emails only the newly-mentioned user' do should_only_email(mentioned_user) @@ -23,7 +27,9 @@ RSpec.shared_examples 'updating mentions' do |service_class| end context 'in description' do - before { update_mentionable(description: mentioned_user.to_reference) } + before do + update_mentionable(description: mentioned_user.to_reference) + end it 'emails only the newly-mentioned user' do should_only_email(mentioned_user) diff --git a/spec/views/projects/diffs/_viewer.html.haml_spec.rb b/spec/views/projects/diffs/_viewer.html.haml_spec.rb new file mode 100644 index 00000000000..32469202508 --- /dev/null +++ b/spec/views/projects/diffs/_viewer.html.haml_spec.rb @@ -0,0 +1,71 @@ +require 'spec_helper' + +describe 'projects/diffs/_viewer.html.haml', :view do + include FakeBlobHelpers + + let(:project) { create(:project, :repository) } + let(:commit) { project.commit('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') } + let(:diff_file) { commit.diffs.diff_file_with_new_path('files/ruby/popen.rb') } + + let(:viewer_class) do + Class.new(DiffViewer::Base) do + include DiffViewer::Rich + + self.partial_name = 'text' + end + end + + let(:viewer) { viewer_class.new(diff_file) } + + before do + assign(:project, project) + + controller.params[:controller] = 'projects/commit' + controller.params[:action] = 'show' + controller.params[:namespace_id] = project.namespace.to_param + controller.params[:project_id] = project.to_param + controller.params[:id] = commit.id + end + + def render_view + render partial: 'projects/diffs/viewer', locals: { viewer: viewer } + end + + context 'when there is a render error' do + before do + allow(viewer).to receive(:render_error).and_return(:too_large) + end + + it 'renders the error' do + render_view + + expect(view).to render_template('projects/diffs/_render_error') + end + end + + context 'when the viewer is collapsed' do + before do + allow(diff_file).to receive(:collapsed?).and_return(true) + end + + it 'renders the collapsed view' do + render_view + + expect(view).to render_template('projects/diffs/_collapsed') + end + end + + context 'when there is no render error' do + it 'prepares the viewer' do + expect(viewer).to receive(:prepare!) + + render_view + end + + it 'renders the viewer' do + render_view + + expect(view).to render_template('projects/diffs/viewers/_text') + end + end +end diff --git a/spec/workers/emails_on_push_worker_spec.rb b/spec/workers/emails_on_push_worker_spec.rb index a0ed85cc0b3..5b6b38e0f76 100644 --- a/spec/workers/emails_on_push_worker_spec.rb +++ b/spec/workers/emails_on_push_worker_spec.rb @@ -71,7 +71,9 @@ describe EmailsOnPushWorker do end context "when there are no errors in sending" do - before { perform } + before do + perform + end it "sends a mail with the correct subject" do expect(email.subject).to include('adds bar folder and branch-test text file') diff --git a/spec/workers/expire_build_artifacts_worker_spec.rb b/spec/workers/expire_build_artifacts_worker_spec.rb index 73cbadc13d9..b47b4a02a68 100644 --- a/spec/workers/expire_build_artifacts_worker_spec.rb +++ b/spec/workers/expire_build_artifacts_worker_spec.rb @@ -5,10 +5,14 @@ describe ExpireBuildArtifactsWorker do let(:worker) { described_class.new } - before { Sidekiq::Worker.clear_all } + before do + Sidekiq::Worker.clear_all + end describe '#perform' do - before { build } + before do + build + end subject! do Sidekiq::Testing.fake! { worker.perform } diff --git a/spec/workers/git_garbage_collect_worker_spec.rb b/spec/workers/git_garbage_collect_worker_spec.rb index 8c5303b61cc..f443bb2c9b4 100644 --- a/spec/workers/git_garbage_collect_worker_spec.rb +++ b/spec/workers/git_garbage_collect_worker_spec.rb @@ -23,7 +23,9 @@ describe GitGarbageCollectWorker do end shared_examples 'gc tasks' do - before { allow(subject).to receive(:bitmaps_enabled?).and_return(bitmaps_enabled) } + before do + allow(subject).to receive(:bitmaps_enabled?).and_return(bitmaps_enabled) + end it 'incremental repack adds a new packfile' do create_objects(project) diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb index 44163c735ba..3c93da63f2e 100644 --- a/spec/workers/post_receive_spec.rb +++ b/spec/workers/post_receive_spec.rb @@ -89,7 +89,9 @@ describe PostReceive do end context "does not create a Ci::Pipeline" do - before { stub_ci_pipeline_yaml_file(nil) } + before do + stub_ci_pipeline_yaml_file(nil) + end it { expect{ subject }.not_to change{ Ci::Pipeline.count } } end diff --git a/spec/workers/stuck_ci_jobs_worker_spec.rb b/spec/workers/stuck_ci_jobs_worker_spec.rb index 8434b0c8e5b..549635f7f33 100644 --- a/spec/workers/stuck_ci_jobs_worker_spec.rb +++ b/spec/workers/stuck_ci_jobs_worker_spec.rb @@ -34,7 +34,9 @@ describe StuckCiJobsWorker do let(:status) { 'pending' } context 'when job is not stuck' do - before { allow_any_instance_of(Ci::Build).to receive(:stuck?).and_return(false) } + before do + allow_any_instance_of(Ci::Build).to receive(:stuck?).and_return(false) + end context 'when job was not updated for more than 1 day ago' do let(:updated_at) { 2.days.ago } @@ -53,7 +55,9 @@ describe StuckCiJobsWorker do end context 'when job is stuck' do - before { allow_any_instance_of(Ci::Build).to receive(:stuck?).and_return(true) } + before do + allow_any_instance_of(Ci::Build).to receive(:stuck?).and_return(true) + end context 'when job was not updated for more than 1 hour ago' do let(:updated_at) { 2.hours.ago } @@ -93,7 +97,9 @@ describe StuckCiJobsWorker do let(:status) { 'running' } let(:updated_at) { 2.days.ago } - before { job.project.update(pending_delete: true) } + before do + job.project.update(pending_delete: true) + end it 'does not drop job' do expect_any_instance_of(Ci::Build).not_to receive(:drop) |