diff options
author | Luke "Jared" Bennett <lbennett@gitlab.com> | 2017-04-05 16:50:44 +0300 |
---|---|---|
committer | Luke "Jared" Bennett <lbennett@gitlab.com> | 2017-04-05 16:51:37 +0300 |
commit | a4bf20f215a70528beb092c82fadd4bca6cf403f (patch) | |
tree | 37f0107b2ece09b03c330c5dbc01b802f53692d8 | |
parent | f47e3f44695e97704fc90012eef82d977df8c5a7 (diff) | |
parent | d4349ba6c4960f50dce7b0beec5f309894dbada9 (diff) |
Merge branch 'master' into 20835-getting-started-better-empty-state-for-activity-tab20835-getting-started-better-empty-state-for-activity-tab
185 files changed, 2546 insertions, 917 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 476307e7076..66f8b6e6f9a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,6 +8,7 @@ cache: variables: MYSQL_ALLOW_EMPTY_PASSWORD: "1" RAILS_ENV: "test" + NODE_ENV: "test" SIMPLECOV: "true" SETUP_DB: "true" USE_BUNDLE_INSTALL: "true" @@ -129,9 +130,7 @@ setup-test-env: stage: prepare script: - node --version - - yarn --version - yarn install --pure-lockfile - - yarn check # ensure that yarn.lock matches package.json - bundle exec rake gitlab:assets:compile - bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init' artifacts: @@ -296,8 +295,6 @@ docs:check:apilint: image: "phusion/baseimage" stage: test <<: *dedicated-runner - variables: - GIT_DEPTH: "3" cache: {} dependencies: [] before_script: [] @@ -308,8 +305,6 @@ docs:check:links: image: "registry.gitlab.com/gitlab-org/gitlab-build-images:nanoc-bootstrap-ruby-2.4-alpine" stage: test <<: *dedicated-runner - variables: - GIT_DEPTH: "3" cache: {} dependencies: [] before_script: [] diff --git a/CHANGELOG.md b/CHANGELOG.md index 734c72f5dd2..3e5475a2296 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,23 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 9.0.3 (2017-04-05) + +- Fix name colision when importing GitHub pull requests from forked repositories. !9719 +- Fix GitHub Importer for PRs of deleted forked repositories. !9992 +- Fix environment folder route when special chars present in environment name. !10250 +- Improve Markdown rendering when a lot of merge requests are referenced. !10252 +- Allow users to import GitHub projects to subgroups. +- Backport API changes needed to fix sticking in EE. +- Remove unnecessary ORDER BY clause from `forked_to_project_id` subquery. (mhasbini) +- Make CI build to use optimistic locking only on status change. +- Fix race condition where a namespace would be deleted before a project was deleted. +- Fix linking to new issue with selected template via url parameter. +- Remove unnecessary ORDER BY clause when updating todos. (mhasbini) +- API: Make the /notes endpoint work with noteable iid instead of id. +- Fixes method not replacing URL parameters correctly and breaking pipelines pagination. +- Move issue, mr, todos next to profile dropdown in top nav. + ## 9.0.2 (2017-03-29) - Correctly update paths when changing a child group. diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 1d0ba9ea182..8f0916f768f 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -0.4.0 +0.5.0 @@ -223,7 +223,7 @@ gem 'oj', '~> 2.17.4' gem 'chronic', '~> 0.10.2' gem 'chronic_duration', '~> 0.10.6' -gem 'webpack-rails', '~> 0.9.9' +gem 'webpack-rails', '~> 0.9.10' gem 'rack-proxy', '~> 0.6.0' gem 'sass-rails', '~> 5.0.6' diff --git a/Gemfile.lock b/Gemfile.lock index 304fc9f2bb3..50ca9af7a7a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -823,8 +823,8 @@ GEM addressable (>= 2.3.6) crack (>= 0.3.2) hashdiff - webpack-rails (0.9.9) - rails (>= 3.2.0) + webpack-rails (0.9.10) + railties (>= 3.2.0) websocket-driver (0.6.3) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.2) @@ -1026,7 +1026,7 @@ DEPENDENCIES virtus (~> 1.0.1) vmstat (~> 2.3.0) webmock (~> 1.24.0) - webpack-rails (~> 0.9.9) + webpack-rails (~> 0.9.10) wikicloth (= 0.8.1) BUNDLED WITH diff --git a/app/assets/javascripts/activities.js b/app/assets/javascripts/activities.js index 36798b793ca..e10db9dc17a 100644 --- a/app/assets/javascripts/activities.js +++ b/app/assets/javascripts/activities.js @@ -15,9 +15,9 @@ class Activities { } pagerCallback(data) { - if (data.count === 0 && this.emptyState) this.emptyState.classList.remove('hidden'); - this.updateTooltips(); - } + if (data.count === 0 && this.emptyState) this.emptyState.classList.remove('hidden'); + this.updateTooltips(); + } updateTooltips() { gl.utils.localTimeAgo($('.js-timeago', '.content_list')); diff --git a/app/assets/javascripts/boards/components/board.js b/app/assets/javascripts/boards/components/board.js index 35b3205cca7..93b8960da2e 100644 --- a/app/assets/javascripts/boards/components/board.js +++ b/app/assets/javascripts/boards/components/board.js @@ -1,7 +1,7 @@ /* eslint-disable comma-dangle, space-before-function-paren, one-var */ /* global Sortable */ - import Vue from 'vue'; +import boardList from './board_list'; import boardBlankState from './board_blank_state'; require('./board_delete'); @@ -16,7 +16,7 @@ require('./board_list'); gl.issueBoards.Board = Vue.extend({ template: '#js-board-template', components: { - 'board-list': gl.issueBoards.BoardList, + boardList, 'board-delete': gl.issueBoards.BoardDelete, boardBlankState, }, diff --git a/app/assets/javascripts/boards/components/board_list.js b/app/assets/javascripts/boards/components/board_list.js index 86e6c26e570..adbd82cb687 100644 --- a/app/assets/javascripts/boards/components/board_list.js +++ b/app/assets/javascripts/boards/components/board_list.js @@ -1,131 +1,197 @@ -/* eslint-disable comma-dangle, space-before-function-paren, max-len */ /* global Sortable */ - -import Vue from 'vue'; import boardNewIssue from './board_new_issue'; import boardCard from './board_card'; +import eventHub from '../eventhub'; -(() => { - const Store = gl.issueBoards.BoardsStore; - - window.gl = window.gl || {}; - window.gl.issueBoards = window.gl.issueBoards || {}; +const Store = gl.issueBoards.BoardsStore; - gl.issueBoards.BoardList = Vue.extend({ - template: '#js-board-list-template', - components: { - boardCard, - boardNewIssue, +export default { + name: 'BoardList', + props: { + disabled: { + type: Boolean, + required: true, }, - props: { - disabled: Boolean, - list: Object, - issues: Array, - loading: Boolean, - issueLinkBase: String, - rootPath: String, + list: { + type: Object, + required: true, }, - data () { - return { - scrollOffset: 250, - filters: Store.state.filters, - showCount: false, - showIssueForm: false - }; + issues: { + type: Array, + required: true, }, - watch: { - filters: { - handler () { - this.list.loadingMore = false; - this.$refs.list.scrollTop = 0; - }, - deep: true - }, - issues () { - this.$nextTick(() => { - if (this.scrollHeight() <= this.listHeight() && this.list.issuesSize > this.list.issues.length) { - this.list.page += 1; - this.list.getIssues(false); - } + loading: { + type: Boolean, + required: true, + }, + issueLinkBase: { + type: String, + required: true, + }, + rootPath: { + type: String, + required: true, + }, + }, + data() { + return { + scrollOffset: 250, + filters: Store.state.filters, + showCount: false, + showIssueForm: false, + }; + }, + components: { + boardCard, + boardNewIssue, + }, + methods: { + listHeight() { + return this.$refs.list.getBoundingClientRect().height; + }, + scrollHeight() { + return this.$refs.list.scrollHeight; + }, + scrollTop() { + return this.$refs.list.scrollTop + this.listHeight(); + }, + loadNextPage() { + const getIssues = this.list.nextPage(); - if (this.scrollHeight() > Math.ceil(this.listHeight())) { - this.showCount = true; - } else { - this.showCount = false; - } + if (getIssues) { + this.list.loadingMore = true; + getIssues.then(() => { + this.list.loadingMore = false; }); } }, - methods: { - listHeight () { - return this.$refs.list.getBoundingClientRect().height; - }, - scrollHeight () { - return this.$refs.list.scrollHeight; - }, - scrollTop () { - return this.$refs.list.scrollTop + this.listHeight(); + toggleForm() { + this.showIssueForm = !this.showIssueForm; + }, + onScroll() { + if ((this.scrollTop() > this.scrollHeight() - this.scrollOffset) && !this.list.loadingMore) { + this.loadNextPage(); + } + }, + }, + watch: { + filters: { + handler() { + this.list.loadingMore = false; + this.$refs.list.scrollTop = 0; }, - loadNextPage () { - const getIssues = this.list.nextPage(); + deep: true, + }, + issues() { + this.$nextTick(() => { + if (this.scrollHeight() <= this.listHeight() && + this.list.issuesSize > this.list.issues.length) { + this.list.page += 1; + this.list.getIssues(false); + } - if (getIssues) { - this.list.loadingMore = true; - getIssues.then(() => { - this.list.loadingMore = false; - }); + if (this.scrollHeight() > Math.ceil(this.listHeight())) { + this.showCount = true; + } else { + this.showCount = false; } - }, - toggleForm() { - this.showIssueForm = !this.showIssueForm; - }, - }, - created() { - gl.IssueBoardsApp.$on(`hide-issue-form-${this.list.id}`, this.toggleForm); + }); }, - mounted () { - const options = gl.issueBoards.getBoardSortableDefaultOptions({ - scroll: document.querySelectorAll('.boards-list')[0], - group: 'issues', - disabled: this.disabled, - filter: '.board-list-count, .is-disabled', - dataIdAttr: 'data-issue-id', - onStart: (e) => { - const card = this.$refs.issue[e.oldIndex]; + }, + created() { + eventHub.$on(`hide-issue-form-${this.list.id}`, this.toggleForm); + }, + mounted() { + const options = gl.issueBoards.getBoardSortableDefaultOptions({ + scroll: document.querySelectorAll('.boards-list')[0], + group: 'issues', + disabled: this.disabled, + filter: '.board-list-count, .is-disabled', + dataIdAttr: 'data-issue-id', + onStart: (e) => { + const card = this.$refs.issue[e.oldIndex]; - card.showDetail = false; - Store.moving.list = card.list; - Store.moving.issue = Store.moving.list.findIssue(+e.item.dataset.issueId); + card.showDetail = false; + Store.moving.list = card.list; + Store.moving.issue = Store.moving.list.findIssue(+e.item.dataset.issueId); - gl.issueBoards.onStart(); - }, - onAdd: (e) => { - gl.issueBoards.BoardsStore.moveIssueToList(Store.moving.list, this.list, Store.moving.issue, e.newIndex); + gl.issueBoards.onStart(); + }, + onAdd: (e) => { + gl.issueBoards.BoardsStore + .moveIssueToList(Store.moving.list, this.list, Store.moving.issue, e.newIndex); - this.$nextTick(() => { - e.item.remove(); - }); - }, - onUpdate: (e) => { - const sortedArray = this.sortable.toArray().filter(id => id !== '-1'); - gl.issueBoards.BoardsStore.moveIssueInList(this.list, Store.moving.issue, e.oldIndex, e.newIndex, sortedArray); - }, - onMove(e) { - return !e.related.classList.contains('board-list-count'); - } - }); + this.$nextTick(() => { + e.item.remove(); + }); + }, + onUpdate: (e) => { + const sortedArray = this.sortable.toArray().filter(id => id !== '-1'); + gl.issueBoards.BoardsStore + .moveIssueInList(this.list, Store.moving.issue, e.oldIndex, e.newIndex, sortedArray); + }, + onMove(e) { + return !e.related.classList.contains('board-list-count'); + }, + }); - this.sortable = Sortable.create(this.$refs.list, options); + this.sortable = Sortable.create(this.$refs.list, options); - // Scroll event on list to load more - this.$refs.list.onscroll = () => { - if ((this.scrollTop() > this.scrollHeight() - this.scrollOffset) && !this.list.loadingMore) { - this.loadNextPage(); - } - }; - }, - beforeDestroy() { - gl.IssueBoardsApp.$off(`hide-issue-form-${this.list.id}`, this.toggleForm); - }, - }); -})(); + // Scroll event on list to load more + this.$refs.list.addEventListener('scroll', this.onScroll); + }, + beforeDestroy() { + eventHub.$off(`hide-issue-form-${this.list.id}`, this.toggleForm); + this.$refs.list.removeEventListener('scroll', this.onScroll); + }, + template: ` + <div class="board-list-component"> + <div + class="board-list-loading text-center" + aria-label="Loading issues" + v-if="loading"> + <i + class="fa fa-spinner fa-spin" + aria-hidden="true"> + </i> + </div> + <board-new-issue + :list="list" + v-if="list.type !== 'closed' && showIssueForm"/> + <ul + class="board-list" + v-show="!loading" + ref="list" + :data-board="list.id" + :class="{ 'is-smaller': showIssueForm }"> + <board-card + v-for="(issue, index) in issues" + ref="issue" + :index="index" + :list="list" + :issue="issue" + :issue-link-base="issueLinkBase" + :root-path="rootPath" + :disabled="disabled" + :key="issue.id" /> + <li + class="board-list-count text-center" + v-if="showCount" + data-id="-1"> + <i + class="fa fa-spinner fa-spin" + aria-label="Loading more issues" + aria-hidden="true" + v-show="list.loadingMore"> + </i> + <span v-if="list.issues.length === list.issuesSize"> + Showing all issues + </span> + <span v-else> + Showing {{ list.issues.length }} of {{ list.issuesSize }} issues + </span> + </li> + </ul> + </div> + `, +}; diff --git a/app/assets/javascripts/boards/components/board_new_issue.js b/app/assets/javascripts/boards/components/board_new_issue.js index b88f59dd6d4..0fa85b6fe14 100644 --- a/app/assets/javascripts/boards/components/board_new_issue.js +++ b/app/assets/javascripts/boards/components/board_new_issue.js @@ -1,4 +1,6 @@ /* global ListIssue */ +import eventHub from '../eventhub'; + const Store = gl.issueBoards.BoardsStore; export default { @@ -49,7 +51,7 @@ export default { }, cancel() { this.title = ''; - gl.IssueBoardsApp.$emit(`hide-issue-form-${this.list.id}`); + eventHub.$emit(`hide-issue-form-${this.list.id}`); }, }, mounted() { diff --git a/app/assets/javascripts/environments/components/environment.js b/app/assets/javascripts/environments/components/environment.js index 51aab8460f6..0518422e475 100644 --- a/app/assets/javascripts/environments/components/environment.js +++ b/app/assets/javascripts/environments/components/environment.js @@ -24,6 +24,7 @@ export default Vue.component('environment-component', { state: store.state, visibility: 'available', isLoading: false, + isLoadingFolderContent: false, cssContainerClass: environmentsData.cssClass, endpoint: environmentsData.environmentsDataEndpoint, canCreateDeployment: environmentsData.canCreateDeployment, @@ -68,15 +69,21 @@ export default Vue.component('environment-component', { this.fetchEnvironments(); eventHub.$on('refreshEnvironments', this.fetchEnvironments); + eventHub.$on('toggleFolder', this.toggleFolder); }, beforeDestroyed() { eventHub.$off('refreshEnvironments'); + eventHub.$off('toggleFolder'); }, methods: { - toggleRow(model) { - return this.store.toggleFolder(model.name); + toggleFolder(folder, folderUrl) { + this.store.toggleFolder(folder); + + if (!folder.isOpen) { + this.fetchChildEnvironments(folder, folderUrl); + } }, /** @@ -117,6 +124,21 @@ export default Vue.component('environment-component', { new Flash('An error occurred while fetching the environments.'); }); }, + + fetchChildEnvironments(folder, folderUrl) { + this.isLoadingFolderContent = true; + + this.service.getFolderContent(folderUrl) + .then(resp => resp.json()) + .then((response) => { + this.store.setfolderContent(folder, response.environments); + this.isLoadingFolderContent = false; + }) + .catch(() => { + this.isLoadingFolderContent = false; + new Flash('An error occurred while fetching the environments.'); + }); + }, }, template: ` @@ -179,7 +201,8 @@ export default Vue.component('environment-component', { :environments="state.environments" :can-create-deployment="canCreateDeploymentParsed" :can-read-environment="canReadEnvironmentParsed" - :service="service"/> + :service="service" + :is-loading-folder-content="isLoadingFolderContent" /> </div> <table-pagination v-if="state.paginationInformation && state.paginationInformation.totalPages > 1" diff --git a/app/assets/javascripts/environments/components/environment_item.js b/app/assets/javascripts/environments/components/environment_item.js index 9c196562c6c..e44d93a30c7 100644 --- a/app/assets/javascripts/environments/components/environment_item.js +++ b/app/assets/javascripts/environments/components/environment_item.js @@ -7,6 +7,7 @@ import RollbackComponent from './environment_rollback'; import TerminalButtonComponent from './environment_terminal_button'; import MonitoringButtonComponent from './environment_monitoring'; import CommitComponent from '../../vue_shared/components/commit'; +import eventHub from '../event_hub'; /** * Envrionment Item Component @@ -410,7 +411,6 @@ export default { folderUrl() { return `${window.location.pathname}/folders/${this.model.folderName}`; }, - }, /** @@ -428,15 +428,37 @@ export default { return true; }, + methods: { + onClickFolder() { + eventHub.$emit('toggleFolder', this.model, this.folderUrl); + }, + }, + template: ` - <tr> + <tr :class="{ 'js-child-row': model.isChildren }"> <td> <a v-if="!model.isFolder" class="environment-name" + :class="{ 'prepend-left-default': model.isChildren }" :href="environmentPath"> {{model.name}} </a> - <a v-else class="folder-name" :href="folderUrl"> + <span v-else + class="folder-name" + @click="onClickFolder" + role="button"> + + <span class="folder-icon"> + <i + v-show="model.isOpen" + class="fa fa-caret-down" + aria-hidden="true" /> + <i + v-show="!model.isOpen" + class="fa fa-caret-right" + aria-hidden="true"/> + </span> + <span class="folder-icon"> <i class="fa fa-folder" aria-hidden="true"></i> </span> @@ -448,7 +470,7 @@ export default { <span class="badge"> {{model.size}} </span> - </a> + </span> </td> <td class="deployment-column"> diff --git a/app/assets/javascripts/environments/components/environments_table.js b/app/assets/javascripts/environments/components/environments_table.js index 338dff40bc9..5e6af3a1d45 100644 --- a/app/assets/javascripts/environments/components/environments_table.js +++ b/app/assets/javascripts/environments/components/environments_table.js @@ -31,6 +31,18 @@ export default { type: Object, required: true, }, + + isLoadingFolderContent: { + type: Boolean, + required: false, + default: false, + }, + }, + + methods: { + folderUrl(model) { + return `${window.location.pathname}/folders/${model.folderName}`; + }, }, template: ` @@ -53,6 +65,31 @@ export default { :can-create-deployment="canCreateDeployment" :can-read-environment="canReadEnvironment" :service="service"></tr> + + <template v-if="model.isFolder && model.isOpen && model.children && model.children.length > 0"> + <tr v-if="isLoadingFolderContent"> + <td colspan="6" class="text-center"> + <i class="fa fa-spin fa-spinner fa-2x" aria-hidden="true"/> + </td> + </tr> + + <template v-else> + <tr is="environment-item" + v-for="children in model.children" + :model="children" + :can-create-deployment="canCreateDeployment" + :can-read-environment="canReadEnvironment" + :service="service"></tr> + + <tr> + <td colspan="6" class="text-center"> + <a :href="folderUrl(model)" class="btn btn-default"> + Show all + </a> + </td> + </tr> + </template> + </template> </template> </tbody> </table> diff --git a/app/assets/javascripts/environments/services/environments_service.js b/app/assets/javascripts/environments/services/environments_service.js index 07040bf0d73..8adb53ea86d 100644 --- a/app/assets/javascripts/environments/services/environments_service.js +++ b/app/assets/javascripts/environments/services/environments_service.js @@ -7,6 +7,7 @@ Vue.use(VueResource); export default class EnvironmentsService { constructor(endpoint) { this.environments = Vue.resource(endpoint); + this.folderResults = 3; } get(scope, page) { @@ -16,4 +17,8 @@ export default class EnvironmentsService { postAction(endpoint) { return Vue.http.post(endpoint, {}, { emulateJSON: true }); } + + getFolderContent(folderUrl) { + return Vue.http.get(`${folderUrl}.json?per_page=${this.folderResults}`); + } } diff --git a/app/assets/javascripts/environments/stores/environments_store.js b/app/assets/javascripts/environments/stores/environments_store.js index 3c3084f3b78..158e7922e3c 100644 --- a/app/assets/javascripts/environments/stores/environments_store.js +++ b/app/assets/javascripts/environments/stores/environments_store.js @@ -38,7 +38,12 @@ export default class EnvironmentsStore { let filtered = {}; if (env.size > 1) { - filtered = Object.assign({}, env, { isFolder: true, folderName: env.name }); + filtered = Object.assign({}, env, { + isFolder: true, + folderName: env.name, + isOpen: false, + children: [], + }); } if (env.latest) { @@ -85,4 +90,67 @@ export default class EnvironmentsStore { this.state.stoppedCounter = count; return count; } + + /** + * Toggles folder open property for the given folder. + * + * @param {Object} folder + * @return {Array} + */ + toggleFolder(folder) { + return this.updateFolder(folder, 'isOpen', !folder.isOpen); + } + + /** + * Updates the folder with the received environments. + * + * + * @param {Object} folder Folder to update + * @param {Array} environments Received environments + * @return {Object} + */ + setfolderContent(folder, environments) { + const updatedEnvironments = environments.map((env) => { + let updated = env; + + if (env.latest) { + updated = Object.assign({}, env, env.latest); + delete updated.latest; + } else { + updated = env; + } + + updated.isChildren = true; + + return updated; + }); + + return this.updateFolder(folder, 'children', updatedEnvironments); + } + + /** + * Given a folder a prop and a new value updates the correct folder. + * + * @param {Object} folder + * @param {String} prop + * @param {String|Boolean|Object|Array} newValue + * @return {Array} + */ + updateFolder(folder, prop, newValue) { + const environments = this.state.environments; + + const updatedEnvironments = environments.map((env) => { + const updateEnv = Object.assign({}, env); + if (env.isFolder && env.id === folder.id) { + updateEnv[prop] = newValue; + } + + return updateEnv; + }); + + this.state.environments = updatedEnvironments; + + return updatedEnvironments; + } + } diff --git a/app/assets/javascripts/lib/utils/number_utils.js b/app/assets/javascripts/lib/utils/number_utils.js new file mode 100644 index 00000000000..e2bf69ee52e --- /dev/null +++ b/app/assets/javascripts/lib/utils/number_utils.js @@ -0,0 +1,34 @@ +/* eslint-disable import/prefer-default-export */ + +/** + * Function that allows a number with an X amount of decimals + * to be formatted in the following fashion: + * * For 1 digit to the left of the decimal point and X digits to the right of it + * * * Show 3 digits to the right + * * For 2 digits to the left of the decimal point and X digits to the right of it + * * * Show 2 digits to the right +*/ +export function formatRelevantDigits(number) { + let digitsLeft = ''; + let relevantDigits = 0; + let formattedNumber = ''; + if (!isNaN(Number(number))) { + digitsLeft = number.split('.')[0]; + switch (digitsLeft.length) { + case 1: + relevantDigits = 3; + break; + case 2: + relevantDigits = 2; + break; + case 3: + relevantDigits = 1; + break; + default: + relevantDigits = 4; + break; + } + formattedNumber = Number(number).toFixed(relevantDigits); + } + return formattedNumber; +} diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index 665a59f3183..5b50bc62876 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -187,6 +187,9 @@ import './visibility_select'; import './wikis'; import './zen_mode'; +// eslint-disable-next-line global-require +if (process.env.NODE_ENV !== 'production') require('./test_utils/'); + document.addEventListener('beforeunload', function () { // Unbind scroll events $(document).off('scroll'); diff --git a/app/assets/javascripts/monitoring/prometheus_graph.js b/app/assets/javascripts/monitoring/prometheus_graph.js index 844a0785bc9..a6ffa0f59de 100644 --- a/app/assets/javascripts/monitoring/prometheus_graph.js +++ b/app/assets/javascripts/monitoring/prometheus_graph.js @@ -1,9 +1,9 @@ -/* eslint-disable no-new*/ +/* eslint-disable no-new */ /* global Flash */ import d3 from 'd3'; import statusCodes from '~/lib/utils/http_status'; -import '../lib/utils/common_utils'; +import { formatRelevantDigits } from '~/lib/utils/number_utils'; import '../flash'; const prometheusGraphsContainer = '.prometheus-graph'; @@ -21,19 +21,19 @@ class PrometheusGraph { const parentContainerWidth = $(prometheusGraphsContainer).parent().width() + extraAddedWidthParent; this.originalWidth = parentContainerWidth; - this.originalHeight = 400; + this.originalHeight = 330; this.width = parentContainerWidth - this.margin.left - this.margin.right; - this.height = 400 - this.margin.top - this.margin.bottom; + this.height = this.originalHeight - this.margin.top - this.margin.bottom; this.backOffRequestCounter = 0; this.configureGraph(); this.init(); } createGraph() { - Object.keys(this.data).forEach((key) => { - const value = this.data[key]; - if (value.length > 0) { - this.plotValues(value, key); + Object.keys(this.graphSpecificProperties).forEach((key) => { + const value = this.graphSpecificProperties[key]; + if (value.data.length > 0) { + this.plotValues(key); } }); } @@ -49,53 +49,56 @@ class PrometheusGraph { }); } - plotValues(valuesToPlot, key) { + plotValues(key) { + const graphSpecifics = this.graphSpecificProperties[key]; + const x = d3.time.scale() .range([0, this.width]); const y = d3.scale.linear() .range([this.height, 0]); - const prometheusGraphContainer = `${prometheusGraphsContainer}[graph-type=${key}]`; + graphSpecifics.xScale = x; + graphSpecifics.yScale = y; - const graphSpecifics = this.graphSpecificProperties[key]; + const prometheusGraphContainer = `${prometheusGraphsContainer}[graph-type=${key}]`; const chart = d3.select(prometheusGraphContainer) - .attr('width', this.width + this.margin.left + this.margin.right) - .attr('height', this.height + this.margin.bottom + this.margin.top) - .append('g') - .attr('transform', `translate(${this.margin.left},${this.margin.top})`); + .attr('width', this.width + this.margin.left + this.margin.right) + .attr('height', this.height + this.margin.bottom + this.margin.top) + .append('g') + .attr('transform', `translate(${this.margin.left},${this.margin.top})`); const axisLabelContainer = d3.select(prometheusGraphContainer) - .attr('width', this.originalWidth + this.marginLabelContainer.left + this.marginLabelContainer.right) - .attr('height', this.originalHeight + this.marginLabelContainer.bottom + this.marginLabelContainer.top) + .attr('width', this.originalWidth) + .attr('height', this.originalHeight) .append('g') .attr('transform', `translate(${this.marginLabelContainer.left},${this.marginLabelContainer.top})`); - x.domain(d3.extent(valuesToPlot, d => d.time)); - y.domain([0, d3.max(valuesToPlot.map(metricValue => metricValue.value))]); + x.domain(d3.extent(graphSpecifics.data, d => d.time)); + y.domain([0, d3.max(graphSpecifics.data.map(metricValue => metricValue.value))]); const xAxis = d3.svg.axis() - .scale(x) - .ticks(this.commonGraphProperties.axis_no_ticks) - .orient('bottom'); + .scale(x) + .ticks(this.commonGraphProperties.axis_no_ticks) + .orient('bottom'); const yAxis = d3.svg.axis() - .scale(y) - .ticks(this.commonGraphProperties.axis_no_ticks) - .tickSize(-this.width) - .orient('left'); + .scale(y) + .ticks(this.commonGraphProperties.axis_no_ticks) + .tickSize(-this.width) + .orient('left'); this.createAxisLabelContainers(axisLabelContainer, key); chart.append('g') - .attr('class', 'x-axis') - .attr('transform', `translate(0,${this.height})`) - .call(xAxis); + .attr('class', 'x-axis') + .attr('transform', `translate(0,${this.height})`) + .call(xAxis); chart.append('g') - .attr('class', 'y-axis') - .call(yAxis); + .attr('class', 'y-axis') + .call(yAxis); const area = d3.svg.area() .x(d => x(d.time)) @@ -108,13 +111,13 @@ class PrometheusGraph { .y(d => y(d.value)); chart.append('path') - .datum(valuesToPlot) - .attr('d', area) - .attr('class', 'metric-area') - .attr('fill', graphSpecifics.area_fill_color); + .datum(graphSpecifics.data) + .attr('d', area) + .attr('class', 'metric-area') + .attr('fill', graphSpecifics.area_fill_color); chart.append('path') - .datum(valuesToPlot) + .datum(graphSpecifics.data) .attr('class', 'metric-line') .attr('stroke', graphSpecifics.line_color) .attr('fill', 'none') @@ -126,7 +129,7 @@ class PrometheusGraph { .attr('class', 'prometheus-graph-overlay') .attr('width', this.width) .attr('height', this.height) - .on('mousemove', this.handleMouseOverGraph.bind(this, x, y, valuesToPlot, chart, prometheusGraphContainer, key)); + .on('mousemove', this.handleMouseOverGraph.bind(this, prometheusGraphContainer)); } // The legends from the metric @@ -134,128 +137,150 @@ class PrometheusGraph { const graphSpecifics = this.graphSpecificProperties[key]; axisLabelContainer.append('line') - .attr('class', 'label-x-axis-line') - .attr('stroke', '#000000') - .attr('stroke-width', '1') - .attr({ - x1: 0, - y1: this.originalHeight - this.marginLabelContainer.top, - x2: this.originalWidth - this.margin.right, - y2: this.originalHeight - this.marginLabelContainer.top, - }); + .attr('class', 'label-x-axis-line') + .attr('stroke', '#000000') + .attr('stroke-width', '1') + .attr({ + x1: 10, + y1: this.originalHeight - this.margin.top, + x2: (this.originalWidth - this.margin.right) + 10, + y2: this.originalHeight - this.margin.top, + }); axisLabelContainer.append('line') - .attr('class', 'label-y-axis-line') - .attr('stroke', '#000000') - .attr('stroke-width', '1') - .attr({ - x1: 0, - y1: 0, - x2: 0, - y2: this.originalHeight - this.marginLabelContainer.top, - }); + .attr('class', 'label-y-axis-line') + .attr('stroke', '#000000') + .attr('stroke-width', '1') + .attr({ + x1: 10, + y1: 0, + x2: 10, + y2: this.originalHeight - this.margin.top, + }); + + axisLabelContainer.append('rect') + .attr('class', 'rect-axis-text') + .attr('x', 0) + .attr('y', 50) + .attr('width', 30) + .attr('height', 150); axisLabelContainer.append('text') - .attr('class', 'label-axis-text') - .attr('text-anchor', 'middle') - .attr('transform', `translate(15, ${(this.originalHeight - this.marginLabelContainer.top) / 2}) rotate(-90)`) - .text(graphSpecifics.graph_legend_title); + .attr('class', 'label-axis-text') + .attr('text-anchor', 'middle') + .attr('transform', `translate(15, ${(this.originalHeight - this.margin.top) / 2}) rotate(-90)`) + .text(graphSpecifics.graph_legend_title); axisLabelContainer.append('rect') - .attr('class', 'rect-axis-text') - .attr('x', (this.originalWidth / 2) - this.margin.right) - .attr('y', this.originalHeight - this.marginLabelContainer.top - 20) - .attr('width', 30) - .attr('height', 80); + .attr('class', 'rect-axis-text') + .attr('x', (this.originalWidth / 2) - this.margin.right) + .attr('y', this.originalHeight - 100) + .attr('width', 30) + .attr('height', 80); axisLabelContainer.append('text') - .attr('class', 'label-axis-text') - .attr('x', (this.originalWidth / 2) - this.margin.right) - .attr('y', this.originalHeight - this.marginLabelContainer.top) - .attr('dy', '.35em') - .text('Time'); + .attr('class', 'label-axis-text') + .attr('x', (this.originalWidth / 2) - this.margin.right) + .attr('y', this.originalHeight - this.margin.top) + .attr('dy', '.35em') + .text('Time'); // Legends // Metric Usage axisLabelContainer.append('rect') - .attr('x', this.originalWidth - 170) - .attr('y', (this.originalHeight / 2) - 60) - .style('fill', graphSpecifics.area_fill_color) - .attr('width', 20) - .attr('height', 35); + .attr('x', this.originalWidth - 170) + .attr('y', (this.originalHeight / 2) - 60) + .style('fill', graphSpecifics.area_fill_color) + .attr('width', 20) + .attr('height', 35); axisLabelContainer.append('text') - .attr('class', 'label-axis-text') - .attr('x', this.originalWidth - 140) - .attr('y', (this.originalHeight / 2) - 50) - .text('Average'); + .attr('class', 'text-metric-title') + .attr('x', this.originalWidth - 140) + .attr('y', (this.originalHeight / 2) - 50) + .text('Average'); axisLabelContainer.append('text') - .attr('class', 'text-metric-usage') - .attr('x', this.originalWidth - 140) - .attr('y', (this.originalHeight / 2) - 25); + .attr('class', 'text-metric-usage') + .attr('x', this.originalWidth - 140) + .attr('y', (this.originalHeight / 2) - 25); } - handleMouseOverGraph(x, y, valuesToPlot, chart, prometheusGraphContainer, key) { + handleMouseOverGraph(prometheusGraphContainer) { const rectOverlay = document.querySelector(`${prometheusGraphContainer} .prometheus-graph-overlay`); - const timeValueFromOverlay = x.invert(d3.mouse(rectOverlay)[0]); - const timeValueIndex = bisectDate(valuesToPlot, timeValueFromOverlay, 1); - const d0 = valuesToPlot[timeValueIndex - 1]; - const d1 = valuesToPlot[timeValueIndex]; - const currentData = timeValueFromOverlay - d0.time > d1.time - timeValueFromOverlay ? d1 : d0; - const maxValueMetric = y(d3.max(valuesToPlot.map(metricValue => metricValue.value))); - const currentTimeCoordinate = x(currentData.time); - const graphSpecifics = this.graphSpecificProperties[key]; - // Remove the current selectors - d3.selectAll(`${prometheusGraphContainer} .selected-metric-line`).remove(); - d3.selectAll(`${prometheusGraphContainer} .circle-metric`).remove(); - d3.selectAll(`${prometheusGraphContainer} .rect-text-metric`).remove(); - d3.selectAll(`${prometheusGraphContainer} .text-metric`).remove(); - - chart.append('line') - .attr('class', 'selected-metric-line') - .attr({ - x1: currentTimeCoordinate, - y1: y(0), - x2: currentTimeCoordinate, - y2: maxValueMetric, - }); + const currentXCoordinate = d3.mouse(rectOverlay)[0]; + + Object.keys(this.graphSpecificProperties).forEach((key) => { + const currentGraphProps = this.graphSpecificProperties[key]; + const timeValueOverlay = currentGraphProps.xScale.invert(currentXCoordinate); + const overlayIndex = bisectDate(currentGraphProps.data, timeValueOverlay, 1); + const d0 = currentGraphProps.data[overlayIndex - 1]; + const d1 = currentGraphProps.data[overlayIndex]; + const evalTime = timeValueOverlay - d0.time > d1.time - timeValueOverlay; + const currentData = evalTime ? d1 : d0; + const currentTimeCoordinate = currentGraphProps.xScale(currentData.time); + const currentPrometheusGraphContainer = `${prometheusGraphsContainer}[graph-type=${key}]`; + const maxValueFromData = d3.max(currentGraphProps.data.map(metricValue => metricValue.value)); + const maxMetricValue = currentGraphProps.yScale(maxValueFromData); + + // Clear up all the pieces of the flag + d3.selectAll(`${currentPrometheusGraphContainer} .selected-metric-line`).remove(); + d3.selectAll(`${currentPrometheusGraphContainer} .circle-metric`).remove(); + d3.selectAll(`${currentPrometheusGraphContainer} .rect-text-metric`).remove(); + d3.selectAll(`${currentPrometheusGraphContainer} .text-metric`).remove(); + + const currentChart = d3.select(currentPrometheusGraphContainer).select('g'); + currentChart.append('line') + .attr('class', 'selected-metric-line') + .attr({ + x1: currentTimeCoordinate, + y1: currentGraphProps.yScale(0), + x2: currentTimeCoordinate, + y2: maxMetricValue, + }); + + currentChart.append('circle') + .attr('class', 'circle-metric') + .attr('fill', currentGraphProps.line_color) + .attr('cx', currentTimeCoordinate) + .attr('cy', currentGraphProps.yScale(currentData.value)) + .attr('r', this.commonGraphProperties.circle_radius_metric); + + // The little box with text + const rectTextMetric = currentChart.append('g') + .attr('class', 'rect-text-metric') + .attr('translate', `(${currentTimeCoordinate}, ${currentGraphProps.yScale(currentData.value)})`); + + rectTextMetric.append('rect') + .attr('class', 'rect-metric') + .attr('x', currentTimeCoordinate + 10) + .attr('y', maxMetricValue) + .attr('width', this.commonGraphProperties.rect_text_width) + .attr('height', this.commonGraphProperties.rect_text_height); + + rectTextMetric.append('text') + .attr('class', 'text-metric') + .attr('x', currentTimeCoordinate + 35) + .attr('y', maxMetricValue + 35) + .text(timeFormat(currentData.time)); + + rectTextMetric.append('text') + .attr('class', 'text-metric-date') + .attr('x', currentTimeCoordinate + 15) + .attr('y', maxMetricValue + 15) + .text(dayFormat(currentData.time)); + + let currentMetricValue = formatRelevantDigits(currentData.value); + if (key === 'cpu_values') { + currentMetricValue = `${currentMetricValue}%`; + } else { + currentMetricValue = `${currentMetricValue} MB`; + } - chart.append('circle') - .attr('class', 'circle-metric') - .attr('fill', graphSpecifics.line_color) - .attr('cx', currentTimeCoordinate) - .attr('cy', y(currentData.value)) - .attr('r', this.commonGraphProperties.circle_radius_metric); - - // The little box with text - const rectTextMetric = chart.append('g') - .attr('class', 'rect-text-metric') - .attr('translate', `(${currentTimeCoordinate}, ${y(currentData.value)})`); - - rectTextMetric.append('rect') - .attr('class', 'rect-metric') - .attr('x', currentTimeCoordinate + 10) - .attr('y', maxValueMetric) - .attr('width', this.commonGraphProperties.rect_text_width) - .attr('height', this.commonGraphProperties.rect_text_height); - - rectTextMetric.append('text') - .attr('class', 'text-metric') - .attr('x', currentTimeCoordinate + 35) - .attr('y', maxValueMetric + 35) - .text(timeFormat(currentData.time)); - - rectTextMetric.append('text') - .attr('class', 'text-metric-date') - .attr('x', currentTimeCoordinate + 15) - .attr('y', maxValueMetric + 15) - .text(dayFormat(currentData.time)); - - // Update the text - d3.select(`${prometheusGraphContainer} .text-metric-usage`) - .text(currentData.value.substring(0, 8)); + d3.select(`${currentPrometheusGraphContainer} .text-metric-usage`) + .text(currentMetricValue); + }); } configureGraph() { @@ -263,12 +288,18 @@ class PrometheusGraph { cpu_values: { area_fill_color: '#edf3fc', line_color: '#5b99f7', - graph_legend_title: 'CPU utilization (%)', + graph_legend_title: 'CPU Usage (Cores)', + data: [], + xScale: {}, + yScale: {}, }, memory_values: { area_fill_color: '#fca326', line_color: '#fc6d26', - graph_legend_title: 'Memory usage (MB)', + graph_legend_title: 'Memory Usage (MB)', + data: [], + xScale: {}, + yScale: {}, }, }; @@ -318,17 +349,17 @@ class PrometheusGraph { } transformData(metricsResponse) { - const metricTypes = {}; Object.keys(metricsResponse.metrics).forEach((key) => { if (key === 'cpu_values' || key === 'memory_values') { const metricValues = (metricsResponse.metrics[key])[0]; - metricTypes[key] = metricValues.values.map(metric => ({ - time: new Date(metric[0] * 1000), - value: metric[1], - })); + if (typeof metricValues !== 'undefined') { + this.graphSpecificProperties[key].data = metricValues.values.map(metric => ({ + time: new Date(metric[0] * 1000), + value: metric[1], + })); + } } }); - this.data = metricTypes; } } diff --git a/app/assets/javascripts/test_utils/index.js b/app/assets/javascripts/test_utils/index.js new file mode 100644 index 00000000000..ef401abce2d --- /dev/null +++ b/app/assets/javascripts/test_utils/index.js @@ -0,0 +1,4 @@ +import simulateDrag from './simulate_drag'; + +// Export to global space for rspec to use +window.simulateDrag = simulateDrag; diff --git a/app/assets/javascripts/test_utils/simulate_drag.js b/app/assets/javascripts/test_utils/simulate_drag.js index d48f2404fa5..e39213cb098 100644 --- a/app/assets/javascripts/test_utils/simulate_drag.js +++ b/app/assets/javascripts/test_utils/simulate_drag.js @@ -1,143 +1,137 @@ -/* eslint-disable wrap-iife, func-names, strict, no-var, vars-on-top, no-param-reassign, object-shorthand, no-shadow, comma-dangle, prefer-template, consistent-return, no-mixed-operators, no-unused-vars, no-unused-expressions, prefer-arrow-callback, max-len */ -(function () { - 'use strict'; - - function simulateEvent(el, type, options) { - var event; - if (!el) return; - var ownerDocument = el.ownerDocument; - - options = options || {}; - - if (/^mouse/.test(type)) { - event = ownerDocument.createEvent('MouseEvents'); - event.initMouseEvent(type, true, true, ownerDocument.defaultView, - options.button, options.screenX, options.screenY, options.clientX, options.clientY, - options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, el); - } else { - event = ownerDocument.createEvent('CustomEvent'); - - event.initCustomEvent(type, true, true, ownerDocument.defaultView, - options.button, options.screenX, options.screenY, options.clientX, options.clientY, - options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, el); - - event.dataTransfer = { - data: {}, - - setData: function (type, val) { - this.data[type] = val; - }, - - getData: function (type) { - return this.data[type]; - } - }; - } - - if (el.dispatchEvent) { - el.dispatchEvent(event); - } else if (el.fireEvent) { - el.fireEvent('on' + type, event); - } - - return event; - } - - function isLast(target) { - var el = typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el; - var children = el.children; - - return children.length - 1 === target.index; +function simulateEvent(el, type, options = {}) { + let event; + if (!el) return null; + + if (/^mouse/.test(type)) { + event = el.ownerDocument.createEvent('MouseEvents'); + event.initMouseEvent(type, true, true, el.ownerDocument.defaultView, + options.button, options.screenX, options.screenY, options.clientX, options.clientY, + options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, el); + } else { + event = el.ownerDocument.createEvent('CustomEvent'); + + event.initCustomEvent(type, true, true, el.ownerDocument.defaultView, + options.button, options.screenX, options.screenY, options.clientX, options.clientY, + options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, el); + + event.dataTransfer = { + data: {}, + + setData(key, val) { + this.data[key] = val; + }, + + getData(key) { + return this.data[key]; + }, + }; } - function getTarget(target) { - var el = typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el; - var children = el.children; - - return ( - children[target.index] || - children[target.index === 'first' ? 0 : -1] || - children[target.index === 'last' ? children.length - 1 : -1] || - el - ); + if (el.dispatchEvent) { + el.dispatchEvent(event); + } else if (el.fireEvent) { + el.fireEvent(`on${type}`, event); } - function getRect(el) { - var rect = el.getBoundingClientRect(); - var width = rect.right - rect.left; - var height = rect.bottom - rect.top + 10; - - return { - x: rect.left, - y: rect.top, - cx: rect.left + width / 2, - cy: rect.top + height / 2, - w: width, - h: height, - hw: width / 2, - wh: height / 2 - }; + return event; +} + +function isLast(target) { + const el = typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el; + const children = el.children; + + return children.length - 1 === target.index; +} + +function getTarget(target) { + const el = typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el; + const children = el.children; + + return ( + children[target.index] || + children[target.index === 'first' ? 0 : -1] || + children[target.index === 'last' ? children.length - 1 : -1] || + el + ); +} + +function getRect(el) { + const rect = el.getBoundingClientRect(); + const width = rect.right - rect.left; + const height = (rect.bottom - rect.top) + 10; + + return { + x: rect.left, + y: rect.top, + cx: rect.left + (width / 2), + cy: rect.top + (height / 2), + w: width, + h: height, + hw: width / 2, + wh: height / 2, + }; +} + +export default function simulateDrag(options) { + const { to, from } = options; + to.el = to.el || from.el; + + const fromEl = getTarget(from); + const toEl = getTarget(to); + const firstEl = getTarget({ + el: to.el, + index: 'first', + }); + const lastEl = getTarget({ + el: options.to.el, + index: 'last', + }); + + const fromRect = getRect(fromEl); + const toRect = getRect(toEl); + const firstRect = getRect(firstEl); + const lastRect = getRect(lastEl); + + const startTime = new Date().getTime(); + const duration = options.duration || 1000; + + simulateEvent(fromEl, 'mousedown', { + button: 0, + clientX: fromRect.cx, + clientY: fromRect.cy, + }); + + if (options.ontap) options.ontap(); + window.SIMULATE_DRAG_ACTIVE = 1; + + if (options.to.index === 0) { + toRect.cy = firstRect.y; + } else if (isLast(options.to)) { + toRect.cy = lastRect.y + lastRect.h + 50; } - function simulateDrag(options, callback) { - options.to.el = options.to.el || options.from.el; + const dragInterval = setInterval(() => { + const progress = (new Date().getTime() - startTime) / duration; + const x = (fromRect.cx + ((toRect.cx - fromRect.cx) * progress)); + const y = (fromRect.cy + ((toRect.cy - fromRect.cy) * progress)); + const overEl = fromEl.ownerDocument.elementFromPoint(x, y); - var fromEl = getTarget(options.from); - var toEl = getTarget(options.to); - var firstEl = getTarget({ - el: options.to.el, - index: 'first' + simulateEvent(overEl, 'mousemove', { + clientX: x, + clientY: y, }); - var lastEl = getTarget({ - el: options.to.el, - index: 'last' - }); - var scrollable = options.scrollable; - - var fromRect = getRect(fromEl); - var toRect = getRect(toEl); - var firstRect = getRect(firstEl); - var lastRect = getRect(lastEl); - - var startTime = new Date().getTime(); - var duration = options.duration || 1000; - simulateEvent(fromEl, 'mousedown', { button: 0 }); - options.ontap && options.ontap(); - window.SIMULATE_DRAG_ACTIVE = 1; - - if (options.to.index === 0) { - toRect.cy = firstRect.y; - } else if (isLast(options.to)) { - toRect.cy = lastRect.y + lastRect.h + 50; - } - var dragInterval = setInterval(function loop() { - var progress = (new Date().getTime() - startTime) / duration; - var x = (fromRect.cx + (toRect.cx - fromRect.cx) * progress) - scrollable.scrollLeft; - var y = (fromRect.cy + (toRect.cy - fromRect.cy) * progress) - scrollable.scrollTop; - var overEl = fromEl.ownerDocument.elementFromPoint(x, y); - - simulateEvent(overEl, 'mousemove', { - clientX: x, - clientY: y - }); - - if (progress >= 1) { - options.ondragend && options.ondragend(); - simulateEvent(toEl, 'mouseup'); - clearInterval(dragInterval); - window.SIMULATE_DRAG_ACTIVE = 0; - } - }, 100); - - return { - target: fromEl, - fromList: fromEl.parentNode, - toList: toEl.parentNode - }; - } - - // Export - window.simulateEvent = simulateEvent; - window.simulateDrag = simulateDrag; -})(); + if (progress >= 1) { + if (options.ondragend) options.ondragend(); + simulateEvent(toEl, 'mouseup'); + clearInterval(dragInterval); + window.SIMULATE_DRAG_ACTIVE = 0; + } + }, 100); + + return { + target: fromEl, + fromList: fromEl.parentNode, + toList: toEl.parentNode, + }; +} diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index baa5a083353..33052106c20 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -296,6 +296,10 @@ margin-top: 110px; } + &.merge-requests .text-content { + margin-top: 40px; + } + &.labels .text-content { margin-top: 70px; } diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index b6168a293e0..7c0fc1008d0 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -46,7 +46,7 @@ } .issue-boards-page { - .page-with-sidebar { + .content-wrapper { padding-bottom: 0; } } @@ -72,7 +72,7 @@ @media (min-width: $screen-sm-min) { height: 475px; // Needed for PhantomJS - height: calc(100vh - 220px); + height: calc(100vh - 222px); min-height: 475px; transition: width .2s; diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index 3d91e0b22d8..6faa3794c83 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -159,6 +159,16 @@ text { fill: $stat-graph-axis-fill; } + + .label-axis-text, + .text-metric-usage { + fill: $black; + font-weight: 500; + } + + .legend-axis-text { + fill: $black; + } } .x-axis path, diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index c2c2f371b87..0fa1f68e034 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -459,20 +459,13 @@ a.deploy-project-label { flex-wrap: wrap; .btn { - margin: 0 10px 10px 0; padding: 8px; + margin-left: 10px; } > div { + margin-bottom: 10px; padding-left: 0; - - &:last-child { - margin-bottom: 0; - - .btn { - margin-right: 0; - } - } } } } diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb index 85ae4985e58..c8a501d7319 100644 --- a/app/controllers/concerns/issuable_collections.rb +++ b/app/controllers/concerns/issuable_collections.rb @@ -15,6 +15,9 @@ module IssuableCollections # a new order into the collection. # We cannot use reorder to not mess up the paginated collection. issuable_ids = issuable_collection.map(&:id) + + return {} if issuable_ids.empty? + issuable_note_count = Note.count_for_collection(issuable_ids, @collection_type) issuable_votes_count = AwardEmoji.votes_for_collection(issuable_ids, @collection_type) issuable_merge_requests_count = diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb index c411c21bb80..8b69c18d689 100644 --- a/app/controllers/groups/application_controller.rb +++ b/app/controllers/groups/application_controller.rb @@ -10,6 +10,7 @@ class Groups::ApplicationController < ApplicationController unless @group id = params[:group_id] || params[:id] @group = Group.find_by_full_path(id) + @group_merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id).execute unless @group && can?(current_user, :read_group, @group) @group = nil diff --git a/app/controllers/import/base_controller.rb b/app/controllers/import/base_controller.rb index eeee027ef2d..9de0297ecfd 100644 --- a/app/controllers/import/base_controller.rb +++ b/app/controllers/import/base_controller.rb @@ -1,17 +1,27 @@ class Import::BaseController < ApplicationController private - def find_or_create_namespace(name, owner) - return current_user.namespace if name == owner + def find_or_create_namespace(names, owner) + return current_user.namespace if names == owner return current_user.namespace unless current_user.can_create_group? - begin - name = params[:target_namespace].presence || name - namespace = Group.create!(name: name, path: name, owner: current_user) - namespace.add_owner(current_user) - namespace - rescue ActiveRecord::RecordNotUnique, ActiveRecord::RecordInvalid - Namespace.find_by_full_path(name) + names = params[:target_namespace].presence || names + full_path_namespace = Namespace.find_by_full_path(names) + + return full_path_namespace if full_path_namespace + + names.split('/').inject(nil) do |parent, name| + begin + namespace = Group.create!(name: name, + path: name, + owner: current_user, + parent: parent) + namespace.add_owner(current_user) + + namespace + rescue ActiveRecord::RecordNotUnique, ActiveRecord::RecordInvalid + Namespace.where(parent: parent).find_by_path_or_name(name) + end end end end diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb index 08713272947..76715e5970d 100644 --- a/app/finders/issues_finder.rb +++ b/app/finders/issues_finder.rb @@ -9,7 +9,7 @@ # state: 'open' or 'closed' or 'all' # group_id: integer # project_id: integer -# milestone_id: integer +# milestone_title: string # assignee_id: integer # search: string # label_name: string diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb index 1eec45d9cb5..42f0ebd774c 100644 --- a/app/finders/merge_requests_finder.rb +++ b/app/finders/merge_requests_finder.rb @@ -9,7 +9,7 @@ # state: 'open' or 'closed' or 'all' # group_id: integer # project_id: integer -# milestone_id: integer +# milestone_title: string # assignee_id: integer # search: string # label_name: string diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index ad0be70c32a..8431c5f228c 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -540,6 +540,8 @@ module Ci end def dependencies + return [] if empty_dependencies? + depended_jobs = depends_on_builds return depended_jobs unless options[:dependencies].present? @@ -549,6 +551,10 @@ module Ci end end + def empty_dependencies? + options[:dependencies]&.empty? + end + private def update_artifacts_size diff --git a/app/models/issue.rb b/app/models/issue.rb index 10a5d9d2a24..472796df9df 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -59,10 +59,6 @@ class Issue < ActiveRecord::Base before_transition any => :closed do |issue| issue.closed_at = Time.zone.now end - - before_transition closed: any do |issue| - issue.closed_at = nil - end end def hook_attrs diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index baee00b8fcd..6ad56b842b2 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -177,6 +177,16 @@ class MergeRequestDiff < ActiveRecord::Base st_commits.count end + def utf8_st_diffs + return [] if st_diffs.blank? + + st_diffs.map do |diff| + diff.each do |k, v| + diff[k] = encode_utf8(v) if v.respond_to?(:encoding) + end + end + end + private # Old GitLab implementations may have generated diffs as ["--broken-diff"]. @@ -270,14 +280,6 @@ class MergeRequestDiff < ActiveRecord::Base project.merge_base_commit(head_commit_sha, start_commit_sha).try(:sha) end - def utf8_st_diffs - st_diffs.map do |diff| - diff.each do |k, v| - diff[k] = encode_utf8(v) if v.respond_to?(:encoding) - end - end - end - # # #save or #update_attributes providing changes on serialized attributes do a lot of # serialization and deserialization calls resulting in bad performance. diff --git a/app/models/project.rb b/app/models/project.rb index 83660d8c431..12fd0668ff8 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -114,6 +114,8 @@ class Project < ActiveRecord::Base has_one :kubernetes_service, dependent: :destroy, inverse_of: :project has_one :prometheus_service, dependent: :destroy, inverse_of: :project has_one :mock_ci_service, dependent: :destroy + has_one :mock_deployment_service, dependent: :destroy + has_one :mock_monitoring_service, dependent: :destroy has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" has_one :forked_from_project, through: :forked_project_link diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb index 02fbd5497fa..9c56518c991 100644 --- a/app/models/project_services/kubernetes_service.rb +++ b/app/models/project_services/kubernetes_service.rb @@ -22,22 +22,21 @@ class KubernetesService < DeploymentService with_options presence: true, if: :activated? do validates :api_url, url: true validates :token - - validates :namespace, - format: { - with: Gitlab::Regex.kubernetes_namespace_regex, - message: Gitlab::Regex.kubernetes_namespace_regex_message, - }, - length: 1..63 end + validates :namespace, + allow_blank: true, + length: 1..63, + if: :activated?, + format: { + with: Gitlab::Regex.kubernetes_namespace_regex, + message: Gitlab::Regex.kubernetes_namespace_regex_message + } + after_save :clear_reactive_cache! def initialize_properties - if properties.nil? - self.properties = {} - self.namespace = "#{project.path}-#{project.id}" if project.present? - end + self.properties = {} if properties.nil? end def title @@ -62,7 +61,7 @@ class KubernetesService < DeploymentService { type: 'text', name: 'namespace', title: 'Kubernetes namespace', - placeholder: 'Kubernetes namespace' }, + placeholder: namespace_placeholder }, { type: 'text', name: 'api_url', title: 'API URL', @@ -92,7 +91,7 @@ class KubernetesService < DeploymentService variables = [ { key: 'KUBE_URL', value: api_url, public: true }, { key: 'KUBE_TOKEN', value: token, public: false }, - { key: 'KUBE_NAMESPACE', value: namespace, public: true } + { key: 'KUBE_NAMESPACE', value: namespace_variable, public: true } ] if ca_pem.present? @@ -135,8 +134,26 @@ class KubernetesService < DeploymentService { pods: pods } end + TEMPLATE_PLACEHOLDER = 'Kubernetes namespace'.freeze + private + def namespace_placeholder + default_namespace || TEMPLATE_PLACEHOLDER + end + + def namespace_variable + if namespace.present? + namespace + else + default_namespace + end + end + + def default_namespace + "#{project.path}-#{project.id}" if project.present? + end + def build_kubeclient!(api_path: 'api', api_version: 'v1') raise "Incomplete settings" unless api_url && namespace && token diff --git a/app/models/project_services/mock_deployment_service.rb b/app/models/project_services/mock_deployment_service.rb new file mode 100644 index 00000000000..59a3811ce5d --- /dev/null +++ b/app/models/project_services/mock_deployment_service.rb @@ -0,0 +1,18 @@ +class MockDeploymentService < DeploymentService + def title + 'Mock deployment' + end + + def description + 'Mock deployment service' + end + + def self.to_param + 'mock_deployment' + end + + # No terminals support + def terminals(environment) + [] + end +end diff --git a/app/models/project_services/mock_monitoring_service.rb b/app/models/project_services/mock_monitoring_service.rb new file mode 100644 index 00000000000..dd04e04e198 --- /dev/null +++ b/app/models/project_services/mock_monitoring_service.rb @@ -0,0 +1,17 @@ +class MockMonitoringService < MonitoringService + def title + 'Mock monitoring' + end + + def description + 'Mock monitoring service' + end + + def self.to_param + 'mock_monitoring' + end + + def metrics(environment) + JSON.parse(File.read(Rails.root + 'spec/fixtures/metrics.json')) + end +end diff --git a/app/models/service.rb b/app/models/service.rb index 54550177744..5a0ec58d193 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -238,7 +238,9 @@ class Service < ActiveRecord::Base slack teamcity ] - service_names << 'mock_ci' if Rails.env.development? + if Rails.env.development? + service_names += %w[mock_ci mock_deployment mock_monitoring] + end service_names.sort_by(&:downcase) end diff --git a/app/models/system_note_metadata.rb b/app/models/system_note_metadata.rb index 5cc66574941..1e6fc837a75 100644 --- a/app/models/system_note_metadata.rb +++ b/app/models/system_note_metadata.rb @@ -1,7 +1,7 @@ class SystemNoteMetadata < ActiveRecord::Base ICON_TYPES = %w[ - commit merge confidentiality status label assignee cross_reference - title time_tracking branch milestone discussion task moved + commit merge confidential visible label assignee cross_reference + title time_tracking branch milestone discussion task moved opened closed merged ].freeze validates :note, presence: true diff --git a/app/services/search/global_service.rb b/app/services/search/global_service.rb index a3c655493a5..c1549df5ac6 100644 --- a/app/services/search/global_service.rb +++ b/app/services/search/global_service.rb @@ -18,7 +18,11 @@ module Search end def scope - @scope ||= %w[issues merge_requests milestones].delete(params[:scope]) { 'projects' } + @scope ||= begin + allowed_scopes = %w[issues merge_requests milestones] + + allowed_scopes.delete(params[:scope]) { 'projects' } + end end end end diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index d3e502b66dd..35cfcc3682e 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -183,7 +183,9 @@ module SystemNoteService body = status.dup body << " via #{source.gfm_reference(project)}" if source - create_note(NoteSummary.new(noteable, project, author, body, action: 'status')) + action = status == 'reopened' ? 'opened' : status + + create_note(NoteSummary.new(noteable, project, author, body, action: action)) end # Called when 'merge when pipeline succeeds' is executed @@ -273,9 +275,15 @@ module SystemNoteService # # Returns the created Note object def change_issue_confidentiality(issue, project, author) - body = issue.confidential ? 'made the issue confidential' : 'made the issue visible to everyone' + if issue.confidential + body = 'made the issue confidential' + action = 'confidential' + else + body = 'made the issue visible to everyone' + action = 'visible' + end - create_note(NoteSummary.new(issue, project, author, body, action: 'confidentiality')) + create_note(NoteSummary.new(issue, project, author, body, action: action)) end # Called when a branch in Noteable is changed diff --git a/app/services/users/create_service.rb b/app/services/users/create_service.rb index 193fcd85896..a847a71a66a 100644 --- a/app/services/users/create_service.rb +++ b/app/services/users/create_service.rb @@ -62,6 +62,7 @@ module Users :email, :external, :force_random_password, + :password_automatically_set, :hide_no_password, :hide_no_ssh_key, :key_id, @@ -85,6 +86,7 @@ module Users [ :email, :email_confirmation, + :password_automatically_set, :name, :password, :username diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml index 6ad76d23df5..8fe0bd149f3 100644 --- a/app/views/groups/merge_requests.html.haml +++ b/app/views/groups/merge_requests.html.haml @@ -1,18 +1,22 @@ - page_title "Merge Requests" -.top-area - = render 'shared/issuable/nav', type: :merge_requests - - if current_user - .nav-controls - = render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New Merge Request" +- if @group_merge_requests.empty? + = render 'shared/empty_states/merge_requests', project_select_button: true +- else + .top-area + = render 'shared/issuable/nav', type: :merge_requests + - if current_user + .nav-controls + = render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request" -= render 'shared/issuable/filter', type: :merge_requests + = render 'shared/issuable/filter', type: :merge_requests -.row-content-block.second-block - Only merge requests from - %strong= @group.name - group are listed here. - - if current_user - To see all merge requests you should visit #{link_to 'dashboard', merge_requests_dashboard_path} page. + .row-content-block.second-block + Only merge requests from + %strong= @group.name + group are listed here. + - if current_user + To see all merge requests you should visit #{link_to 'dashboard', merge_requests_dashboard_path} page. -= render 'shared/merge_requests' + .prepend-top-default + = render 'shared/merge_requests' diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index 8e83b2002b2..33e68bc766e 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -1,8 +1,4 @@ = render "header_title" - -- content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('simulate_drag') if Rails.env.test? - = render 'shared/milestones/top', milestone: @milestone, group: @group = render 'shared/milestones/tabs', milestone: @milestone, show_project_name: true = render 'shared/milestones/sidebar', milestone: @milestone, affix_offset: 102 diff --git a/app/views/projects/boards/_show.html.haml b/app/views/projects/boards/_show.html.haml index added3f669b..7ca0ec8ed2b 100644 --- a/app/views/projects/boards/_show.html.haml +++ b/app/views/projects/boards/_show.html.haml @@ -6,10 +6,8 @@ = page_specific_javascript_bundle_tag('common_vue') = page_specific_javascript_bundle_tag('filtered_search') = page_specific_javascript_bundle_tag('boards') - = page_specific_javascript_bundle_tag('simulate_drag') if Rails.env.test? %script#js-board-template{ type: "text/x-template" }= render "projects/boards/components/board" - %script#js-board-list-template{ type: "text/x-template" }= render "projects/boards/components/board_list" %script#js-board-modal-filter{ type: "text/x-template" }= render "shared/issuable/search_bar", type: :boards_modal = render "projects/issues/head" diff --git a/app/views/projects/boards/components/_board_list.html.haml b/app/views/projects/boards/components/_board_list.html.haml deleted file mode 100644 index 4a0b2110601..00000000000 --- a/app/views/projects/boards/components/_board_list.html.haml +++ /dev/null @@ -1,26 +0,0 @@ -.board-list-component - .board-list-loading.text-center{ "v-if" => "loading" } - = icon("spinner spin") - - if can? current_user, :create_issue, @project - %board-new-issue{ ":list" => "list", - "v-if" => 'list.type !== "closed" && showIssueForm' } - %ul.board-list{ "ref" => "list", - "v-show" => "!loading", - ":data-board" => "list.id", - ":class" => '{ "is-smaller": showIssueForm }' } - %board-card{ "v-for" => "(issue, index) in issues", - "ref" => "issue", - ":index" => "index", - ":list" => "list", - ":issue" => "issue", - ":issue-link-base" => "issueLinkBase", - ":root-path" => "rootPath", - ":disabled" => "disabled", - ":key" => "issue.id" } - %li.board-list-count.text-center{ "v-if" => "showCount", - "data-issue-id" => "-1" } - = icon("spinner spin", "v-show" => "list.loadingMore" ) - %span{ "v-if" => "list.issues.length === list.issuesSize" } - Showing all issues - %span{ "v-else" => true } - Showing {{ list.issues.length }} of {{ list.issuesSize }} issues diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index b78de092a60..160345cfaa5 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -238,6 +238,8 @@ %ul %li Be careful. Renaming a project's repository can have unintended side effects. %li You will need to update your local repositories to point to the new location. + - if @project.deployment_services.any? + %li Your deployment services will be broken, you will need to manually fix the services after renaming. = f.submit 'Rename project', class: "btn btn-warning" - if can?(current_user, :change_namespace, @project) %hr diff --git a/app/views/projects/environments/_external_url.html.haml b/app/views/projects/environments/_external_url.html.haml index bf0f1819073..a82ef5ee5bb 100644 --- a/app/views/projects/environments/_external_url.html.haml +++ b/app/views/projects/environments/_external_url.html.haml @@ -1,3 +1,4 @@ - if environment.external_url && can?(current_user, :read_environment, environment) = link_to environment.external_url, target: '_blank', rel: 'noopener noreferrer', class: 'btn external-url' do = icon('external-link') + View deployment diff --git a/app/views/projects/environments/_metrics_button.html.haml b/app/views/projects/environments/_metrics_button.html.haml index acbac1869fd..e27281d6917 100644 --- a/app/views/projects/environments/_metrics_button.html.haml +++ b/app/views/projects/environments/_metrics_button.html.haml @@ -4,3 +4,4 @@ = link_to environment_metrics_path(environment), title: 'See metrics', class: 'btn metrics-button' do = icon('area-chart') + Monitoring diff --git a/app/views/projects/environments/metrics.html.haml b/app/views/projects/environments/metrics.html.haml index 3b45162df52..92dc58cd38d 100644 --- a/app/views/projects/environments/metrics.html.haml +++ b/app/views/projects/environments/metrics.html.haml @@ -11,7 +11,7 @@ .col-sm-6 %h3.page-title Environment: - = @environment.name + = link_to @environment.name, environment_path(@environment) .col-sm-6 .nav-controls diff --git a/app/views/projects/environments/show.html.haml b/app/views/projects/environments/show.html.haml index f463a429f65..ff6aaebda22 100644 --- a/app/views/projects/environments/show.html.haml +++ b/app/views/projects/environments/show.html.haml @@ -4,9 +4,9 @@ %div{ class: container_class } .top-area.adjust - .col-md-9 + .col-md-7 %h3.page-title= @environment.name - .col-md-3 + .col-md-5 .nav-controls = render 'projects/environments/metrics_button', environment: @environment = render 'projects/environments/terminal_button', environment: @environment diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index 8d4a91cb64c..29f861c09c6 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -3,9 +3,6 @@ - hide_class = '' = render "projects/issues/head" -- content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('simulate_drag') if Rails.env.test? - - if @labels.exists? || @prioritized_labels.exists? %div{ class: container_class } .top-area.adjust diff --git a/app/views/projects/merge_requests/_merge_requests.html.haml b/app/views/projects/merge_requests/_merge_requests.html.haml index fe82f751f53..4e97f74dd6a 100644 --- a/app/views/projects/merge_requests/_merge_requests.html.haml +++ b/app/views/projects/merge_requests/_merge_requests.html.haml @@ -1,8 +1,8 @@ %ul.content-list.mr-list.issuable-list - = render @merge_requests - - if @merge_requests.blank? - %li - .nothing-here-block No merge requests to show + - if @merge_requests.exists? + = render @merge_requests + - else + = render 'shared/empty_states/merge_requests' - if @merge_requests.present? = paginate @merge_requests, theme: "gitlab" diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml index 8a96c8dacf6..64f17ab34b1 100644 --- a/app/views/projects/merge_requests/index.html.haml +++ b/app/views/projects/merge_requests/index.html.haml @@ -7,16 +7,19 @@ - content_for :page_specific_javascripts do = page_specific_javascript_bundle_tag('filtered_search') -%div{ class: container_class } - .top-area - = render 'shared/issuable/nav', type: :merge_requests - .nav-controls - - merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project)) - - if merge_project - = link_to new_namespace_project_merge_request_path(merge_project.namespace, merge_project), class: "btn btn-new", title: "New Merge Request" do - New Merge Request +- if @project.merge_requests.exists? + %div{ class: container_class } + .top-area + = render 'shared/issuable/nav', type: :merge_requests + .nav-controls + - merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project)) + - if merge_project + = link_to new_namespace_project_merge_request_path(merge_project.namespace, merge_project), class: "btn btn-new", title: "New merge request" do + New merge request - = render 'shared/issuable/search_bar', type: :merge_requests + = render 'shared/issuable/search_bar', type: :merge_requests - .merge-requests-holder - = render 'merge_requests' + .merge-requests-holder + = render 'merge_requests' +- else + = render 'shared/empty_states/merge_requests', button_path: new_namespace_project_merge_request_path(@project.namespace, @project) diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index f612b5c7d6b..5249d752585 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -3,9 +3,6 @@ - page_description @milestone.description = render "projects/issues/head" -- content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('simulate_drag') if Rails.env.test? - %div{ class: container_class } .detail-page-header.milestone-page-header .status-box{ class: status_box_class(@milestone) } diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 09ac1fd6794..0c7b53e5a9a 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -78,7 +78,7 @@ - if git_import_enabled? %button.btn.js-toggle-button.import_git{ type: "button" } = icon('git', text: 'Repo by URL') - .import_gitlab_project + .import_gitlab_project.has-tooltip{ data: { container: 'body' } } - if gitlab_project_import_enabled? = link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit' do = icon('gitlab', text: 'GitLab export') @@ -109,6 +109,9 @@ %p Please wait a moment, this page will automatically refresh when ready. :javascript + var importBtnTooltip = "Please enter a valid project name."; + var $importBtnWrapper = $('.import_gitlab_project'); + $('.how_to_import_link').bind('click', function (e) { e.preventDefault(); var import_modal = $(this).next(".modal").show(); @@ -123,15 +126,8 @@ $(".btn_import_gitlab_project").attr("href", _href + '?namespace_id=' + $("#project_namespace_id").val() + '&path=' + $("#project_path").val()); }); - $('.btn_import_gitlab_project').attr('disabled',true) - $('.import_gitlab_project').attr('title', 'Project path and name required.'); - - $('.import_gitlab_project').click(function( event ) { - if($('.btn_import_gitlab_project').attr('disabled')) { - event.preventDefault(); - new Flash("Please enter path and name for the project to be imported to."); - } - }); + $('.btn_import_gitlab_project').attr('disabled', $('#project_path').val().trim().length === 0); + $importBtnWrapper.attr('title', importBtnTooltip); $('#new_project').submit(function(){ var $path = $('#project_path'); @@ -139,13 +135,13 @@ }); $('#project_path').keyup(function(){ - if($(this).val().length !=0) { + if($(this).val().trim().length !== 0) { $('.btn_import_gitlab_project').attr('disabled', false); - $('.import_gitlab_project').attr('title',''); - $(".flash-container").html("") + $importBtnWrapper.attr('title',''); + $importBtnWrapper.removeClass('has-tooltip'); } else { $('.btn_import_gitlab_project').attr('disabled',true); - $('.import_gitlab_project').attr('title', 'Project path and name required.'); + $importBtnWrapper.addClass('has-tooltip'); } }); diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml index c52527332bc..0d2cd4a7476 100644 --- a/app/views/projects/wikis/_form.html.haml +++ b/app/views/projects/wikis/_form.html.haml @@ -1,3 +1,5 @@ +- commit_message = @page.persisted? ? "Update #{@page.title}" : "Create #{@page.title}" + = form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, html: { class: 'form-horizontal wiki-form common-note-form prepend-top-default js-quick-submit' } do |f| = form_errors(@page) @@ -28,7 +30,7 @@ .form-group = f.label :commit_message, class: 'control-label' - .col-sm-10= f.text_field :message, class: 'form-control', rows: 18 + .col-sm-10= f.text_field :message, class: 'form-control', rows: 18, value: commit_message .form-actions - if @page && @page.persisted? diff --git a/app/views/shared/_merge_requests.html.haml b/app/views/shared/_merge_requests.html.haml index b7982b7fe9b..eecbb32e90e 100644 --- a/app/views/shared/_merge_requests.html.haml +++ b/app/views/shared/_merge_requests.html.haml @@ -6,4 +6,4 @@ = paginate @merge_requests, theme: "gitlab" - else - .nothing-here-block No merge requests to show + = render 'shared/empty_states/merge_requests' diff --git a/app/views/shared/empty_states/_merge_requests.html.haml b/app/views/shared/empty_states/_merge_requests.html.haml new file mode 100644 index 00000000000..7f2f99f3406 --- /dev/null +++ b/app/views/shared/empty_states/_merge_requests.html.haml @@ -0,0 +1,22 @@ +- button_path = local_assigns.fetch(:button_path, false) +- project_select_button = local_assigns.fetch(:project_select_button, false) +- has_button = button_path || project_select_button + +.row.empty-state.merge-requests + .col-xs-12{ class: "#{'col-sm-6 pull-right' if has_button}" } + .svg-content + = render 'shared/empty_states/icons/merge_requests.svg' + .col-xs-12{ class: "#{'col-sm-6' if has_button}" } + .text-content + - if has_button + %h4 + Merge requests are a place to propose changes you've made to a project and discuss those changes with others. + %p + Interested parties can even contribute by pushing commits if they want to. + - if project_select_button + = render 'shared/new_project_item_select', path: 'merge_requests/new', label: 'New merge request' + - else + = link_to 'New merge request', button_path, class: 'btn btn-new', title: 'New merge request', id: 'new_merge_request_link' + - else + %h4.text-center + There are no merge requests to show. diff --git a/app/views/shared/empty_states/icons/_merge_requests.svg b/app/views/shared/empty_states/icons/_merge_requests.svg new file mode 100644 index 00000000000..e77f6319a95 --- /dev/null +++ b/app/views/shared/empty_states/icons/_merge_requests.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="755 221 385 225" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><rect id="a" width="278" height="179" rx="10"/><mask id="d" width="278" height="179" x="0" y="0" fill="#fff"><use xlink:href="#a"/></mask><path id="b" d="M13.6 49H57c5.5 0 10-4.5 10-10V10c0-5.5-4.5-10-10-10H10C4.5 0 0 4.5 0 10v42c0 5.5 3.2 7 7.2 3l6.4-6z"/><mask id="e" width="67" height="57.2" x="0" y="0" fill="#fff"><use xlink:href="#b"/></mask><path id="c" d="M13.6 49H57c5.5 0 10-4.5 10-10V10c0-5.5-4.5-10-10-10H10C4.5 0 0 4.5 0 10v42c0 5.5 3.2 7 7.2 3l6.4-6z"/><mask id="f" width="67" height="57.2" x="0" y="0" fill="#fff"><use xlink:href="#c"/></mask></defs><g fill="none" fill-rule="evenodd"><g fill="#F9F9F9" transform="translate(752 227)"><rect width="120" height="22" x="30" rx="11"/><rect width="132" height="22" y="44" rx="11"/><rect width="190" height="22" x="208" y="66" rx="11"/><rect width="158" height="22" x="129" y="197" rx="11"/><rect width="158" height="22" x="66" y="154" rx="11"/><rect width="350" height="22" x="31" y="110" rx="11"/><path d="M153 22H21h21.5c6 0 11 5 11 11s-5 11-11 11H21h132-36.5c-6 0-11-5-11-11s5-11 11-11H153zm252 66H288h36.5c6 0 11 5 11 11s-5 11-11 11H288h117-36.5c-6 0-11-5-11-11s5-11 11-11H405zm-244 44H44h36.5c6 0 11 5 11 11s-5 11-11 11H44h117-36.5c-6 0-11-5-11-11s5-11 11-11H161zm75 44H119h21.5c6 0 11 5 11 11s-5 11-11 11H119h117-51.5c-6 0-11-5-11-11s5-11 11-11H236z"/></g><g transform="translate(812 240)"><use fill="#FFF" stroke="#EEE" stroke-width="8" mask="url(#d)" xlink:href="#a"/><path fill="#EEE" d="M4 29h271v4H4z"/><g transform="translate(34 60)"><rect width="6" height="2" y="1" fill="#B5A7DD" rx="1"/><rect width="15" height="4" x="15" fill="#EEE" rx="2"/><rect width="15" height="4" x="72" fill="#EEE" rx="2"/><rect width="15" height="4" x="39" y="22" fill="#EEE" rx="2"/><rect width="15" height="4" x="53" y="11" fill="#FC6D26" rx="2"/><rect width="20" height="4" x="48" fill="#FC6D26" opacity=".5" rx="2"/><rect width="20" height="4" x="15" y="22" fill="#EEE" rx="2"/><rect width="20" height="4" x="29" y="11" fill="#EEE" rx="2"/><rect width="10" height="4" x="34" fill="#FC6D26" rx="2"/><rect width="10" height="4" x="15" y="11" fill="#EEE" rx="2"/><rect width="6" height="2" y="12" fill="#B5A7DD" rx="1"/><rect width="6" height="2" y="23" fill="#B5A7DD" rx="1"/></g><g transform="translate(34 93)"><rect width="6" height="2" y="1" fill="#B5A7DD" rx="1"/><rect width="15" height="4" x="15" fill="#FC6D26" rx="2"/><rect width="15" height="4" x="72" fill="#EEE" rx="2"/><rect width="15" height="4" x="39" y="22" fill="#FC6D26" opacity=".5" rx="2"/><rect width="15" height="4" x="53" y="11" fill="#EEE" rx="2"/><rect width="20" height="4" x="48" fill="#FC6D26" rx="2"/><rect width="20" height="4" x="15" y="22" fill="#FC6D26" rx="2"/><rect width="20" height="4" x="29" y="11" fill="#EEE" rx="2"/><rect width="10" height="4" x="34" fill="#FC6D26" opacity=".5" rx="2"/><rect width="10" height="4" x="15" y="11" fill="#EEE" rx="2"/><rect width="6" height="2" y="12" fill="#B5A7DD" rx="1"/><rect width="6" height="2" y="23" fill="#B5A7DD" rx="1"/></g><g transform="translate(34 126)"><rect width="6" height="2" y="1" fill="#B5A7DD" rx="1"/><rect width="15" height="4" x="15" fill="#EEE" rx="2"/><rect width="15" height="4" x="72" fill="#EEE" rx="2"/><rect width="15" height="4" x="39" y="22" fill="#EEE" rx="2"/><rect width="15" height="4" x="53" y="11" fill="#EEE" rx="2"/><rect width="20" height="4" x="48" fill="#FC6D26" rx="2"/><rect width="20" height="4" x="15" y="22" fill="#EEE" rx="2"/><rect width="20" height="4" x="29" y="11" fill="#EEE" rx="2"/><rect width="10" height="4" x="34" fill="#FC6D26" opacity=".5" rx="2"/><rect width="10" height="4" x="15" y="11" fill="#EEE" rx="2"/><rect width="6" height="2" y="12" fill="#B5A7DD" rx="1"/><rect width="6" height="2" y="23" fill="#B5A7DD" rx="1"/></g><g transform="translate(157 59)"><rect width="6" height="2" y="1" fill="#FDE5D8" rx="1"/><rect width="15" height="4" x="15" fill="#EEE" rx="2"/><rect width="15" height="4" x="72" fill="#EEE" rx="2"/><rect width="15" height="4" x="39" y="22" fill="#6B4FBB" opacity=".5" rx="2"/><rect width="15" height="4" x="53" y="11" fill="#6B4FBB" rx="2"/><rect width="20" height="4" x="48" fill="#6B4FBB" opacity=".5" rx="2"/><rect width="20" height="4" x="15" y="22" fill="#6B4FBB" rx="2"/><rect width="20" height="4" x="29" y="11" fill="#EEE" rx="2"/><rect width="10" height="4" x="34" fill="#6B4FBB" rx="2"/><rect width="10" height="4" x="15" y="11" fill="#EEE" rx="2"/><rect width="6" height="2" y="12" fill="#FDE5D8" rx="1"/><rect width="6" height="2" y="23" fill="#FDE5D8" rx="1"/><rect width="6" height="2" y="34" fill="#FDE5D8" rx="1"/><rect width="15" height="4" x="15" y="33" fill="#EEE" rx="2"/><rect width="15" height="4" x="58" y="22" fill="#EEE" rx="2"/><rect width="15" height="4" x="39" y="55" fill="#6B4FBB" opacity=".5" rx="2"/><rect width="15" height="4" x="29" y="44" fill="#6B4FBB" rx="2"/><rect width="20" height="4" x="48" y="33" fill="#6B4FBB" rx="2"/><rect width="20" height="4" x="15" y="55" fill="#EEE" rx="2"/><rect width="10" height="4" x="34" y="33" fill="#EEE" rx="2"/><rect width="10" height="4" x="15" y="44" fill="#EEE" rx="2"/><rect width="10" height="4" x="48" y="44" fill="#EEE" rx="2"/><rect width="10" height="4" x="62" y="44" fill="#EEE" rx="2"/><rect width="10" height="4" x="77" y="22" fill="#EEE" rx="2"/><rect width="6" height="2" y="45" fill="#FDE5D8" rx="1"/><rect width="6" height="2" y="56" fill="#FDE5D8" rx="1"/><rect width="6" height="2" y="67" fill="#FDE5D8" rx="1"/><rect width="15" height="4" x="15" y="66" fill="#6B4FBB" rx="2"/><rect width="15" height="4" x="39" y="88" fill="#EEE" rx="2"/><rect width="15" height="4" x="53" y="77" fill="#6B4FBB" opacity=".5" rx="2"/><rect width="20" height="4" x="15" y="88" fill="#EEE" rx="2"/><rect width="20" height="4" x="29" y="77" fill="#6B4FBB" rx="2"/><rect width="10" height="4" x="34" y="66" fill="#EEE" rx="2"/><rect width="10" height="4" x="72" y="77" fill="#EEE" rx="2"/><rect width="10" height="4" x="15" y="77" fill="#EEE" rx="2"/><rect width="6" height="2" y="78" fill="#FDE5D8" rx="1"/><rect width="6" height="2" y="89" fill="#FDE5D8" rx="1"/></g></g><g transform="translate(1057 221)"><use fill="#FFF" stroke="#FDE5D8" stroke-width="8" mask="url(#e)" xlink:href="#b"/><rect width="29" height="3" x="14" y="14" fill="#FDB692" rx="1.5"/><rect width="39" height="3" x="14" y="23" fill="#FDB692" rx="1.5"/><rect width="29" height="3" x="14" y="32" fill="#FDB692" rx="1.5"/></g><g transform="translate(1046 285)"><circle cx="16" cy="15" r="15" fill="#FFF7F4" stroke="#FC6D26" stroke-width="3"/><path stroke="#FC6D26" stroke-width="2" d="M0 14h1c5 0 9.2-2.7 11.4-6.7M14 1V0"/><path stroke="#FC6D26" stroke-width="2" d="M7.8 3c3 4.3 7.8 7 13.2 7 3.3 0 6.3-1 9-2.7"/><circle cx="10.5" cy="17.5" r="1.5" fill="#FC6D26"/><circle cx="21.5" cy="17.5" r="1.5" fill="#FC6D26"/></g><g transform="translate(825 370)"><circle cx="15" cy="16" r="15" fill="#F4F1FA" stroke="#6B4FBB" stroke-width="3"/><path fill="#6B4FBB" d="M25 7h2.7C25 2.8 20.4 0 15 0 9.6 0 5 2.8 2.3 7H5l2.5-3L10 7l2.5-3L15 7l2.5-3L20 7l2.5-3L25 7z"/><circle cx="9.5" cy="17.5" r="1.5" fill="#6B4FBB"/><circle cx="20.5" cy="17.5" r="1.5" fill="#6B4FBB"/></g><g transform="matrix(-1 0 0 1 840 306)"><use fill="#FFF" stroke="#E2DCF2" stroke-width="8" mask="url(#f)" xlink:href="#c"/><rect width="29" height="3" x="24" y="14" fill="#6B4FBB" opacity=".5" rx="1.5"/><rect width="19" height="3" x="34" y="23" fill="#6B4FBB" opacity=".5" rx="1.5"/><rect width="19" height="3" x="34" y="32" fill="#6B4FBB" opacity=".5" rx="1.5"/></g></g></svg> diff --git a/app/views/shared/icons/_activity.svg b/app/views/shared/icons/_activity.svg deleted file mode 100644 index d465504b154..00000000000 --- a/app/views/shared/icons/_activity.svg +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> - <!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch --> - <title>path-1</title> - <desc>Created with Sketch.</desc> - <defs></defs> - <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> - <g id="_activity" fill="#7E7D7D"> - <g id="Page-1"> - <g id="path-1"> - <path d="M5,0 C4.448,0 4,0.448 4,1 L4,3 L1,3 C0.448,3 0,3.448 0,4 L0,9 C0,9.552 0.448,10 1,10 L5,10 L5,8 L11,8 L11,10 L15,10 C15.552,10 16,9.552 16,9 L16,4 C16,3.448 15.552,3 15,3 L12,3 L12,1 C12,0.448 11.552,0 11,0 L5,0 L5,0 L5,0 L5,0 Z M6,2.5 C6,2.224 6.224,2 6.5,2 L9.5,2 C9.776,2 10,2.224 10,2.5 C10,2.776 9.776,3 9.5,3 L6.5,3 C6.224,3 6,2.776 6,2.5 L6,2.5 L6,2.5 L6,2.5 Z M6,11 L10.001,11 L10.001,9 L6,9 L6,11 L6,11 L6,11 L6,11 Z M11,11 L11,12 L5,12 L5,11 L1,11 C0.448,11 0,11.448 0,12 L0,15 C0,15.552 0.448,16 1,16 L15,16 C15.552,16 16,15.552 16,15 L16,12 C16,11.448 15.552,11 15,11 L11,11 L11,11 L11,11 L11,11 Z"></path> - </g> - </g> - </g> - </g> -</svg>
\ No newline at end of file diff --git a/app/views/shared/icons/_commits.svg b/app/views/shared/icons/_commits.svg deleted file mode 100644 index ba9bb89935e..00000000000 --- a/app/views/shared/icons/_commits.svg +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> - <!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch --> - <title>Pasted Image 240</title> - <desc>Created with Sketch.</desc> - <defs></defs> - <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> - <path d="M3,8 C3,5.951 4.236,4.194 6,3.422 L6,0 L1,0 C0.448,0 0,0.448 0,1 L0,15 C0,15.552 0.448,16 1,16 L6,16 L6,12.578 C4.236,11.806 3,10.049 3,8 M7,12.899 L7,16 L9,16 L9,12.899 C8.677,12.965 8.343,13 8,13 C7.657,13 7.323,12.965 7,12.899 M15,0 L10,0 L10,3.422 C11.764,4.194 13,5.951 13,8 C13,10.049 11.764,11.806 10,12.578 L10,16 L15,16 C15.552,16 16,15.552 16,15 L16,1 C16,0.448 15.552,0 15,0 M10,8 C10,9.105 9.105,10 8,10 C6.895,10 6,9.105 6,8 C6,6.895 6.895,6 8,6 C9.105,6 10,6.895 10,8 M4,8 C4,10.209 5.791,12 8,12 C10.209,12 12,10.209 12,8 C12,5.791 10.209,4 8,4 C5.791,4 4,5.791 4,8 M9,3.101 L9,0 L7,0 L7,3.101 C7.323,3.035 7.657,3 8,3 C8.343,3 8.677,3.035 9,3.101" id="Pasted-Image-240" fill="#7E7D7D"></path> - </g> -</svg>
\ No newline at end of file diff --git a/app/views/shared/icons/_contributionanalytics.svg b/app/views/shared/icons/_contributionanalytics.svg deleted file mode 100644 index adf09a14964..00000000000 --- a/app/views/shared/icons/_contributionanalytics.svg +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> - <!-- Generator: Sketch 3.7.2 (28276) - http://www.bohemiancoding.com/sketch --> - <title>Group</title> - <desc>Created with Sketch.</desc> - <defs></defs> - <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> - <g id="Group"> - <path d="M8,0 C3.581,0 0,3.581 0,8 C0,12.419 3.581,16 8,16 C12.419,16 16,12.419 16,8 C16,3.581 12.419,0 8,0 M8,2 C11.308,2 14,4.692 14,8 C14,11.308 11.308,14 8,14 C4.692,14 2,11.308 2,8 C2,4.692 4.692,2 8,2" id="Fill-1" fill="#7E7C7C"></path> - <polygon id="Stroke-6" fill="#7E7C7C" points="2.0197351 9.86809696 6.4567351 6.52409696 5.79233671 6.46815759 9.53233671 10.4271576 9.87070552 10.78534 10.2338016 10.4522494 15.0258016 6.05624938 14.3497984 5.31935062 9.55779844 9.71535062 10.2592633 9.74044241 6.51926329 5.78144241 6.21208651 5.45627854 5.8548649 5.72550304 1.4178649 9.06950304"></polygon> - <path d="M7.0313,6.3928 C7.0313,6.9448 6.5833,7.3928 6.0313,7.3928 C5.4793,7.3928 5.0313,6.9448 5.0313,6.3928 C5.0313,5.8408 5.4793,5.3928 6.0313,5.3928 C6.5833,5.3928 7.0313,5.8408 7.0313,6.3928" id="Fill-8" fill="#FEFEFE"></path> - <path d="M6.5313,6.3928 C6.5313,6.66865763 6.30715763,6.8928 6.0313,6.8928 C5.75544237,6.8928 5.5313,6.66865763 5.5313,6.3928 C5.5313,6.11694237 5.75544237,5.8928 6.0313,5.8928 C6.30715763,5.8928 6.5313,6.11694237 6.5313,6.3928 L6.5313,6.3928 Z M7.5313,6.3928 C7.5313,5.56465763 6.85944237,4.8928 6.0313,4.8928 C5.20315763,4.8928 4.5313,5.56465763 4.5313,6.3928 C4.5313,7.22094237 5.20315763,7.8928 6.0313,7.8928 C6.85944237,7.8928 7.5313,7.22094237 7.5313,6.3928 L7.5313,6.3928 Z" id="Stroke-10" fill="#7E7C7C"></path> - <path d="M10.8854,9.8715 C10.8854,10.4235 10.4374,10.8715 9.8854,10.8715 C9.3334,10.8715 8.8854,10.4235 8.8854,9.8715 C8.8854,9.3195 9.3334,8.8715 9.8854,8.8715 C10.4374,8.8715 10.8854,9.3195 10.8854,9.8715" id="Fill-12" fill="#FEFEFE"></path> - <path d="M10.3854,9.8715 C10.3854,10.1473576 10.1612576,10.3715 9.8854,10.3715 C9.60954237,10.3715 9.3854,10.1473576 9.3854,9.8715 C9.3854,9.59564237 9.60954237,9.3715 9.8854,9.3715 C10.1612576,9.3715 10.3854,9.59564237 10.3854,9.8715 L10.3854,9.8715 Z M11.3854,9.8715 C11.3854,9.04335763 10.7135424,8.3715 9.8854,8.3715 C9.05725763,8.3715 8.3854,9.04335763 8.3854,9.8715 C8.3854,10.6996424 9.05725763,11.3715 9.8854,11.3715 C10.7135424,11.3715 11.3854,10.6996424 11.3854,9.8715 L11.3854,9.8715 Z" id="Stroke-14" fill="#7E7C7C"></path> - </g> - </g> -</svg>
\ No newline at end of file diff --git a/app/views/shared/icons/_delta.svg b/app/views/shared/icons/_delta.svg deleted file mode 100644 index 7c0c0d3999c..00000000000 --- a/app/views/shared/icons/_delta.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg width="14px" height="10px" viewBox="322 21 14 10" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> - <path d="M330.078605,22.8166945 L335.259532,29.6235062 C335.615145,30.0907182 335.412062,30.4694683 334.822641,30.4694683 L331.657805,30.4694683 L324.04678,30.4694683 C323.449879,30.4694683 323.260751,30.0822112 323.609889,29.6235062 L328.790816,22.8166945 C329.146429,22.3494825 329.729467,22.3579895 330.078605,22.8166945 Z" id="delta" stroke="#5C5C5C" stroke-width="1" fill="none"></path> -</svg> diff --git a/app/views/shared/icons/_files.svg b/app/views/shared/icons/_files.svg deleted file mode 100644 index fc378d81e40..00000000000 --- a/app/views/shared/icons/_files.svg +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> - <!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch --> - <title>Pasted Image 237</title> - <desc>Created with Sketch.</desc> - <defs></defs> - <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> - <g id="Pasted-Image-237"> - <path d="M15.1111,16 C15.6021,16 16.0001,15.602 16.0001,15.111 L16.0001,4.444 C15.5341,3.983 12.0671,0.378 11.5551,0 L0.8891,0 C0.3981,0 0.0001,0.398 0.0001,0.889 L0.0001,15.111 C0.0001,15.602 0.3981,16 0.8891,16 L15.1111,16 M14.0001,14.111 L1.8891,14.111 L1.8891,2 L10.8131,2 C11.4451,2.42 13.5811,4.555 14.0001,5.187 L14.0001,14.111" id="Fill-1" fill="#7E7D7D"></path> - <path d="M0.889,0 C0.398,0 0,0.398 0,0.889 L0,15.111 C0,15.602 0.398,16 0.889,16 L15.111,16 C15.602,16 16,15.602 16,15.111 L16,4.445 C15.534,3.983 12.068,0.377 11.555,0 L0.889,0 L0.889,0 Z M1.889,2 L10.813,2 C11.446,2.42 13.581,4.554 14,5.187 L14,14.111 L1.889,14.111 L1.889,2 L1.889,2 Z" id="Clip-4"></path> - <polygon id="Fill-6" fill="#7E7D7D" points="9 7 11 7 11 2 9 2"></polygon> - <polygon id="Clip-9" points="9 7 11 7 11 2.001 9 2.001"></polygon> - <polygon id="Fill-11" fill="#7E7D7D" points="10 7 15.444 7 15.444 5 10 5"></polygon> - <polygon id="Clip-14" points="10 7 15.444 7 15.444 5 10 5"></polygon> - </g> - </g> -</svg>
\ No newline at end of file diff --git a/app/views/shared/icons/_icon_close.svg b/app/views/shared/icons/_icon_close.svg index 9d62012518b..59a6cb32d18 100644 --- a/app/views/shared/icons/_icon_close.svg +++ b/app/views/shared/icons/_icon_close.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15"><path d="M9,7.5l5.83-5.91a.48.48,0,0,0,0-.69L14.11.15a.46.46,0,0,0-.68,0l-5.93,6L1.57.15a.46.46,0,0,0-.68,0L.15.9a.48.48,0,0,0,0,.69L6,7.5.15,13.41a.48.48,0,0,0,0,.69l.74.75a.46.46,0,0,0,.68,0l5.93-6,5.93,6a.46.46,0,0,0,.68,0l.74-.75a.48.48,0,0,0,0-.69Z"/></svg>
\ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15"><path d="M9,7.5l5.83-5.91a.48.48,0,0,0,0-.69L14.11.15a.46.46,0,0,0-.68,0l-5.93,6L1.57.15a.46.46,0,0,0-.68,0L.15.9a.48.48,0,0,0,0,.69L6,7.5.15,13.41a.48.48,0,0,0,0,.69l.74.75a.46.46,0,0,0,.68,0l5.93-6,5.93,6a.46.46,0,0,0,.68,0l.74-.75a.48.48,0,0,0,0-.69Z"/></svg> diff --git a/app/views/shared/icons/_icon_empty_groups.svg b/app/views/shared/icons/_icon_empty_groups.svg index 9228be05f03..cf378145e59 100644 --- a/app/views/shared/icons/_icon_empty_groups.svg +++ b/app/views/shared/icons/_icon_empty_groups.svg @@ -1 +1 @@ -<svg width="249" height="368" viewBox="891 156 249 368" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><rect id="a" width="131" height="162" rx="10"/><mask id="e" x="0" y="0" width="131" height="162" fill="#fff"><use xlink:href="#a"/></mask><path d="M223.616 127.958V108.96c0-4.416-3.584-8-8.005-8h-23.985c-2.778 0-5.98 2.014-7.18 4.5l-5.07 10.5h-49.763c-5.527 0-9.996 4.475-9.996 9.997v53.005c0 5.513 4.475 9.997 9.996 9.997h84.01c5.525 0 9.994-4.477 9.994-9.998v-51.004z" id="b"/><mask id="f" x="0" y="0" width="104" height="88" fill="#fff"><use xlink:href="#b"/></mask><path d="M47 25h.996C53.52 25 58 29.472 58 34.99v20.02C58 60.526 53.52 65 47.996 65H10.004C4.48 65 0 60.528 0 55.01V34.99C0 29.474 4.48 25 10.004 25H11v-7c0-9.94 8.06-18 18-18s18 8.06 18 18v7zm-6 0H17v-7c0-6.627 5.373-12 12-12s12 5.373 12 12v7z" id="c"/><mask id="g" x="0" y="0" width="58" height="65" fill="#fff"><use xlink:href="#c"/></mask><path d="M0 10.008C0 4.48 4.476 0 10 0h218c5.523 0 10 4.473 10 10.008v140.94c0 5.53-4.062 11.882-9.08 14.196l-100.84 46.5c-5.015 2.31-13.142 2.312-18.16 0l-100.84-46.5C4.064 162.832 0 156.484 0 150.95V10.007z" id="d"/><mask id="h" x="0" y="0" width="238" height="213.417" fill="#fff"><use xlink:href="#d"/></mask></defs><g fill="none" fill-rule="evenodd" transform="translate(891 156)"><g transform="rotate(8 -266.528 490.3)"><use stroke="#E5E5E5" mask="url(#e)" stroke-width="8" fill="#FFF" xlink:href="#a"/><rect fill="#FC8A51" x="20" y="31" width="12" height="4" rx="2"/><rect fill="#FC8A51" x="60" y="31" width="12" height="4" rx="2"/><rect fill="#FDE5D8" x="36" y="31" width="20" height="4" rx="2"/><rect fill="#6B4FBB" x="20" y="65" width="20" height="4" rx="2"/><rect fill="#FDE5D8" x="44" y="65" width="20" height="4" rx="2"/><rect fill="#FC8A51" x="36" y="80" width="20" height="4" rx="2"/><rect fill="#FDE5D8" x="20" y="80" width="12" height="4" rx="2"/><rect fill="#FDE5D8" x="20" y="48" width="12" height="4" rx="2"/><rect fill="#FC8A51" x="36" y="48" width="12" height="4" rx="2"/><rect fill="#FDE5D8" x="60" y="80" width="12" height="4" rx="2"/><rect fill="#6B4FBB" x="52" y="48" width="12" height="4" rx="2"/><rect fill="#FDE5D8" x="68" y="48" width="12" height="4" rx="2"/></g><use stroke="#B5A7DD" mask="url(#f)" stroke-width="8" fill="#FFF" transform="rotate(5 171.616 144.96)" xlink:href="#b"/><path d="M58 132c0-9.94 8.06-18 18-18s18 8.06 18 18-8.06 18-18 18-18-8.06-18-18z" fill="#C1E7D0"/><path d="M90.143 132c0-7.81-6.332-14.143-14.143-14.143-7.81 0-14.143 6.332-14.143 14.143 0 7.81 6.332 14.143 14.143 14.143 7.81 0 14.143-6.332 14.143-14.143z" fill="#FFF"/><path d="M74.686 133.875l-3.18-3.18c-.29-.29-.77-.296-1.06-.005l-1.55 1.55c-.287.287-.29.766.004 1.06l4.92 4.92c.504.504 1.32.504 1.823 0l.654-.653 7.804-7.804c.3-.3.29-.77-.005-1.067l-1.578-1.58c-.302-.3-.775-.298-1.068-.004l-6.764 6.763z" fill="#31AF64"/><path d="M4 66c0-9.94 8.06-18 18-18s18 8.06 18 18-8.06 18-18 18S4 75.94 4 66z" fill="#D5ECF7"/><path d="M36.143 66c0-7.81-6.332-14.143-14.143-14.143-7.81 0-14.143 6.332-14.143 14.143 0 7.81 6.332 14.143 14.143 14.143 7.81 0 14.143-6.332 14.143-14.143z" fill="#FFF"/><path d="M22 55.714c5.68 0 10.286 4.605 10.286 10.286 0 5.68-4.605 10.286-10.286 10.286-3.45 0-6.505-1.7-8.37-4.307L22 66V55.714z" fill="#2D9FD8"/><g transform="rotate(-8 748.533 18.147)"><use stroke="#FDE5D8" mask="url(#g)" stroke-width="8" fill="#FFF" xlink:href="#c"/><path d="M31 46.584c1.766-.772 3-2.534 3-4.584 0-2.76-2.24-5-5-5s-5 2.24-5 5c0 2.05 1.234 3.812 3 4.584v3.42c0 1.1.895 1.996 2 1.996 1.112 0 2-.894 2-1.997v-3.42z" fill="#FC8A51"/></g><g transform="translate(0 154)"><use stroke="#E5E5E5" mask="url(#h)" stroke-width="8" fill="#FFF" xlink:href="#d"/><g opacity=".3"><path d="M141.837 104.53l-2.56-7.993-5.074-15.843c-.26-.815-1.398-.815-1.66 0l-5.074 15.843h-16.85l-5.075-15.843c-.26-.815-1.398-.815-1.66 0l-5.073 15.843-2.56 7.993c-.234.73.022 1.528.633 1.98l22.16 16.33 22.16-16.33c.61-.452.866-1.25.632-1.98" fill="#A1A1A1"/><path fill="#5C5C5C" d="M119.044 122.84l8.425-26.303h-16.85l8.424 26.304"/><path fill="#787878" d="M119.044 122.84l-8.425-26.303H98.81l20.232 26.304"/><path fill="#787878" d="M119.044 122.84l8.425-26.303h11.807l-20.233 26.304"/><path d="M98.812 96.537l-2.56 7.993c-.234.73.022 1.528.633 1.98l22.16 16.33L98.81 96.538z" fill="#A1A1A1"/><path d="M98.812 96.537h11.807l-5.075-15.843c-.26-.815-1.398-.815-1.66 0l-5.073 15.843z" fill="#5C5C5C"/><path d="M139.277 96.537l2.56 7.993c.234.73-.022 1.528-.634 1.98l-22.16 16.33 20.234-26.303z" fill="#A1A1A1"/><path d="M139.277 96.537H127.47l5.074-15.843c.26-.815 1.398-.815 1.66 0l5.073 15.843z" fill="#5C5C5C"/></g><path d="M57 18.29c1.105 0 2-.818 2-1.828 0-1.01-.895-1.83-2-1.83H41c-1.105 0-2 .82-2 1.83 0 1.01.895 1.83 2 1.83h16zm36 0c1.105 0 2-.818 2-1.828 0-1.01-.895-1.83-2-1.83H77c-1.105 0-2 .82-2 1.83 0 1.01.895 1.83 2 1.83h16zm36 0c1.105 0 2-.818 2-1.828 0-1.01-.895-1.83-2-1.83h-16c-1.105 0-2 .82-2 1.83 0 1.01.895 1.83 2 1.83h16zm36 0c1.105 0 2-.818 2-1.828 0-1.01-.895-1.83-2-1.83h-16c-1.105 0-2 .82-2 1.83 0 1.01.895 1.83 2 1.83h16zm36 0c1.105 0 2-.818 2-1.828 0-1.01-.895-1.83-2-1.83h-16c-1.105 0-2 .82-2 1.83 0 1.01.895 1.83 2 1.83h16zm17 24.693c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V28.35c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.633zm-202 0c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V28.35c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.633zm202 32.923c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V61.274c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.632zm-202 0c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V61.274c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.632zm202 32.923c0 1.01.895 1.828 2 1.828s2-.82 2-1.83V94.2c0-1.012-.895-1.83-2-1.83s-2 .818-2 1.83v14.63zm-202 0c0 1.01.895 1.828 2 1.828s2-.82 2-1.83V94.2c0-1.012-.895-1.83-2-1.83s-2 .818-2 1.83v14.63zm202 32.922c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V127.12c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.632zm-202 0c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V127.12c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.632zm179.023 19.555c-.988.452-1.388 1.55-.894 2.454.493.904 1.694 1.27 2.682.82l14.31-6.545c.99-.452 1.39-1.55.896-2.454-.494-.902-1.696-1.27-2.684-.817l-14.31 6.544zm-32.2 14.723c-.987.452-1.388 1.55-.894 2.454.493.904 1.695 1.27 2.683.818l14.31-6.544c.99-.45 1.39-1.55.895-2.454-.494-.903-1.695-1.27-2.683-.818l-14.31 6.544zm-32.2 14.724c-.987.45-1.387 1.55-.893 2.454.494.903 1.695 1.27 2.683.818l14.31-6.544c.99-.452 1.39-1.55.896-2.454-.495-.904-1.697-1.27-2.685-.818l-14.31 6.544zm-23.67-2.023l-12.186-5.57c-.987-.452-2.19-.086-2.683.817-.494.904-.093 2.003.895 2.454l12.185 5.573c.754.345 1.57.645 2.438.898 1.052.307 2.177-.224 2.513-1.187.335-.962-.246-1.99-1.298-2.298-.677-.197-1.302-.426-1.864-.684zM62.57 168.437c-.988-.452-2.19-.086-2.683.818-.494.903-.094 2.002.894 2.454l14.31 6.544c.988.45 2.19.085 2.683-.818.494-.904.094-2.003-.894-2.454l-14.312-6.544zm-32.2-14.723c-.988-.452-2.19-.086-2.683.818-.494.904-.093 2.003.895 2.454l14.31 6.544c.988.452 2.19.086 2.684-.818.494-.903.093-2.002-.895-2.454l-14.312-6.543z" fill="#EEE"/></g><g><path d="M104 18c0-9.94 8.06-18 18-18s18 8.06 18 18-8.06 18-18 18-18-8.06-18-18z" fill="#FADFD9"/><path d="M136.143 18c0-7.81-6.332-14.143-14.143-14.143-7.81 0-14.143 6.332-14.143 14.143 0 7.81 6.332 14.143 14.143 14.143 7.81 0 14.143-6.332 14.143-14.143z" fill="#FFF"/><path d="M119.43 8.994c0-.707.57-1.28 1.283-1.28h2.574c.71 0 1.284.57 1.284 1.28v10.298c0 .706-.57 1.28-1.283 1.28h-2.574c-.71 0-1.284-.57-1.284-1.28V8.994zm0 15.433c0-.71.57-1.284 1.283-1.284h2.574c.71 0 1.284.57 1.284 1.284V27c0 .71-.57 1.286-1.283 1.286h-2.574c-.71 0-1.284-.57-1.284-1.285v-2.573z" fill="#E75E40"/></g><g><path d="M213 89c0-9.94 8.06-18 18-18s18 8.06 18 18-8.06 18-18 18-18-8.06-18-18z" fill="#F6D4DC"/><path d="M245.143 89c0-7.81-6.332-14.143-14.143-14.143-7.81 0-14.143 6.332-14.143 14.143 0 7.81 6.332 14.143 14.143 14.143 7.81 0 14.143-6.332 14.143-14.143z" fill="#FFF"/><path d="M231 86.348l-3.603-3.602c-.288-.29-.766-.286-1.063.01l-1.578 1.578c-.3.302-.3.773-.01 1.063L228.348 89l-3.602 3.603c-.29.288-.286.766.01 1.063l1.578 1.578c.302.3.773.3 1.063.01L231 91.652l3.603 3.602c.288.29.766.286 1.063-.01l1.578-1.578c.3-.302.3-.773.01-1.063L233.652 89l3.602-3.603c.29-.288.286-.766-.01-1.063l-1.578-1.578c-.302-.3-.773-.3-1.063-.01L231 86.348z" fill="#D22852"/></g></g></svg>
\ No newline at end of file +<svg width="249" height="368" viewBox="891 156 249 368" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><rect id="a" width="131" height="162" rx="10"/><mask id="e" x="0" y="0" width="131" height="162" fill="#fff"><use xlink:href="#a"/></mask><path d="M223.616 127.958V108.96c0-4.416-3.584-8-8.005-8h-23.985c-2.778 0-5.98 2.014-7.18 4.5l-5.07 10.5h-49.763c-5.527 0-9.996 4.475-9.996 9.997v53.005c0 5.513 4.475 9.997 9.996 9.997h84.01c5.525 0 9.994-4.477 9.994-9.998v-51.004z" id="b"/><mask id="f" x="0" y="0" width="104" height="88" fill="#fff"><use xlink:href="#b"/></mask><path d="M47 25h.996C53.52 25 58 29.472 58 34.99v20.02C58 60.526 53.52 65 47.996 65H10.004C4.48 65 0 60.528 0 55.01V34.99C0 29.474 4.48 25 10.004 25H11v-7c0-9.94 8.06-18 18-18s18 8.06 18 18v7zm-6 0H17v-7c0-6.627 5.373-12 12-12s12 5.373 12 12v7z" id="c"/><mask id="g" x="0" y="0" width="58" height="65" fill="#fff"><use xlink:href="#c"/></mask><path d="M0 10.008C0 4.48 4.476 0 10 0h218c5.523 0 10 4.473 10 10.008v140.94c0 5.53-4.062 11.882-9.08 14.196l-100.84 46.5c-5.015 2.31-13.142 2.312-18.16 0l-100.84-46.5C4.064 162.832 0 156.484 0 150.95V10.007z" id="d"/><mask id="h" x="0" y="0" width="238" height="213.417" fill="#fff"><use xlink:href="#d"/></mask></defs><g fill="none" fill-rule="evenodd" transform="translate(891 156)"><g transform="rotate(8 -266.528 490.3)"><use stroke="#E5E5E5" mask="url(#e)" stroke-width="8" fill="#FFF" xlink:href="#a"/><rect fill="#FC8A51" x="20" y="31" width="12" height="4" rx="2"/><rect fill="#FC8A51" x="60" y="31" width="12" height="4" rx="2"/><rect fill="#FDE5D8" x="36" y="31" width="20" height="4" rx="2"/><rect fill="#6B4FBB" x="20" y="65" width="20" height="4" rx="2"/><rect fill="#FDE5D8" x="44" y="65" width="20" height="4" rx="2"/><rect fill="#FC8A51" x="36" y="80" width="20" height="4" rx="2"/><rect fill="#FDE5D8" x="20" y="80" width="12" height="4" rx="2"/><rect fill="#FDE5D8" x="20" y="48" width="12" height="4" rx="2"/><rect fill="#FC8A51" x="36" y="48" width="12" height="4" rx="2"/><rect fill="#FDE5D8" x="60" y="80" width="12" height="4" rx="2"/><rect fill="#6B4FBB" x="52" y="48" width="12" height="4" rx="2"/><rect fill="#FDE5D8" x="68" y="48" width="12" height="4" rx="2"/></g><use stroke="#B5A7DD" mask="url(#f)" stroke-width="8" fill="#FFF" transform="rotate(5 171.616 144.96)" xlink:href="#b"/><path d="M58 132c0-9.94 8.06-18 18-18s18 8.06 18 18-8.06 18-18 18-18-8.06-18-18z" fill="#C1E7D0"/><path d="M90.143 132c0-7.81-6.332-14.143-14.143-14.143-7.81 0-14.143 6.332-14.143 14.143 0 7.81 6.332 14.143 14.143 14.143 7.81 0 14.143-6.332 14.143-14.143z" fill="#FFF"/><path d="M74.686 133.875l-3.18-3.18c-.29-.29-.77-.296-1.06-.005l-1.55 1.55c-.287.287-.29.766.004 1.06l4.92 4.92c.504.504 1.32.504 1.823 0l.654-.653 7.804-7.804c.3-.3.29-.77-.005-1.067l-1.578-1.58c-.302-.3-.775-.298-1.068-.004l-6.764 6.763z" fill="#31AF64"/><path d="M4 66c0-9.94 8.06-18 18-18s18 8.06 18 18-8.06 18-18 18S4 75.94 4 66z" fill="#D5ECF7"/><path d="M36.143 66c0-7.81-6.332-14.143-14.143-14.143-7.81 0-14.143 6.332-14.143 14.143 0 7.81 6.332 14.143 14.143 14.143 7.81 0 14.143-6.332 14.143-14.143z" fill="#FFF"/><path d="M22 55.714c5.68 0 10.286 4.605 10.286 10.286 0 5.68-4.605 10.286-10.286 10.286-3.45 0-6.505-1.7-8.37-4.307L22 66V55.714z" fill="#2D9FD8"/><g transform="rotate(-8 748.533 18.147)"><use stroke="#FDE5D8" mask="url(#g)" stroke-width="8" fill="#FFF" xlink:href="#c"/><path d="M31 46.584c1.766-.772 3-2.534 3-4.584 0-2.76-2.24-5-5-5s-5 2.24-5 5c0 2.05 1.234 3.812 3 4.584v3.42c0 1.1.895 1.996 2 1.996 1.112 0 2-.894 2-1.997v-3.42z" fill="#FC8A51"/></g><g transform="translate(0 154)"><use stroke="#E5E5E5" mask="url(#h)" stroke-width="8" fill="#FFF" xlink:href="#d"/><g opacity=".3"><path d="M141.837 104.53l-2.56-7.993-5.074-15.843c-.26-.815-1.398-.815-1.66 0l-5.074 15.843h-16.85l-5.075-15.843c-.26-.815-1.398-.815-1.66 0l-5.073 15.843-2.56 7.993c-.234.73.022 1.528.633 1.98l22.16 16.33 22.16-16.33c.61-.452.866-1.25.632-1.98" fill="#A1A1A1"/><path fill="#5C5C5C" d="M119.044 122.84l8.425-26.303h-16.85l8.424 26.304"/><path fill="#787878" d="M119.044 122.84l-8.425-26.303H98.81l20.232 26.304"/><path fill="#787878" d="M119.044 122.84l8.425-26.303h11.807l-20.233 26.304"/><path d="M98.812 96.537l-2.56 7.993c-.234.73.022 1.528.633 1.98l22.16 16.33L98.81 96.538z" fill="#A1A1A1"/><path d="M98.812 96.537h11.807l-5.075-15.843c-.26-.815-1.398-.815-1.66 0l-5.073 15.843z" fill="#5C5C5C"/><path d="M139.277 96.537l2.56 7.993c.234.73-.022 1.528-.634 1.98l-22.16 16.33 20.234-26.303z" fill="#A1A1A1"/><path d="M139.277 96.537H127.47l5.074-15.843c.26-.815 1.398-.815 1.66 0l5.073 15.843z" fill="#5C5C5C"/></g><path d="M57 18.29c1.105 0 2-.818 2-1.828 0-1.01-.895-1.83-2-1.83H41c-1.105 0-2 .82-2 1.83 0 1.01.895 1.83 2 1.83h16zm36 0c1.105 0 2-.818 2-1.828 0-1.01-.895-1.83-2-1.83H77c-1.105 0-2 .82-2 1.83 0 1.01.895 1.83 2 1.83h16zm36 0c1.105 0 2-.818 2-1.828 0-1.01-.895-1.83-2-1.83h-16c-1.105 0-2 .82-2 1.83 0 1.01.895 1.83 2 1.83h16zm36 0c1.105 0 2-.818 2-1.828 0-1.01-.895-1.83-2-1.83h-16c-1.105 0-2 .82-2 1.83 0 1.01.895 1.83 2 1.83h16zm36 0c1.105 0 2-.818 2-1.828 0-1.01-.895-1.83-2-1.83h-16c-1.105 0-2 .82-2 1.83 0 1.01.895 1.83 2 1.83h16zm17 24.693c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V28.35c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.633zm-202 0c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V28.35c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.633zm202 32.923c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V61.274c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.632zm-202 0c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V61.274c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.632zm202 32.923c0 1.01.895 1.828 2 1.828s2-.82 2-1.83V94.2c0-1.012-.895-1.83-2-1.83s-2 .818-2 1.83v14.63zm-202 0c0 1.01.895 1.828 2 1.828s2-.82 2-1.83V94.2c0-1.012-.895-1.83-2-1.83s-2 .818-2 1.83v14.63zm202 32.922c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V127.12c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.632zm-202 0c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V127.12c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.632zm179.023 19.555c-.988.452-1.388 1.55-.894 2.454.493.904 1.694 1.27 2.682.82l14.31-6.545c.99-.452 1.39-1.55.896-2.454-.494-.902-1.696-1.27-2.684-.817l-14.31 6.544zm-32.2 14.723c-.987.452-1.388 1.55-.894 2.454.493.904 1.695 1.27 2.683.818l14.31-6.544c.99-.45 1.39-1.55.895-2.454-.494-.903-1.695-1.27-2.683-.818l-14.31 6.544zm-32.2 14.724c-.987.45-1.387 1.55-.893 2.454.494.903 1.695 1.27 2.683.818l14.31-6.544c.99-.452 1.39-1.55.896-2.454-.495-.904-1.697-1.27-2.685-.818l-14.31 6.544zm-23.67-2.023l-12.186-5.57c-.987-.452-2.19-.086-2.683.817-.494.904-.093 2.003.895 2.454l12.185 5.573c.754.345 1.57.645 2.438.898 1.052.307 2.177-.224 2.513-1.187.335-.962-.246-1.99-1.298-2.298-.677-.197-1.302-.426-1.864-.684zM62.57 168.437c-.988-.452-2.19-.086-2.683.818-.494.903-.094 2.002.894 2.454l14.31 6.544c.988.45 2.19.085 2.683-.818.494-.904.094-2.003-.894-2.454l-14.312-6.544zm-32.2-14.723c-.988-.452-2.19-.086-2.683.818-.494.904-.093 2.003.895 2.454l14.31 6.544c.988.452 2.19.086 2.684-.818.494-.903.093-2.002-.895-2.454l-14.312-6.543z" fill="#EEE"/></g><g><path d="M104 18c0-9.94 8.06-18 18-18s18 8.06 18 18-8.06 18-18 18-18-8.06-18-18z" fill="#FADFD9"/><path d="M136.143 18c0-7.81-6.332-14.143-14.143-14.143-7.81 0-14.143 6.332-14.143 14.143 0 7.81 6.332 14.143 14.143 14.143 7.81 0 14.143-6.332 14.143-14.143z" fill="#FFF"/><path d="M119.43 8.994c0-.707.57-1.28 1.283-1.28h2.574c.71 0 1.284.57 1.284 1.28v10.298c0 .706-.57 1.28-1.283 1.28h-2.574c-.71 0-1.284-.57-1.284-1.28V8.994zm0 15.433c0-.71.57-1.284 1.283-1.284h2.574c.71 0 1.284.57 1.284 1.284V27c0 .71-.57 1.286-1.283 1.286h-2.574c-.71 0-1.284-.57-1.284-1.285v-2.573z" fill="#E75E40"/></g><g><path d="M213 89c0-9.94 8.06-18 18-18s18 8.06 18 18-8.06 18-18 18-18-8.06-18-18z" fill="#F6D4DC"/><path d="M245.143 89c0-7.81-6.332-14.143-14.143-14.143-7.81 0-14.143 6.332-14.143 14.143 0 7.81 6.332 14.143 14.143 14.143 7.81 0 14.143-6.332 14.143-14.143z" fill="#FFF"/><path d="M231 86.348l-3.603-3.602c-.288-.29-.766-.286-1.063.01l-1.578 1.578c-.3.302-.3.773-.01 1.063L228.348 89l-3.602 3.603c-.29.288-.286.766.01 1.063l1.578 1.578c.302.3.773.3 1.063.01L231 91.652l3.603 3.602c.288.29.766.286 1.063-.01l1.578-1.578c.3-.302.3-.773.01-1.063L233.652 89l3.602-3.603c.29-.288.286-.766-.01-1.063l-1.578-1.578c-.302-.3-.773-.3-1.063-.01L231 86.348z" fill="#D22852"/></g></g></svg> diff --git a/app/views/shared/icons/_icon_mr_issue.svg b/app/views/shared/icons/_icon_mr_issue.svg index ae219a3ded2..a56af9c556c 100644 --- a/app/views/shared/icons/_icon_mr_issue.svg +++ b/app/views/shared/icons/_icon_mr_issue.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g fill-rule="evenodd"><path d="m8.411 1.012c-.136-.008-.273-.012-.411-.012-3.866 0-7 3.134-7 7 0 3.866 3.134 7 7 7 3.866 0 7-3.134 7-7 0-.138-.004-.275-.012-.411-.464.201-.964.334-1.488.386 0 .008 0 .016 0 .025 0 3.038-2.462 5.5-5.5 5.5-3.038 0-5.5-2.462-5.5-5.5 0-3.038 2.462-5.5 5.5-5.5.008 0 .016 0 .025 0 .052-.524.185-1.024.386-1.488"/><path d="m12 2h-1.01c-.54 0-.991.448-.991 1 0 .556.444 1 .991 1h1.01v1.01c0 .54.448.991 1 .991.556 0 1-.444 1-.991v-1.01h1.01c.54 0 .991-.448.991-1 0-.556-.444-1-.991-1h-1.01v-1.01c0-.54-.448-.991-1-.991-.556 0-1 .444-1 .991v1.01m-5 4.01c0-.557.444-1.01 1-1.01.552 0 1 .443 1 1.01v1.981c0 .557-.444 1.01-1 1.01-.552 0-1-.443-1-1.01v-1.981m1 5.991c.552 0 1-.448 1-1 0-.552-.448-1-1-1-.552 0-1 .448-1 1 0 .552.448 1 1 1"/></g></svg>
\ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g fill-rule="evenodd"><path d="m8.411 1.012c-.136-.008-.273-.012-.411-.012-3.866 0-7 3.134-7 7 0 3.866 3.134 7 7 7 3.866 0 7-3.134 7-7 0-.138-.004-.275-.012-.411-.464.201-.964.334-1.488.386 0 .008 0 .016 0 .025 0 3.038-2.462 5.5-5.5 5.5-3.038 0-5.5-2.462-5.5-5.5 0-3.038 2.462-5.5 5.5-5.5.008 0 .016 0 .025 0 .052-.524.185-1.024.386-1.488"/><path d="m12 2h-1.01c-.54 0-.991.448-.991 1 0 .556.444 1 .991 1h1.01v1.01c0 .54.448.991 1 .991.556 0 1-.444 1-.991v-1.01h1.01c.54 0 .991-.448.991-1 0-.556-.444-1-.991-1h-1.01v-1.01c0-.54-.448-.991-1-.991-.556 0-1 .444-1 .991v1.01m-5 4.01c0-.557.444-1.01 1-1.01.552 0 1 .443 1 1.01v1.981c0 .557-.444 1.01-1 1.01-.552 0-1-.443-1-1.01v-1.981m1 5.991c.552 0 1-.448 1-1 0-.552-.448-1-1-1-.552 0-1 .448-1 1 0 .552.448 1 1 1"/></g></svg> diff --git a/app/views/shared/icons/_icon_play.svg b/app/views/shared/icons/_icon_play.svg index e965afa9a56..4c69fc99a9e 100644 --- a/app/views/shared/icons/_icon_play.svg +++ b/app/views/shared/icons/_icon_play.svg @@ -1,3 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 11" class="icon-play"> - <path fill-rule="evenodd" d="m9.283 6.47l-7.564 4.254c-.949.534-1.719.266-1.719-.576v-9.292c0-.852.756-1.117 1.719-.576l7.564 4.254c.949.534.963 1.392 0 1.934"/> - </svg>
\ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 11" class="icon-play"><path fill-rule="evenodd" d="m9.283 6.47l-7.564 4.254c-.949.534-1.719.266-1.719-.576v-9.292c0-.852.756-1.117 1.719-.576l7.564 4.254c.949.534.963 1.392 0 1.934"/></svg> diff --git a/app/views/shared/icons/_icon_stopwatch.svg b/app/views/shared/icons/_icon_stopwatch.svg index f20de04538e..6c2a8b2773f 100644 --- a/app/views/shared/icons/_icon_stopwatch.svg +++ b/app/views/shared/icons/_icon_stopwatch.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 14" enable-background="new 0 0 12 14"><path d="m11.5 2.4l-1.3-1.1-1 1.1 1.4 1.1.9-1.1"/><path d="m6.8 2v-.5h.5v-1.5h-2.6v1.5h.5v.5c-2.9.4-5.2 2.9-5.2 6 0 3.3 2.7 6 6 6s6-2.7 6-6c0-3-2.3-5.6-5.2-6m-.8 10.5c-2.5 0-4.5-2-4.5-4.5s2-4.5 4.5-4.5 4.5 2 4.5 4.5-2 4.5-4.5 4.5"/><path d="m6.2 8.9h-.5c-.1 0-.2-.1-.2-.2v-3.5c0-.1.1-.2.2-.2h.5c.1 0 .2.1.2.2v3.5c0 .1-.1.2-.2.2"/></svg>
\ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 14" enable-background="new 0 0 12 14"><path d="m11.5 2.4l-1.3-1.1-1 1.1 1.4 1.1.9-1.1"/><path d="m6.8 2v-.5h.5v-1.5h-2.6v1.5h.5v.5c-2.9.4-5.2 2.9-5.2 6 0 3.3 2.7 6 6 6s6-2.7 6-6c0-3-2.3-5.6-5.2-6m-.8 10.5c-2.5 0-4.5-2-4.5-4.5s2-4.5 4.5-4.5 4.5 2 4.5 4.5-2 4.5-4.5 4.5"/><path d="m6.2 8.9h-.5c-.1 0-.2-.1-.2-.2v-3.5c0-.1.1-.2.2-.2h.5c.1 0 .2.1.2.2v3.5c0 .1-.1.2-.2.2"/></svg> diff --git a/app/views/shared/icons/_icon_timer.svg b/app/views/shared/icons/_icon_timer.svg index 0b1e5804427..572a31ebcca 100644 --- a/app/views/shared/icons/_icon_timer.svg +++ b/app/views/shared/icons/_icon_timer.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><g fill="#8F8F8F" fill-rule="evenodd"><path d="M29.513 10.134A15.922 15.922 0 0 0 23 7.28V6h2.993C26.55 6 27 5.552 27 5V2a1 1 0 0 0-1.007-1H14.007C13.45 1 13 1.448 13 2v3a1 1 0 0 0 1.007 1H17v1.28C9.597 8.686 4 15.19 4 23c0 8.837 7.163 16 16 16s16-7.163 16-16c0-3.461-1.099-6.665-2.967-9.283l1.327-1.58a2.498 2.498 0 0 0-.303-3.53 2.499 2.499 0 0 0-3.528.315l-1.016 1.212zM20 34c6.075 0 11-4.925 11-11s-4.925-11-11-11S9 16.925 9 23s4.925 11 11 11z"/><path d="M19 21h-4.002c-.552 0-.998.452-.998 1.01v1.98c0 .567.447 1.01.998 1.01h7.004c.274 0 .521-.111.701-.291a.979.979 0 0 0 .297-.704v-8.01c0-.54-.452-.995-1.01-.995h-1.98a.997.997 0 0 0-1.01.995V21z"/></g></svg>
\ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><g fill="#8F8F8F" fill-rule="evenodd"><path d="M29.513 10.134A15.922 15.922 0 0 0 23 7.28V6h2.993C26.55 6 27 5.552 27 5V2a1 1 0 0 0-1.007-1H14.007C13.45 1 13 1.448 13 2v3a1 1 0 0 0 1.007 1H17v1.28C9.597 8.686 4 15.19 4 23c0 8.837 7.163 16 16 16s16-7.163 16-16c0-3.461-1.099-6.665-2.967-9.283l1.327-1.58a2.498 2.498 0 0 0-.303-3.53 2.499 2.499 0 0 0-3.528.315l-1.016 1.212zM20 34c6.075 0 11-4.925 11-11s-4.925-11-11-11S9 16.925 9 23s4.925 11 11 11z"/><path d="M19 21h-4.002c-.552 0-.998.452-.998 1.01v1.98c0 .567.447 1.01.998 1.01h7.004c.274 0 .521-.111.701-.291a.979.979 0 0 0 .297-.704v-8.01c0-.54-.452-.995-1.01-.995h-1.98a.997.997 0 0 0-1.01.995V21z"/></g></svg> diff --git a/app/views/shared/icons/_illustration_no_commits.svg b/app/views/shared/icons/_illustration_no_commits.svg index 4f9d9add60d..34f177d7efa 100644 --- a/app/views/shared/icons/_illustration_no_commits.svg +++ b/app/views/shared/icons/_illustration_no_commits.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 168 107" xmlns:xlink="http://www.w3.org/1999/xlink"><g fill="#eee" fill-rule="evenodd"><path d="m4.01 2h1.102c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-1.102c-2.218 0-4.01 1.788-4.01 4 0 .552.448 1 1 1 .552 0 1-.448 1-1 0-1.108.892-2 2.01-2m12.702 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m8.088 0c.822 0 1.554.503 1.86 1.254.208.512.791.758 1.303.55.512-.208.758-.791.55-1.303-.609-1.497-2.069-2.5-3.712-2.5h-2.188c-.552 0-1 .448-1 1 0 .552.448 1 1 1h2.188m2.01 12.518c0 .552.448 1 1 1 .552 0 1-.448 1-1v-5.7c0-.552-.448-1-1-1-.552 0-1 .448-1 1v5.7m0 11.6c0 .552.448 1 1 1 .552 0 1-.448 1-1v-5.7c0-.552-.448-1-1-1-.552 0-1 .448-1 1v5.7m0 11.6c0 .552.448 1 1 1 .552 0 1-.448 1-1v-5.7c0-.552-.448-1-1-1-.552 0-1 .448-1 1v5.7m0 6.282c0 1.108-.892 2-2.01 2h-.72c-.552 0-1 .448-1 1 0 .552.448 1 1 1h.72c2.218 0 4.01-1.788 4.01-4v-.382c0-.552-.448-1-1-1-.552 0-1 .448-1 1v.382m-14.325 2c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-8.47 0c-.755 0-1.438-.424-1.782-1.085-.255-.49-.859-.681-1.349-.426-.49.255-.681.859-.426 1.349.684 1.316 2.046 2.162 3.556 2.162h2.57c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-2.57m-2.01-12.136c0-.552-.448-1-1-1-.552 0-1 .448-1 1v5.7c0 .552.448 1 1 1 .552 0 1-.448 1-1v-5.7m0-11.6c0-.552-.448-1-1-1-.552 0-1 .448-1 1v5.7c0 .552.448 1 1 1 .552 0 1-.448 1-1v-5.7m0-11.6c0-.552-.448-1-1-1-.552 0-1 .448-1 1v5.7c0 .552.448 1 1 1 .552 0 1-.448 1-1v-5.7m0-6.664c0-.552-.448-1-1-1-.552 0-1 .448-1 1v.764c0 .552.448 1 1 1 .552 0 1-.448 1-1v-.764" id="0"/><circle cx="21" cy="24" r="10"/><rect width="33" height="3" x="37" y="18" rx="1.5" id="1"/><rect width="53" height="3" x="37" y="27" rx="1.5" id="2"/><path d="m131 29c0 .552.447.999.996.999h22.01c.545 0 .996-.451.996-.999v-9c0-.552-.447-.999-.996-.999h-22.01c-.545 0-.996.451-.996.999v9m.996-12h22.01c1.655 0 2.996 1.344 2.996 2.999v9c0 1.657-1.35 2.999-2.996 2.999h-22.01c-1.655 0-2.996-1.344-2.996-2.999v-9c0-1.657 1.35-2.999 2.996-2.999" id="3"/><g transform="translate(0 59)"><use xlink:href="#0"/><circle cx="21" cy="24" r="10"/><use xlink:href="#1"/><use xlink:href="#2"/><use xlink:href="#3"/></g></g></svg>
\ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 168 107" xmlns:xlink="http://www.w3.org/1999/xlink"><g fill="#eee" fill-rule="evenodd"><path d="m4.01 2h1.102c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-1.102c-2.218 0-4.01 1.788-4.01 4 0 .552.448 1 1 1 .552 0 1-.448 1-1 0-1.108.892-2 2.01-2m12.702 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m8.088 0c.822 0 1.554.503 1.86 1.254.208.512.791.758 1.303.55.512-.208.758-.791.55-1.303-.609-1.497-2.069-2.5-3.712-2.5h-2.188c-.552 0-1 .448-1 1 0 .552.448 1 1 1h2.188m2.01 12.518c0 .552.448 1 1 1 .552 0 1-.448 1-1v-5.7c0-.552-.448-1-1-1-.552 0-1 .448-1 1v5.7m0 11.6c0 .552.448 1 1 1 .552 0 1-.448 1-1v-5.7c0-.552-.448-1-1-1-.552 0-1 .448-1 1v5.7m0 11.6c0 .552.448 1 1 1 .552 0 1-.448 1-1v-5.7c0-.552-.448-1-1-1-.552 0-1 .448-1 1v5.7m0 6.282c0 1.108-.892 2-2.01 2h-.72c-.552 0-1 .448-1 1 0 .552.448 1 1 1h.72c2.218 0 4.01-1.788 4.01-4v-.382c0-.552-.448-1-1-1-.552 0-1 .448-1 1v.382m-14.325 2c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-8.47 0c-.755 0-1.438-.424-1.782-1.085-.255-.49-.859-.681-1.349-.426-.49.255-.681.859-.426 1.349.684 1.316 2.046 2.162 3.556 2.162h2.57c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-2.57m-2.01-12.136c0-.552-.448-1-1-1-.552 0-1 .448-1 1v5.7c0 .552.448 1 1 1 .552 0 1-.448 1-1v-5.7m0-11.6c0-.552-.448-1-1-1-.552 0-1 .448-1 1v5.7c0 .552.448 1 1 1 .552 0 1-.448 1-1v-5.7m0-11.6c0-.552-.448-1-1-1-.552 0-1 .448-1 1v5.7c0 .552.448 1 1 1 .552 0 1-.448 1-1v-5.7m0-6.664c0-.552-.448-1-1-1-.552 0-1 .448-1 1v.764c0 .552.448 1 1 1 .552 0 1-.448 1-1v-.764" id="0"/><circle cx="21" cy="24" r="10"/><rect width="33" height="3" x="37" y="18" rx="1.5" id="1"/><rect width="53" height="3" x="37" y="27" rx="1.5" id="2"/><path d="m131 29c0 .552.447.999.996.999h22.01c.545 0 .996-.451.996-.999v-9c0-.552-.447-.999-.996-.999h-22.01c-.545 0-.996.451-.996.999v9m.996-12h22.01c1.655 0 2.996 1.344 2.996 2.999v9c0 1.657-1.35 2.999-2.996 2.999h-22.01c-1.655 0-2.996-1.344-2.996-2.999v-9c0-1.657 1.35-2.999 2.996-2.999" id="3"/><g transform="translate(0 59)"><use xlink:href="#0"/><circle cx="21" cy="24" r="10"/><use xlink:href="#1"/><use xlink:href="#2"/><use xlink:href="#3"/></g></g></svg> diff --git a/app/views/shared/icons/_members.svg b/app/views/shared/icons/_members.svg deleted file mode 100644 index f8043b31fe8..00000000000 --- a/app/views/shared/icons/_members.svg +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg width="22px" height="16px" viewBox="0 0 22 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> - <!-- Generator: Sketch 3.7.2 (28276) - http://www.bohemiancoding.com/sketch --> - <title>Group</title> - <desc>Created with Sketch.</desc> - <defs></defs> - <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> - <g id="Group" fill="#7E7C7C"> - <path d="M6.4357,11.8588 C7.1487,11.2798 7.8797,10.7808 8.5357,10.3708 C8.5837,10.3008 8.6187,10.2338 8.6187,10.1768 L8.6187,8.8088 C8.9197,8.5218 9.0927,8.1248 9.0927,7.7028 L9.0927,5.3748 C9.0927,3.9478 7.9187,2.7858 6.4757,2.7858 L5.9687,2.7858 C4.5247,2.7858 3.3507,3.9478 3.3507,5.3748 L3.3507,7.7028 C3.3507,8.1248 3.5247,8.5218 3.8247,8.8088 L3.8247,10.5838 C3.2537,10.8738 1.8797,11.6198 0.5967,12.6618 C0.2177,12.9698 -0.0003,13.4258 -0.0003,13.9138 L-0.0003,15.5088 C-0.0003,15.5438 0.0857,15.7668 0.3467,15.7778 C1.3257,15.8198 3.8417,15.8328 5.9617,15.9038 C5.8337,15.8148 5.7447,15.6748 5.7447,15.5088 L5.7447,13.5498 C5.7447,12.9848 5.9967,12.2158 6.4357,11.8588" id="Fill-1"></path> - <path d="M21.3092,12.1 C19.6932,10.787 17.9592,9.86 17.3042,9.53 L17.3042,7.235 C17.6722,6.9 17.8862,6.428 17.8862,5.925 L17.8862,3.066 C17.8862,1.376 16.4952,0 14.7852,0 L14.1632,0 C12.4532,0 11.0622,1.376 11.0622,3.066 L11.0622,5.925 C11.0622,6.428 11.2752,6.9 11.6442,7.235 L11.6442,9.53 C10.9892,9.86 9.2542,10.787 7.6392,12.1 C7.2002,12.457 6.9482,12.985 6.9482,13.55 L6.9482,15.509 C6.9482,15.78 7.1702,16 7.4442,16 L14.1172,16 L14.1172,11.704 C12.6812,11.595 11.5652,10.853 11.5652,9.945 C11.5652,9.804 11.5982,9.669 11.6482,9.538 C11.9502,10.326 13.0982,10.913 14.4762,10.913 C15.8532,10.913 17.0012,10.326 17.3032,9.538 C17.3532,9.669 17.3862,9.804 17.3862,9.945 C17.3862,10.793 16.4152,11.5 15.1172,11.679 L15.1172,16 L21.5032,16 C21.7772,16 22.0002,15.78 22.0002,15.509 L22.0002,13.55 C22.0002,12.985 21.7482,12.457 21.3092,12.1" id="Fill-4"></path> - </g> - </g> -</svg>
\ No newline at end of file diff --git a/app/views/shared/icons/_milestones.svg b/app/views/shared/icons/_milestones.svg deleted file mode 100644 index 3d62ecc0631..00000000000 --- a/app/views/shared/icons/_milestones.svg +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg width="16px" height="17px" viewBox="0 0 16 17" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> - <!-- Generator: Sketch 3.7.2 (28276) - http://www.bohemiancoding.com/sketch --> - <title>Group</title> - <desc>Created with Sketch.</desc> - <defs></defs> - <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> - <g id="Group" fill="#7E7C7C"> - <path d="M15.1111,1 L0.8891,1 C0.3981,1 0.0001,1.446 0.0001,1.996 L0.0001,15.945 C0.0001,16.495 0.3981,16.941 0.8891,16.941 L15.1111,16.941 C15.6021,16.941 16.0001,16.495 16.0001,15.945 L16.0001,1.996 C16.0001,1.446 15.6021,1 15.1111,1 L15.1111,1 L15.1111,1 Z M14.0001,6.0002 L14.0001,14.949 L2.0001,14.949 L2.0001,6.0002 L14.0001,6.0002 Z M14.0001,4.0002 L14.0001,2.993 L2.0001,2.993 L2.0001,4.0002 L14.0001,4.0002 Z" id="Combined-Shape"></path> - <polygon id="Fill-11" points="3 2.0002 5 2.0002 5 0.0002 3 0.0002"></polygon> - <polygon id="Fill-16" points="11 2.0002 13 2.0002 13 0.0002 11 0.0002"></polygon> - <path d="M5.37709616,11.5511984 L6.92309616,12.7821984 C7.35112915,13.123019 7.97359761,13.0565604 8.32002627,12.6330535 L10.7740263,9.63305349 C11.1237073,9.20557058 11.0606364,8.57555475 10.6331535,8.22587373 C10.2056706,7.87619272 9.57565475,7.93926361 9.22597373,8.36674651 L6.77197373,11.3667465 L8.16890384,11.2176016 L6.62290384,9.98660159 C6.19085236,9.6425813 5.56172188,9.71394467 5.21770159,10.1459962 C4.8736813,10.5780476 4.94504467,11.2071781 5.37709616,11.5511984 L5.37709616,11.5511984 Z" id="Stroke-21"></path> - </g> - </g> -</svg>
\ No newline at end of file diff --git a/app/views/shared/icons/_mr.svg b/app/views/shared/icons/_mr.svg deleted file mode 100644 index dd3dbcc4473..00000000000 --- a/app/views/shared/icons/_mr.svg +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> - <!-- Generator: Sketch 3.7.2 (28276) - http://www.bohemiancoding.com/sketch --> - <title>Group</title> - <desc>Created with Sketch.</desc> - <defs></defs> - <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> - <g id="Group" fill="#7E7C7C"> - <path d="M15.1111,0 L0.8891,0 C0.3981,0 0.0001,0.446 0.0001,0.996 L0.0001,14.945 C0.0001,15.495 0.3981,15.941 0.8891,15.941 L15.1111,15.941 C15.6021,15.941 16.0001,15.495 16.0001,14.945 L16.0001,0.996 C16.0001,0.446 15.6021,0 15.1111,0 L15.1111,0 L15.1111,0 Z M2.0001,13.949 L14.0001,13.949 L14.0001,1.993 L2.0001,1.993 L2.0001,13.949 Z M2,5.0002 L14,5.0002 L14,3.0002 L2,3.0002 L2,5.0002 Z" id="Combined-Shape"></path> - <path d="M8.547,12.0002 L12,12.0002 L12,10.0002 L8.547,10.0002 L8.547,12.0002 Z M5.2029,12 L3.9999,10.867 L5.2029,9.501 L3.9999,8.181 L5.2029,7 L7.4529,9.499 L5.2029,12 Z" id="Combined-Shape"></path> - </g> - </g> -</svg>
\ No newline at end of file diff --git a/app/views/shared/icons/_pipelines.svg b/app/views/shared/icons/_pipelines.svg deleted file mode 100644 index 794e8a27025..00000000000 --- a/app/views/shared/icons/_pipelines.svg +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> - <!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch --> - <title>Pasted Image 246</title> - <desc>Created with Sketch.</desc> - <defs></defs> - <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> - <path d="M12.5,14 C11.672,14 11,13.328 11,12.5 C11,11.672 11.672,11 12.5,11 C13.328,11 14,11.672 14,12.5 C14,13.328 13.328,14 12.5,14 M12.5,9 L3.5,9 C1.567,9 0,10.567 0,12.5 C0,14.433 1.567,16 3.5,16 L12.5,16 C14.433,16 16,14.433 16,12.5 C16,10.567 14.433,9 12.5,9 M3.5,2 C4.328,2 5,2.672 5,3.5 C5,4.328 4.328,5 3.5,5 C2.672,5 2,4.328 2,3.5 C2,2.672 2.672,2 3.5,2 M3.5,7 L12.5,7 C14.433,7 16,5.433 16,3.5 C16,1.567 14.433,0 12.5,0 L3.5,0 C1.567,0 0,1.567 0,3.5 C0,5.433 1.567,7 3.5,7" id="Pasted-Image-246" fill="#303030"></path> - </g> -</svg>
\ No newline at end of file diff --git a/app/views/shared/icons/_wiki.svg b/app/views/shared/icons/_wiki.svg deleted file mode 100644 index 182d91e23aa..00000000000 --- a/app/views/shared/icons/_wiki.svg +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> - <!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch --> - <title>Pasted Image 241</title> - <desc>Created with Sketch.</desc> - <defs></defs> - <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> - <path d="M2.004,12.9999459 L3.939,12.9999459 L3.939,4.99994585 L2.004,4.99994585 L2.004,12.9999459 Z M7.017,9.99994585 L13.018,9.99994585 L13.018,8.99994585 L7.017,8.99994585 L7.017,9.99994585 Z M7.017,7.99994585 L13.018,7.99994585 L13.018,6.99994585 L7.017,6.99994585 L7.017,7.99994585 Z M7.017,5.99994585 L13.018,5.99994585 L13.018,4.99994585 L7.017,4.99994585 L7.017,5.99994585 Z M14.754,-5.41499267e-05 L4.938,-5.41499267e-05 C4.386,-5.41499267e-05 3.938,0.44794585 3.938,0.99994585 L3.938,2.99994585 L1,2.99994585 C0.448,2.99994585 0,3.44794585 0,3.99994585 L0,12.9999459 C0.037,13.4999459 -0.25,16.0509459 3.938,15.9999459 L12.408,15.9999459 C12.408,15.9999459 15.754,15.9169459 15.754,13.9999459 L15.754,0.99994585 C15.754,0.44794585 15.306,-5.41499267e-05 14.754,-5.41499267e-05 L14.754,-5.41499267e-05 Z" id="Pasted-Image-241" fill="#7E7D7D"></path> - </g> -</svg>
\ No newline at end of file diff --git a/app/workers/process_commit_worker.rb b/app/workers/process_commit_worker.rb index e9a5bd7f24e..2f7967cf531 100644 --- a/app/workers/process_commit_worker.rb +++ b/app/workers/process_commit_worker.rb @@ -53,6 +53,8 @@ class ProcessCommitWorker def update_issue_metrics(commit, author) mentioned_issues = commit.all_references(author).issues + return if mentioned_issues.empty? + Issue::Metrics.where(issue_id: mentioned_issues.map(&:id), first_mentioned_in_commit_at: nil). update_all(first_mentioned_in_commit_at: commit.committed_date) end diff --git a/changelogs/unreleased/20841-getting-started-better-empty-state-for-merge-requests-view.yml b/changelogs/unreleased/20841-getting-started-better-empty-state-for-merge-requests-view.yml new file mode 100644 index 00000000000..34909c06df3 --- /dev/null +++ b/changelogs/unreleased/20841-getting-started-better-empty-state-for-merge-requests-view.yml @@ -0,0 +1,4 @@ +--- +title: Added merge requests empty state +merge_request: 7342 +author: diff --git a/changelogs/unreleased/24187-set-git-terminal-prompt-env-var-in-initializer.yml b/changelogs/unreleased/24187-set-git-terminal-prompt-env-var-in-initializer.yml new file mode 100644 index 00000000000..7fe5c8a84af --- /dev/null +++ b/changelogs/unreleased/24187-set-git-terminal-prompt-env-var-in-initializer.yml @@ -0,0 +1,4 @@ +--- +title: Set GIT_TERMINAL_PROMPT env variable in initializer +merge_request: 10372 +author: diff --git a/changelogs/unreleased/26595-fix-issue-preselected-template.yml b/changelogs/unreleased/26595-fix-issue-preselected-template.yml deleted file mode 100644 index a94765f8f2a..00000000000 --- a/changelogs/unreleased/26595-fix-issue-preselected-template.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix linking to new issue with selected template via url parameter -merge_request: -author: diff --git a/changelogs/unreleased/28732-expandable-folders.yml b/changelogs/unreleased/28732-expandable-folders.yml new file mode 100644 index 00000000000..9ae30ba6253 --- /dev/null +++ b/changelogs/unreleased/28732-expandable-folders.yml @@ -0,0 +1,4 @@ +--- +title: Add back expandable folder behavior +merge_request: +author: diff --git a/changelogs/unreleased/29034-fix-github-importer.yml b/changelogs/unreleased/29034-fix-github-importer.yml deleted file mode 100644 index 6d08db3d55d..00000000000 --- a/changelogs/unreleased/29034-fix-github-importer.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix name colision when importing GitHub pull requests from forked repositories -merge_request: 9719 -author: diff --git a/changelogs/unreleased/29432-prevent-click-disabled-btn.yml b/changelogs/unreleased/29432-prevent-click-disabled-btn.yml new file mode 100644 index 00000000000..f30570cf68b --- /dev/null +++ b/changelogs/unreleased/29432-prevent-click-disabled-btn.yml @@ -0,0 +1,4 @@ +--- +title: Fix project title validation, prevent clicking on disabled button +merge_request: 9931 +author: diff --git a/changelogs/unreleased/29492-useless-queries.yml b/changelogs/unreleased/29492-useless-queries.yml new file mode 100644 index 00000000000..266a04be352 --- /dev/null +++ b/changelogs/unreleased/29492-useless-queries.yml @@ -0,0 +1,4 @@ +--- +title: Remove useless queries with false conditions (e.g 1=0) +merge_request: 10141 +author: mhasbini diff --git a/changelogs/unreleased/29541-fix-github-importer-deleted-fork.yml b/changelogs/unreleased/29541-fix-github-importer-deleted-fork.yml deleted file mode 100644 index fcb5798a619..00000000000 --- a/changelogs/unreleased/29541-fix-github-importer-deleted-fork.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix GitHub Importer for PRs of deleted forked repositories -merge_request: 9992 -author: diff --git a/changelogs/unreleased/29670-jira-integration-documentation-improvment.yml b/changelogs/unreleased/29670-jira-integration-documentation-improvment.yml new file mode 100644 index 00000000000..8975f0b6ef3 --- /dev/null +++ b/changelogs/unreleased/29670-jira-integration-documentation-improvment.yml @@ -0,0 +1,4 @@ +--- +title: Added clarification to the Jira integration documentation. +merge_request: 10066 +author: Matthew Bender diff --git a/changelogs/unreleased/29871-api-remove-merge-requests-comments-endpoints.yml b/changelogs/unreleased/29871-api-remove-merge-requests-comments-endpoints.yml deleted file mode 100644 index e3fb62d53b6..00000000000 --- a/changelogs/unreleased/29871-api-remove-merge-requests-comments-endpoints.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: 'API: Make the /notes endpoint work with noteable iid instead of id' -merge_request: -author: diff --git a/changelogs/unreleased/30098-banzai-filter-mergerequestreferencefilter-has-an-n-1-query-problem.yml b/changelogs/unreleased/30098-banzai-filter-mergerequestreferencefilter-has-an-n-1-query-problem.yml deleted file mode 100644 index f3f4e065aef..00000000000 --- a/changelogs/unreleased/30098-banzai-filter-mergerequestreferencefilter-has-an-n-1-query-problem.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Improve Markdown rendering when a lot of merge requests are referenced -merge_request: 10252 -author: diff --git a/changelogs/unreleased/30264-fix-vue-pagination.yml b/changelogs/unreleased/30264-fix-vue-pagination.yml deleted file mode 100644 index d5846e52bcf..00000000000 --- a/changelogs/unreleased/30264-fix-vue-pagination.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fixes method not replacing URL parameters correctly and breaking pipelines - pagination -merge_request: -author: diff --git a/changelogs/unreleased/30276-move-top-badges.yml b/changelogs/unreleased/30276-move-top-badges.yml deleted file mode 100644 index 68a8ab359fd..00000000000 --- a/changelogs/unreleased/30276-move-top-badges.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Move issue, mr, todos next to profile dropdown in top nav -merge_request: -author: diff --git a/changelogs/unreleased/30289-allow-users-to-import-github-projects-to-subgroups.yml b/changelogs/unreleased/30289-allow-users-to-import-github-projects-to-subgroups.yml deleted file mode 100644 index a33382a85e3..00000000000 --- a/changelogs/unreleased/30289-allow-users-to-import-github-projects-to-subgroups.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Allow users to import GitHub projects to subgroups -merge_request: -author: diff --git a/changelogs/unreleased/30400-fix-blob-highlighting-in-search.yml b/changelogs/unreleased/30400-fix-blob-highlighting-in-search.yml new file mode 100644 index 00000000000..942258450c0 --- /dev/null +++ b/changelogs/unreleased/30400-fix-blob-highlighting-in-search.yml @@ -0,0 +1,4 @@ +--- +title: Fix blob highlighting in search +merge_request: 10420 +author: diff --git a/changelogs/unreleased/add-mock-deployment-and-monitoring-service-for-development.yaml b/changelogs/unreleased/add-mock-deployment-and-monitoring-service-for-development.yaml new file mode 100644 index 00000000000..4c81d21a94b --- /dev/null +++ b/changelogs/unreleased/add-mock-deployment-and-monitoring-service-for-development.yaml @@ -0,0 +1,4 @@ +--- +title: Added mock deployment and monitoring service with environments fixtures +merge_request: +author: diff --git a/changelogs/unreleased/bug-api_milestone_merge_requests_scope.yml b/changelogs/unreleased/bug-api_milestone_merge_requests_scope.yml new file mode 100644 index 00000000000..a1e1c29165e --- /dev/null +++ b/changelogs/unreleased/bug-api_milestone_merge_requests_scope.yml @@ -0,0 +1,4 @@ +--- +title: Fixes milestone/merge_requests endpoint to actually scope the result +merge_request: +author: Joren De Groof diff --git a/changelogs/unreleased/environment-performance-improvements.yml b/changelogs/unreleased/environment-performance-improvements.yml new file mode 100644 index 00000000000..43e8f0afcee --- /dev/null +++ b/changelogs/unreleased/environment-performance-improvements.yml @@ -0,0 +1,4 @@ +--- +title: Improved UX for the environments metrics view +merge_request: 9946 +author: diff --git a/changelogs/unreleased/fix-gb-environments-folders-route.yml b/changelogs/unreleased/fix-gb-environments-folders-route.yml deleted file mode 100644 index fd9d9e6f168..00000000000 --- a/changelogs/unreleased/fix-gb-environments-folders-route.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix environment folder route when special chars present in environment name -merge_request: 10250 -author: diff --git a/changelogs/unreleased/fix-import-fork.yml b/changelogs/unreleased/fix-import-fork.yml new file mode 100644 index 00000000000..ff8dd131995 --- /dev/null +++ b/changelogs/unreleased/fix-import-fork.yml @@ -0,0 +1,4 @@ +--- +title: Fix Import/Export MR diffs not showing and missing forked MRs +merge_request: +author: diff --git a/changelogs/unreleased/fix-import-namespace.yml b/changelogs/unreleased/fix-import-namespace.yml new file mode 100644 index 00000000000..9a2fa5e425f --- /dev/null +++ b/changelogs/unreleased/fix-import-namespace.yml @@ -0,0 +1,4 @@ +--- +title: Create subgroups if they don't exist while importing projects +merge_request: +author: diff --git a/changelogs/unreleased/fix_wiki_commit_message.yml b/changelogs/unreleased/fix_wiki_commit_message.yml new file mode 100644 index 00000000000..e5cd398b4b5 --- /dev/null +++ b/changelogs/unreleased/fix_wiki_commit_message.yml @@ -0,0 +1,4 @@ +--- +title: Fix wiki commit message +merge_request: 10464 +author: blackst0ne diff --git a/changelogs/unreleased/forked-subquery-order.yml b/changelogs/unreleased/forked-subquery-order.yml deleted file mode 100644 index 06fb8236783..00000000000 --- a/changelogs/unreleased/forked-subquery-order.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Remove unnecessary ORDER BY clause from `forked_to_project_id` subquery -merge_request: -author: mhasbini diff --git a/changelogs/unreleased/issue_91_ee_backport.yml b/changelogs/unreleased/issue_91_ee_backport.yml new file mode 100644 index 00000000000..17bc0e435f3 --- /dev/null +++ b/changelogs/unreleased/issue_91_ee_backport.yml @@ -0,0 +1,4 @@ +--- +title: Do not set closed_at to nil when issue is reopened +merge_request: +author: diff --git a/changelogs/unreleased/make-ci-build-to-lock-on-status-change.yml b/changelogs/unreleased/make-ci-build-to-lock-on-status-change.yml deleted file mode 100644 index b0c5eb4ed17..00000000000 --- a/changelogs/unreleased/make-ci-build-to-lock-on-status-change.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Make CI build to use optimistic locking only on status change -merge_request: -author: diff --git a/changelogs/unreleased/remove_index_for_users-current_sign_in_at.yml b/changelogs/unreleased/remove_index_for_users-current_sign_in_at.yml new file mode 100644 index 00000000000..ec3a2c8e2bf --- /dev/null +++ b/changelogs/unreleased/remove_index_for_users-current_sign_in_at.yml @@ -0,0 +1,4 @@ +--- +title: Remove index for users.current sign in at +merge_request: 10401 +author: blackst0ne diff --git a/changelogs/unreleased/sh-fix-destroy-user-race.yml b/changelogs/unreleased/sh-fix-destroy-user-race.yml deleted file mode 100644 index 4d650b51ada..00000000000 --- a/changelogs/unreleased/sh-fix-destroy-user-race.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix race condition where a namespace would be deleted before a project was - deleted -merge_request: -author: diff --git a/changelogs/unreleased/sh-fix-ssh-keys-with-spaces.yml b/changelogs/unreleased/sh-fix-ssh-keys-with-spaces.yml new file mode 100644 index 00000000000..fe75d7e1156 --- /dev/null +++ b/changelogs/unreleased/sh-fix-ssh-keys-with-spaces.yml @@ -0,0 +1,4 @@ +--- +title: Handle SSH keys that have multiple spaces between each marker +merge_request: +author: diff --git a/changelogs/unreleased/todo-update-order.yml b/changelogs/unreleased/todo-update-order.yml deleted file mode 100644 index 2046b6df11e..00000000000 --- a/changelogs/unreleased/todo-update-order.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Remove unnecessary ORDER BY clause when updating todos -merge_request: -author: mhasbini diff --git a/changelogs/unreleased/zj-kube-service-auto-fill.yml b/changelogs/unreleased/zj-kube-service-auto-fill.yml new file mode 100644 index 00000000000..7a2c7a5085b --- /dev/null +++ b/changelogs/unreleased/zj-kube-service-auto-fill.yml @@ -0,0 +1,4 @@ +--- +title: Don't fill in the default kubernetes namespace +merge_request: +author: diff --git a/config/application.rb b/config/application.rb index f9f01b66473..f2ecc4ce77c 100644 --- a/config/application.rb +++ b/config/application.rb @@ -150,6 +150,7 @@ module Gitlab # This is needed for gitlab-shell ENV['GITLAB_PATH_OUTSIDE_HOOK'] = ENV['PATH'] + ENV['GIT_TERMINAL_PROMPT'] = '0' config.generators do |g| g.factory_girl false diff --git a/config/webpack.config.js b/config/webpack.config.js index 70d98b022c1..36c09c14d56 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -23,7 +23,6 @@ var config = { main: './main.js', blob: './blob_edit/blob_bundle.js', boards: './boards/boards_bundle.js', - simulate_drag: './test_utils/simulate_drag.js', cycle_analytics: './cycle_analytics/cycle_analytics_bundle.js', commit_pipelines: './commit/pipelines/pipelines_bundle.js', diff_notes: './diff_notes/diff_notes_bundle.js', @@ -53,7 +52,7 @@ var config = { filename: IS_PRODUCTION ? '[name].[chunkhash].bundle.js' : '[name].bundle.js' }, - devtool: 'inline-source-map', + devtool: 'cheap-module-source-map', module: { rules: [ @@ -164,6 +163,7 @@ if (IS_PRODUCTION) { } if (IS_DEV_SERVER) { + config.devtool = 'cheap-module-eval-source-map'; config.devServer = { port: DEV_SERVER_PORT, headers: { 'Access-Control-Allow-Origin': '*' }, diff --git a/db/fixtures/development/19_environments.rb b/db/fixtures/development/19_environments.rb new file mode 100644 index 00000000000..93214b9d3e7 --- /dev/null +++ b/db/fixtures/development/19_environments.rb @@ -0,0 +1,70 @@ +require './spec/support/sidekiq' + +class Gitlab::Seeder::Environments + def initialize(project) + @project = project + end + + def seed! + @project.create_mock_deployment_service!(active: true) + @project.create_mock_monitoring_service!(active: true) + + create_master_deployments!('production') + create_master_deployments!('staging') + create_merge_request_review_deployments! + end + + private + + def create_master_deployments!(name) + @project.repository.commits('master', limit: 4).map do |commit| + create_deployment!( + @project, + name, + 'master', + commit.id + ) + end + end + + def create_merge_request_review_deployments! + @project.merge_requests.sample(4).map do |merge_request| + next unless merge_request.diff_head_sha + + create_deployment!( + merge_request.source_project, + "review/#{merge_request.source_branch}", + merge_request.source_branch, + merge_request.diff_head_sha + ) + end + end + + def create_deployment!(project, name, ref, sha) + environment = find_or_create_environment!(project, name) + environment.deployments.create!( + project: project, + ref: ref, + sha: sha, + tag: false, + deployable: find_deployable(project, name) + ) + end + + def find_or_create_environment!(project, name) + project.environments.find_or_create_by!(name: name).tap do |environment| + environment.update(external_url: "https://google.com/#{name}") + end + end + + def find_deployable(project, environment) + project.builds.where(environment: environment).sample + end +end + +Gitlab::Seeder.quiet do + Project.all.sample(5).each do |project| + project_environments = Gitlab::Seeder::Environments.new(project) + project_environments.seed! + end +end diff --git a/db/fixtures/development/19_nested_groups.rb b/db/fixtures/development/20_nested_groups.rb index d8dddc3fee9..d8dddc3fee9 100644 --- a/db/fixtures/development/19_nested_groups.rb +++ b/db/fixtures/development/20_nested_groups.rb diff --git a/db/migrate/20170402231018_remove_index_for_users_current_sign_in_at.rb b/db/migrate/20170402231018_remove_index_for_users_current_sign_in_at.rb new file mode 100644 index 00000000000..8316ee9eb9f --- /dev/null +++ b/db/migrate/20170402231018_remove_index_for_users_current_sign_in_at.rb @@ -0,0 +1,25 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class RemoveIndexForUsersCurrentSignInAt < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + disable_ddl_transaction! + + def up + if index_exists? :users, :current_sign_in_at + if Gitlab::Database.postgresql? + execute 'DROP INDEX CONCURRENTLY index_users_on_current_sign_in_at;' + else + remove_index :users, :current_sign_in_at + end + end + end + + def down + add_concurrent_index :users, :current_sign_in_at + end +end diff --git a/db/schema.rb b/db/schema.rb index 19aca4b941e..ccf18d07179 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170329124448) do +ActiveRecord::Schema.define(version: 20170402231018) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -1252,7 +1252,6 @@ ActiveRecord::Schema.define(version: 20170329124448) do add_index "users", ["authentication_token"], name: "index_users_on_authentication_token", unique: true, using: :btree add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree add_index "users", ["created_at"], name: "index_users_on_created_at", using: :btree - add_index "users", ["current_sign_in_at"], name: "index_users_on_current_sign_in_at", using: :btree add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree add_index "users", ["email"], name: "index_users_on_email_trigram", using: :gin, opclasses: {"email"=>"gin_trgm_ops"} add_index "users", ["ghost"], name: "index_users_on_ghost", using: :btree diff --git a/doc/development/img/cache-hit.svg b/doc/development/img/cache-hit.svg new file mode 100644 index 00000000000..1c37693df2d --- /dev/null +++ b/doc/development/img/cache-hit.svg @@ -0,0 +1,21 @@ +<svg version="1.1" id="mscgen_js-svg-__svg" class="mscgen_js-svg-__svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="976" height="310" viewBox="0 0 976 310"><desc> + +# Generated by mscgen_js - https://sverweij.github.io/mscgen_js +msc { + # options + hscale="1.5"; + + # entities + c [label="Client", textbgcolor="lime"], + rails [label="Rails", textbgcolor="cyan"], + etag [label="EtagCaching", textbgcolor="orange"], + redis [label="Redis", textbgcolor="white"]; + + # arcs + c => rails [label="GET /projects/5/pipelines"]; + rails => etag [label="GET /projects/5/pipelines"]; + etag => redis [label="read(key = 'GET <Etag>')"]; + redis => etag [label="cache hit", linecolor="green", textcolor="green"]; + |||; + etag => c [label="304 Not Modified", linecolor="blue", textcolor="blue"]; +}</desc><defs><style type="text/css">svg.mscgen_js-svg-__svg{font-family:Helvetica,sans-serif;font-size:12px;font-weight:normal;font-style:normal;text-decoration:none;background-color:white;stroke:black;stroke-width:2;color:black}.mscgen_js-svg-__svg path, .mscgen_js-svg-__svg rect{fill:none;color:black;stroke:black}.mscgen_js-svg-__svg .label-text-background{fill:white;stroke:white;stroke-width:0}.mscgen_js-svg-__svg .bglayer{fill:white;stroke:white;stroke-width:0}.mscgen_js-svg-__svg line{stroke:black}.mscgen_js-svg-__svg .return, .mscgen_js-svg-__svg .comment{stroke-dasharray:5,3}.mscgen_js-svg-__svg .inline_expression_divider{stroke-dasharray:10,5}.mscgen_js-svg-__svg text{color:inherit;stroke:none;text-anchor:middle}.mscgen_js-svg-__svg text.entity-text{text-decoration:underline}.mscgen_js-svg-__svg text.anchor-start{text-anchor:start}.mscgen_js-svg-__svg .arrow-marker{overflow:visible}.mscgen_js-svg-__svg .arrow-style{stroke-width:1}.mscgen_js-svg-__svg .arcrow, .mscgen_js-svg-__svg .arcrowomit, .mscgen_js-svg-__svg .emphasised{stroke-linecap:butt}.mscgen_js-svg-__svg .arcrowomit{stroke-dasharray:2,2;}.mscgen_js-svg-__svg .box, .mscgen_js-svg-__svg .entity{fill:white;stroke-linejoin:round}.mscgen_js-svg-__svg .inherit{stroke:inherit;color:inherit}.mscgen_js-svg-__svg .inherit-fill{fill:inherit}.mscgen_js-svg-__svg .watermark{stroke:black;color:black;fill:black;font-size:48pt;font-weight:bold;opacity:0.14}</style><marker orient="auto" id="mscgen_js-svg-__svgmethod-black" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10"><polygon points="1,1 9,3 1,5" class="arrow-style" stroke="black" fill="black"></polygon></marker><marker orient="auto" id="mscgen_js-svg-__svgmethod-l-black" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10"><polygon points="17,1 9,3 17,5" class="arrow-style" stroke="black" fill="black"></polygon></marker><marker orient="auto" id="mscgen_js-svg-__svgmethod-blue" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10"><polygon points="1,1 9,3 1,5" class="arrow-style" stroke="blue" fill="blue"></polygon></marker><marker orient="auto" id="mscgen_js-svg-__svgmethod-l-blue" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10"><polygon points="17,1 9,3 17,5" class="arrow-style" stroke="blue" fill="blue"></polygon></marker><marker orient="auto" id="mscgen_js-svg-__svgmethod-green" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10"><polygon points="1,1 9,3 1,5" class="arrow-style" stroke="green" fill="green"></polygon></marker><marker orient="auto" id="mscgen_js-svg-__svgmethod-l-green" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10"><polygon points="17,1 9,3 17,5" class="arrow-style" stroke="green" fill="green"></polygon></marker></defs><g id="mscgen_js-svg-__svg__body" transform="translate(53,3) scale(1,1)"><g id="mscgen_js-svg-__svg__background"><rect width="976" height="310" x="-53" y="-3" class="bglayer"></rect></g><g id="mscgen_js-svg-__svg__arcspanlayer"></g><g id="mscgen_js-svg-__svg__lifelinelayer"><line x1="75" y1="38" x2="75" y2="76" class="arcrow"></line><line x1="315" y1="38" x2="315" y2="76" class="arcrow"></line><line x1="555" y1="38" x2="555" y2="76" class="arcrow"></line><line x1="795" y1="38" x2="795" y2="76" class="arcrow"></line><line x1="75" y1="76" x2="75" y2="114" class="arcrow"></line><line x1="315" y1="76" x2="315" y2="114" class="arcrow"></line><line x1="555" y1="76" x2="555" y2="114" class="arcrow"></line><line x1="795" y1="76" x2="795" y2="114" class="arcrow"></line><line x1="75" y1="114" x2="75" y2="152" class="arcrow"></line><line x1="315" y1="114" x2="315" y2="152" class="arcrow"></line><line x1="555" y1="114" x2="555" y2="152" class="arcrow"></line><line x1="795" y1="114" x2="795" y2="152" class="arcrow"></line><line x1="75" y1="152" x2="75" y2="190" class="arcrow"></line><line x1="315" y1="152" x2="315" y2="190" class="arcrow"></line><line x1="555" y1="152" x2="555" y2="190" class="arcrow"></line><line x1="795" y1="152" x2="795" y2="190" class="arcrow"></line><line x1="75" y1="190" x2="75" y2="228" class="arcrow"></line><line x1="315" y1="190" x2="315" y2="228" class="arcrow"></line><line x1="555" y1="190" x2="555" y2="228" class="arcrow"></line><line x1="795" y1="190" x2="795" y2="228" class="arcrow"></line><line x1="75" y1="228" x2="75" y2="266" class="arcrow"></line><line x1="315" y1="228" x2="315" y2="266" class="arcrow"></line><line x1="555" y1="228" x2="555" y2="266" class="arcrow"></line><line x1="795" y1="228" x2="795" y2="266" class="arcrow"></line><line x1="75" y1="266" x2="75" y2="304" class="arcrow"></line><line x1="315" y1="266" x2="315" y2="304" class="arcrow"></line><line x1="555" y1="266" x2="555" y2="304" class="arcrow"></line><line x1="795" y1="266" x2="795" y2="304" class="arcrow"></line></g><g id="mscgen_js-svg-__svg__sequencelayer"><g id="mscgen_js-svg-__svgentities"><g><rect width="150" height="38" class="entity" style="fill:lime;"></rect><g><text x="75" y="22.5" class="entity-text "><tspan>Client</tspan></text></g></g><g><rect width="150" height="38" x="240" class="entity" style="fill:cyan;"></rect><g><text x="315" y="22.5" class="entity-text "><tspan>Rails</tspan></text></g></g><g><rect width="150" height="38" x="480" class="entity" style="fill:orange;"></rect><g><text x="555" y="22.5" class="entity-text "><tspan>EtagCaching</tspan></text></g></g><g><rect width="150" height="38" x="720" class="entity" style="fill:white;"></rect><g><text x="795" y="22.5" class="entity-text "><tspan>Redis</tspan></text></g></g></g><g><line x1="75" y1="95" x2="315" y2="95" class="arc directional method" style="stroke:black" marker-end="url(#mscgen_js-svg-__svgmethod-black)"></line><g><rect width="134.06" height="14" x="127.97" y="79.5" class="label-text-background"></rect><text x="195" y="90.5" class="directional-text method-text "><tspan>GET /projects/5/pipelines</tspan></text></g></g><g><line x1="315" y1="133" x2="555" y2="133" class="arc directional method" style="stroke:black" marker-end="url(#mscgen_js-svg-__svgmethod-black)"></line><g><rect width="134.06" height="14" x="367.97" y="117.5" class="label-text-background"></rect><text x="435" y="128.5" class="directional-text method-text "><tspan>GET /projects/5/pipelines</tspan></text></g></g><g><line x1="555" y1="171" x2="795" y2="171" class="arc directional method" style="stroke:black" marker-end="url(#mscgen_js-svg-__svgmethod-black)"></line><g><rect width="135.64" height="14" x="607.17" y="155.5" class="label-text-background"></rect><text x="675" y="166.5" class="directional-text method-text "><tspan>read(key = 'GET <Etag>')</tspan></text></g></g><g><line x1="795" y1="209" x2="555" y2="209" class="arc directional method" style="stroke:green" marker-end="url(#mscgen_js-svg-__svgmethod-green)"></line><g><rect width="48.02" height="14" x="650.98" y="193.5" class="label-text-background"></rect><text x="675" y="204.5" class="directional-text method-text " style="fill:green;"><tspan>cache hit</tspan></text></g></g><g></g><g><line x1="555" y1="285" x2="75" y2="285" class="arc directional method" style="stroke:blue" marker-end="url(#mscgen_js-svg-__svgmethod-blue)"></line><g><rect width="90.72" height="14" x="269.63" y="269.5" class="label-text-background"></rect><text x="315" y="280.5" class="directional-text method-text " style="fill:blue;"><tspan>304 Not Modified</tspan></text></g></g></g><g id="mscgen_js-svg-__svg__notelayer"></g><g id="mscgen_js-svg-__svg__watermark"></g></g></svg>
\ No newline at end of file diff --git a/doc/development/img/cache-miss.svg b/doc/development/img/cache-miss.svg new file mode 100644 index 00000000000..8429e6a1918 --- /dev/null +++ b/doc/development/img/cache-miss.svg @@ -0,0 +1,24 @@ +<svg version="1.1" id="mscgen_js-svg-__svg" class="mscgen_js-svg-__svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="976" height="386" viewBox="0 0 976 386"><desc> + +# Generated by mscgen_js - https://sverweij.github.io/mscgen_js +msc { + # options + hscale="1.5"; + + # entities + c [label="Client", textbgcolor="lime"], + rails [label="Rails", textbgcolor="cyan"], + etag [label="EtagCaching", textbgcolor="orange"], + redis [label="Redis", textbgcolor="white"]; + + # arcs + c => rails [label="GET /projects/5/pipelines"]; + rails => etag [label="GET /projects/5/pipelines"]; + etag => redis [label="read(key = 'GET <Etag>')"]; + redis => etag [label="cache miss", linecolor="red", textcolor="red"]; + |||; + etag => redis [label="write('<New Etag>')"]; + etag => rails [label="GET /projects/5/pipelines"]; + rails => c [label="JSON response w/ ETag", linecolor="blue", textcolor="blue"]; +} +</desc><defs><style type="text/css">svg.mscgen_js-svg-__svg{font-family:Helvetica,sans-serif;font-size:12px;font-weight:normal;font-style:normal;text-decoration:none;background-color:white;stroke:black;stroke-width:2}.mscgen_js-svg-__svg path, .mscgen_js-svg-__svg rect{fill:none}.mscgen_js-svg-__svg .label-text-background{fill:white;stroke:white;stroke-width:0}.mscgen_js-svg-__svg .bglayer{fill:white;stroke:white;stroke-width:0}.mscgen_js-svg-__svg line{}.mscgen_js-svg-__svg .return, .mscgen_js-svg-__svg .comment{stroke-dasharray:5,3}.mscgen_js-svg-__svg .inline_expression_divider{stroke-dasharray:10,5}.mscgen_js-svg-__svg text{color:inherit;stroke:none;text-anchor:middle}.mscgen_js-svg-__svg text.entity-text{text-decoration:underline}.mscgen_js-svg-__svg text.anchor-start{text-anchor:start}.mscgen_js-svg-__svg .arrow-marker{overflow:visible}.mscgen_js-svg-__svg .arrow-style{stroke-width:1}.mscgen_js-svg-__svg .arcrow, .mscgen_js-svg-__svg .arcrowomit, .mscgen_js-svg-__svg .emphasised{stroke-linecap:butt}.mscgen_js-svg-__svg .arcrowomit{stroke-dasharray:2,2}.mscgen_js-svg-__svg .box, .mscgen_js-svg-__svg .entity{fill:white;stroke-linejoin:round}.mscgen_js-svg-__svg .inherit{stroke:inherit;color:inherit}.mscgen_js-svg-__svg .inherit-fill{fill:inherit}.mscgen_js-svg-__svg .watermark{font-size:48pt;font-weight:bold;opacity:0.14}</style><marker orient="auto" id="mscgen_js-svg-__svgmethod-black" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10"><polygon points="1,1 9,3 1,5" class="arrow-style" stroke="black" fill="black"></polygon></marker><marker orient="auto" id="mscgen_js-svg-__svgmethod-l-black" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10"><polygon points="17,1 9,3 17,5" class="arrow-style" stroke="black" fill="black"></polygon></marker><marker orient="auto" id="mscgen_js-svg-__svgmethod-blue" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10"><polygon points="1,1 9,3 1,5" class="arrow-style" stroke="blue" fill="blue"></polygon></marker><marker orient="auto" id="mscgen_js-svg-__svgmethod-l-blue" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10"><polygon points="17,1 9,3 17,5" class="arrow-style" stroke="blue" fill="blue"></polygon></marker><marker orient="auto" id="mscgen_js-svg-__svgmethod-red" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10"><polygon points="1,1 9,3 1,5" class="arrow-style" stroke="red" fill="red"></polygon></marker><marker orient="auto" id="mscgen_js-svg-__svgmethod-l-red" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10"><polygon points="17,1 9,3 17,5" class="arrow-style" stroke="red" fill="red"></polygon></marker></defs><g id="mscgen_js-svg-__svg__body" transform="translate(53,3) scale(1,1)"><g id="mscgen_js-svg-__svg__background"><rect width="976" height="386" x="-53" y="-3" class="bglayer"></rect></g><g id="mscgen_js-svg-__svg__arcspanlayer"></g><g id="mscgen_js-svg-__svg__lifelinelayer"><line x1="75" y1="38" x2="75" y2="76" class="arcrow"></line><line x1="315" y1="38" x2="315" y2="76" class="arcrow"></line><line x1="555" y1="38" x2="555" y2="76" class="arcrow"></line><line x1="795" y1="38" x2="795" y2="76" class="arcrow"></line><line x1="75" y1="76" x2="75" y2="114" class="arcrow"></line><line x1="315" y1="76" x2="315" y2="114" class="arcrow"></line><line x1="555" y1="76" x2="555" y2="114" class="arcrow"></line><line x1="795" y1="76" x2="795" y2="114" class="arcrow"></line><line x1="75" y1="114" x2="75" y2="152" class="arcrow"></line><line x1="315" y1="114" x2="315" y2="152" class="arcrow"></line><line x1="555" y1="114" x2="555" y2="152" class="arcrow"></line><line x1="795" y1="114" x2="795" y2="152" class="arcrow"></line><line x1="75" y1="152" x2="75" y2="190" class="arcrow"></line><line x1="315" y1="152" x2="315" y2="190" class="arcrow"></line><line x1="555" y1="152" x2="555" y2="190" class="arcrow"></line><line x1="795" y1="152" x2="795" y2="190" class="arcrow"></line><line x1="75" y1="190" x2="75" y2="228" class="arcrow"></line><line x1="315" y1="190" x2="315" y2="228" class="arcrow"></line><line x1="555" y1="190" x2="555" y2="228" class="arcrow"></line><line x1="795" y1="190" x2="795" y2="228" class="arcrow"></line><line x1="75" y1="228" x2="75" y2="266" class="arcrow"></line><line x1="315" y1="228" x2="315" y2="266" class="arcrow"></line><line x1="555" y1="228" x2="555" y2="266" class="arcrow"></line><line x1="795" y1="228" x2="795" y2="266" class="arcrow"></line><line x1="75" y1="266" x2="75" y2="304" class="arcrow"></line><line x1="315" y1="266" x2="315" y2="304" class="arcrow"></line><line x1="555" y1="266" x2="555" y2="304" class="arcrow"></line><line x1="795" y1="266" x2="795" y2="304" class="arcrow"></line><line x1="75" y1="304" x2="75" y2="342" class="arcrow"></line><line x1="315" y1="304" x2="315" y2="342" class="arcrow"></line><line x1="555" y1="304" x2="555" y2="342" class="arcrow"></line><line x1="795" y1="304" x2="795" y2="342" class="arcrow"></line><line x1="75" y1="342" x2="75" y2="380" class="arcrow"></line><line x1="315" y1="342" x2="315" y2="380" class="arcrow"></line><line x1="555" y1="342" x2="555" y2="380" class="arcrow"></line><line x1="795" y1="342" x2="795" y2="380" class="arcrow"></line></g><g id="mscgen_js-svg-__svg__sequencelayer"><g id="mscgen_js-svg-__svgentities"><g><rect width="150" height="38" class="entity" style="fill:lime;"></rect><g><text x="75" y="22.5" class="entity-text "><tspan>Client</tspan></text></g></g><g><rect width="150" height="38" x="240" class="entity" style="fill:cyan;"></rect><g><text x="315" y="22.5" class="entity-text "><tspan>Rails</tspan></text></g></g><g><rect width="150" height="38" x="480" class="entity" style="fill:orange;"></rect><g><text x="555" y="22.5" class="entity-text "><tspan>EtagCaching</tspan></text></g></g><g><rect width="150" height="38" x="720" class="entity" style="fill:white;"></rect><g><text x="795" y="22.5" class="entity-text "><tspan>Redis</tspan></text></g></g></g><g><line x1="75" y1="95" x2="315" y2="95" class="arc directional method" style="stroke:black" marker-end="url(#mscgen_js-svg-__svgmethod-black)"></line><g><rect width="134.06" height="14" x="127.97" y="79.5" class="label-text-background"></rect><text x="195" y="90.5" class="directional-text method-text "><tspan>GET /projects/5/pipelines</tspan></text></g></g><g><line x1="315" y1="133" x2="555" y2="133" class="arc directional method" style="stroke:black" marker-end="url(#mscgen_js-svg-__svgmethod-black)"></line><g><rect width="134.06" height="14" x="367.97" y="117.5" class="label-text-background"></rect><text x="435" y="128.5" class="directional-text method-text "><tspan>GET /projects/5/pipelines</tspan></text></g></g><g><line x1="555" y1="171" x2="795" y2="171" class="arc directional method" style="stroke:black" marker-end="url(#mscgen_js-svg-__svgmethod-black)"></line><g><rect width="135.64" height="14" x="607.17" y="155.5" class="label-text-background"></rect><text x="675" y="166.5" class="directional-text method-text "><tspan>read(key = 'GET <Etag>')</tspan></text></g></g><g><line x1="795" y1="209" x2="555" y2="209" class="arc directional method" style="stroke:red" marker-end="url(#mscgen_js-svg-__svgmethod-red)"></line><g><rect width="60.02" height="14" x="644.98" y="193.5" class="label-text-background"></rect><text x="675" y="204.5" class="directional-text method-text " style="fill:red;"><tspan>cache miss</tspan></text></g></g><g></g><g><line x1="555" y1="285" x2="795" y2="285" class="arc directional method" style="stroke:black" marker-end="url(#mscgen_js-svg-__svgmethod-black)"></line><g><rect width="103.94" height="14" x="623.02" y="269.5" class="label-text-background"></rect><text x="675" y="280.5" class="directional-text method-text "><tspan>write('<New Etag>')</tspan></text></g></g><g><line x1="555" y1="323" x2="315" y2="323" class="arc directional method" style="stroke:black" marker-end="url(#mscgen_js-svg-__svgmethod-black)"></line><g><rect width="134.06" height="14" x="367.97" y="307.5" class="label-text-background"></rect><text x="435" y="318.5" class="directional-text method-text "><tspan>GET /projects/5/pipelines</tspan></text></g></g><g><line x1="315" y1="361" x2="75" y2="361" class="arc directional method" style="stroke:blue" marker-end="url(#mscgen_js-svg-__svgmethod-blue)"></line><g><rect width="130.72" height="14" x="129.63" y="345.5" class="label-text-background"></rect><text x="195" y="356.5" class="directional-text method-text " style="fill:blue;"><tspan>JSON response w/ ETag</tspan></text></g></g></g><g id="mscgen_js-svg-__svg__notelayer"></g><g id="mscgen_js-svg-__svg__watermark"></g></g></svg>
\ No newline at end of file diff --git a/doc/development/polling.md b/doc/development/polling.md index a7f2962acf0..e5a717f712b 100644 --- a/doc/development/polling.md +++ b/doc/development/polling.md @@ -22,6 +22,9 @@ Instead you should use polling mechanism with ETag caching in Redis. ## How it works +![Cache miss](img/cache-miss.svg) +![Cache hit](img/cache-hit.svg) + 1. Whenever a resource changes we generate a random value and store it in Redis. 1. When a client makes a request we set the `ETag` response header to the value diff --git a/doc/install/requirements.md b/doc/install/requirements.md index 7b586138f42..35586091f74 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -129,6 +129,9 @@ If you want to run the database separately, expect a size of about 1 MB per user ### PostgreSQL Requirements +As of GitLab 9.0, PostgreSQL 9.6 is recommended. Lower versions of PostgreSQL +may work but primary testing and developement takes place using PostgreSQL 9.6. + Users using PostgreSQL must ensure the `pg_trgm` extension is loaded into every GitLab database. This extension can be enabled (using a PostgreSQL super user) by running the following query for every database: diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md index 154a0f817da..1c493599cf8 100644 --- a/doc/update/patch_versions.md +++ b/doc/update/patch_versions.md @@ -57,7 +57,7 @@ sudo -u git -H bundle clean sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production # Clean up assets and cache -sudo -u git -H bundle exec rake gitlab:assets:clean gitlab:assets:compile cache:clear RAILS_ENV=production +sudo -u git -H bundle exec rake yarn:install gitlab:assets:clean gitlab:assets:compile cache:clear RAILS_ENV=production ``` ### 4. Update gitlab-workhorse to the corresponding version diff --git a/doc/user/project/integrations/img/jira_project_settings.png b/doc/user/project/integrations/img/jira_project_settings.png Binary files differnew file mode 100644 index 00000000000..cb6a6ba14ce --- /dev/null +++ b/doc/user/project/integrations/img/jira_project_settings.png diff --git a/doc/user/project/integrations/jira.md b/doc/user/project/integrations/jira.md index 4c64d1e0907..e02f81fd972 100644 --- a/doc/user/project/integrations/jira.md +++ b/doc/user/project/integrations/jira.md @@ -157,6 +157,11 @@ the same goal: where `PROJECT-1` is the issue ID of the JIRA project. +>**Note:** +- Only commits and merges into the project's default branch (usually **master**) will + close an issue in Jira. You can change your projects default branch under + [project settings](img/jira_project_settings.png). + ### JIRA issue closing example Let's consider the following example: diff --git a/features/dashboard/dashboard.feature b/features/dashboard/dashboard.feature index b1d5e4a7acb..1af4d46dec9 100644 --- a/features/dashboard/dashboard.feature +++ b/features/dashboard/dashboard.feature @@ -63,7 +63,8 @@ Feature: Dashboard @javascript Scenario: Visiting Project's merge requests after sorting - Given I visit dashboard merge requests page + Given project "Shop" has a "Bugfix MR" merge request open + And I visit dashboard merge requests page And I sort the list by "Oldest updated" And I visit project "Shop" merge requests page Then The list should be sorted by "Oldest updated" diff --git a/features/project/issues/issues.feature b/features/project/issues/issues.feature index b2b4fe72220..27fa67c1843 100644 --- a/features/project/issues/issues.feature +++ b/features/project/issues/issues.feature @@ -56,14 +56,16 @@ Feature: Project Issues @javascript Scenario: Visiting Merge Requests after being sorted the list - Given I visit project "Shop" issues page + Given project "Shop" has a "Bugfix MR" merge request open + And I visit project "Shop" issues page And I sort the list by "Oldest updated" And I visit project "Shop" merge requests page Then The list should be sorted by "Oldest updated" @javascript Scenario: Visiting Merge Requests from a differente Project after sorting - Given I visit project "Shop" merge requests page + Given project "Shop" has a "Bugfix MR" merge request open + And I visit project "Shop" merge requests page And I sort the list by "Oldest updated" And I visit dashboard merge requests page Then The list should be sorted by "Oldest updated" diff --git a/features/steps/project/fork.rb b/features/steps/project/fork.rb index 79db9728227..7591e7d5612 100644 --- a/features/steps/project/fork.rb +++ b/features/steps/project/fork.rb @@ -42,8 +42,7 @@ class Spinach::Features::ProjectFork < Spinach::FeatureSteps end step 'I click link "New merge request"' do - expect(page).to have_content(/new merge request/i) - click_link "New Merge Request" + page.has_link?('New Merge Request') ? click_link("New Merge Request") : click_link('New merge request') end step 'I should see the new merge request page for my namespace' do diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb index c0827ff8fc7..ef1bb453615 100644 --- a/features/steps/project/forked_merge_requests.rb +++ b/features/steps/project/forked_merge_requests.rb @@ -16,7 +16,7 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps end step 'I click link "New Merge Request"' do - click_link "New Merge Request" + page.has_link?('New Merge Request') ? click_link("New Merge Request") : click_link('New merge request') end step 'I should see merge request "Merge Request On Forked Project"' do diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index c9c4f537fad..5510c65265a 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -14,7 +14,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end step 'I click link "New Merge Request"' do - click_link "New Merge Request" + page.has_link?('New Merge Request') ? click_link("New Merge Request") : click_link('New merge request') end step 'I click link "Bug NS-04"' do diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb index 345a28f27dc..782009a32a7 100644 --- a/features/steps/shared/project.rb +++ b/features/steps/shared/project.rb @@ -273,6 +273,10 @@ module SharedProject @project.update(public_builds: false) end + step 'project "Shop" has a "Bugfix MR" merge request open' do + create(:merge_request, title: "Bugfix MR", target_project: project, source_project: project, author: project.users.first) + end + def user_owns_project(user_name:, project_name:, visibility: :private) user = user_exists(user_name, username: user_name.gsub(/\s/, '').underscore) project = Project.find_by(name: project_name) diff --git a/lib/api/helpers/runner.rb b/lib/api/helpers/runner.rb index 74848a6e144..1369b021ea4 100644 --- a/lib/api/helpers/runner.rb +++ b/lib/api/helpers/runner.rb @@ -50,10 +50,14 @@ module API forbidden!('Job has been erased!') if job.erased? end - def authenticate_job!(job) + def authenticate_job! + job = Ci::Build.find_by_id(params[:id]) + validate_job!(job) do forbidden! unless job_token_valid?(job) end + + job end def job_token_valid?(job) diff --git a/lib/api/milestones.rb b/lib/api/milestones.rb index e7ab82f08db..a3ea619a2fb 100644 --- a/lib/api/milestones.rb +++ b/lib/api/milestones.rb @@ -139,7 +139,7 @@ module API finder_params = { project_id: user_project.id, - milestone_id: milestone.id, + milestone_title: milestone.title, sort: 'position_asc' } diff --git a/lib/api/runner.rb b/lib/api/runner.rb index 4c9db2c8716..d288369e362 100644 --- a/lib/api/runner.rb +++ b/lib/api/runner.rb @@ -113,8 +113,7 @@ module API optional :state, type: String, desc: %q(Job's status: success, failed) end put '/:id' do - job = Ci::Build.find_by_id(params[:id]) - authenticate_job!(job) + job = authenticate_job! job.update_attributes(trace: params[:trace]) if params[:trace] @@ -140,8 +139,7 @@ module API optional :token, type: String, desc: %q(Job's authentication token) end patch '/:id/trace' do - job = Ci::Build.find_by_id(params[:id]) - authenticate_job!(job) + job = authenticate_job! error!('400 Missing header Content-Range', 400) unless request.headers.has_key?('Content-Range') content_range = request.headers['Content-Range'] @@ -175,8 +173,7 @@ module API require_gitlab_workhorse! Gitlab::Workhorse.verify_api_request!(headers) - job = Ci::Build.find_by_id(params[:id]) - authenticate_job!(job) + job = authenticate_job! forbidden!('Job is not running') unless job.running? if params[:filesize] @@ -212,8 +209,7 @@ module API not_allowed! unless Gitlab.config.artifacts.enabled require_gitlab_workhorse! - job = Ci::Build.find_by_id(params[:id]) - authenticate_job!(job) + job = authenticate_job! forbidden!('Job is not running!') unless job.running? artifacts_upload_path = ArtifactUploader.artifacts_upload_path @@ -245,8 +241,7 @@ module API optional :token, type: String, desc: %q(Job's authentication token) end get '/:id/artifacts' do - job = Ci::Build.find_by_id(params[:id]) - authenticate_job!(job) + job = authenticate_job! artifacts_file = job.artifacts_file unless artifacts_file.file_storage? diff --git a/lib/api/services.rb b/lib/api/services.rb index 4e0c9cb1f63..6802a99311e 100644 --- a/lib/api/services.rb +++ b/lib/api/services.rb @@ -562,8 +562,14 @@ module API desc: 'URL to the mock service' } ] + services['mock-deployment'] = [] + services['mock-monitoring'] = [] - service_classes << MockCiService + service_classes += [ + MockCiService, + MockDeploymentService, + MockMonitoringService, + ] end trigger_services = { diff --git a/lib/ci/api/builds.rb b/lib/ci/api/builds.rb index 746e76a1b1f..95cc6308c3b 100644 --- a/lib/ci/api/builds.rb +++ b/lib/ci/api/builds.rb @@ -86,8 +86,7 @@ module Ci # Example Request: # PATCH /builds/:id/trace.txt patch ":id/trace.txt" do - build = Ci::Build.find_by_id(params[:id]) - authenticate_build!(build) + build = authenticate_build! error!('400 Missing header Content-Range', 400) unless request.headers.has_key?('Content-Range') content_range = request.headers['Content-Range'] @@ -117,8 +116,7 @@ module Ci require_gitlab_workhorse! Gitlab::Workhorse.verify_api_request!(headers) not_allowed! unless Gitlab.config.artifacts.enabled - build = Ci::Build.find_by_id(params[:id]) - authenticate_build!(build) + build = authenticate_build! forbidden!('build is not running') unless build.running? if params[:filesize] @@ -154,8 +152,7 @@ module Ci post ":id/artifacts" do require_gitlab_workhorse! not_allowed! unless Gitlab.config.artifacts.enabled - build = Ci::Build.find_by_id(params[:id]) - authenticate_build!(build) + build = authenticate_build! forbidden!('Build is not running!') unless build.running? artifacts_upload_path = ArtifactUploader.artifacts_upload_path @@ -189,8 +186,7 @@ module Ci # Example Request: # GET /builds/:id/artifacts get ":id/artifacts" do - build = Ci::Build.find_by_id(params[:id]) - authenticate_build!(build) + build = authenticate_build! artifacts_file = build.artifacts_file unless artifacts_file.file_storage? @@ -214,8 +210,7 @@ module Ci # Example Request: # DELETE /builds/:id/artifacts delete ":id/artifacts" do - build = Ci::Build.find_by_id(params[:id]) - authenticate_build!(build) + build = authenticate_build! status(200) build.erase_artifacts! diff --git a/lib/ci/api/helpers.rb b/lib/ci/api/helpers.rb index 996990b464f..5109dc9670f 100644 --- a/lib/ci/api/helpers.rb +++ b/lib/ci/api/helpers.rb @@ -13,10 +13,14 @@ module Ci forbidden! unless current_runner end - def authenticate_build!(build) + def authenticate_build! + build = Ci::Build.find_by_id(params[:id]) + validate_build!(build) do forbidden! unless build_token_valid?(build) end + + build end def validate_build!(build) diff --git a/lib/gitlab/import_export/hash_util.rb b/lib/gitlab/import_export/hash_util.rb new file mode 100644 index 00000000000..d4adeeb3797 --- /dev/null +++ b/lib/gitlab/import_export/hash_util.rb @@ -0,0 +1,25 @@ +module Gitlab + module ImportExport + class HashUtil + def self.deep_symbolize_array!(array) + return if array.blank? + + array.map! do |hash| + hash.deep_symbolize_keys! + + yield(hash) if block_given? + + hash + end + end + + def self.deep_symbolize_array_with_date!(array) + self.deep_symbolize_array!(array) do |hash| + hash.select { |k, _v| k.to_s.end_with?('_date') }.each do |key, value| + hash[key] = Time.zone.parse(value) + end + end + end + end + end +end diff --git a/lib/gitlab/import_export/import_export.yml b/lib/gitlab/import_export/import_export.yml index ab74c8782f6..f69288f7d67 100644 --- a/lib/gitlab/import_export/import_export.yml +++ b/lib/gitlab/import_export/import_export.yml @@ -89,3 +89,5 @@ methods: - :type merge_request_diff: - :utf8_st_diffs + merge_requests: + - :diff_head_sha diff --git a/lib/gitlab/import_export/importer.rb b/lib/gitlab/import_export/importer.rb index 063ce74ecad..fbdd74788bc 100644 --- a/lib/gitlab/import_export/importer.rb +++ b/lib/gitlab/import_export/importer.rb @@ -9,7 +9,7 @@ module Gitlab end def execute - if import_file && check_version! && [project_tree, avatar_restorer, repo_restorer, wiki_restorer, uploads_restorer].all?(&:restore) + if import_file && check_version! && [repo_restorer, wiki_restorer, project_tree, avatar_restorer, uploads_restorer].all?(&:restore) project_tree.restored_project else raise Projects::ImportService::Error.new(@shared.errors.join(', ')) diff --git a/lib/gitlab/import_export/merge_request_parser.rb b/lib/gitlab/import_export/merge_request_parser.rb new file mode 100644 index 00000000000..c20adc20bfd --- /dev/null +++ b/lib/gitlab/import_export/merge_request_parser.rb @@ -0,0 +1,41 @@ +module Gitlab + module ImportExport + class MergeRequestParser + FORKED_PROJECT_ID = -1 + + def initialize(project, diff_head_sha, merge_request, relation_hash) + @project = project + @diff_head_sha = diff_head_sha + @merge_request = merge_request + @relation_hash = relation_hash + end + + def parse! + if fork_merge_request? && @diff_head_sha + @merge_request.source_project_id = @relation_hash['project_id'] + + fetch_ref unless branch_exists?(@merge_request.source_branch) + create_target_branch unless branch_exists?(@merge_request.target_branch) + end + + @merge_request + end + + def create_target_branch + @project.repository.create_branch(@merge_request.target_branch, @merge_request.target_branch_sha) + end + + def fetch_ref + @project.repository.fetch_ref(@project.repository.path, @diff_head_sha, @merge_request.source_branch) + end + + def branch_exists?(branch_name) + @project.repository.branch_exists?(branch_name) + end + + def fork_merge_request? + @relation_hash['source_project_id'] == FORKED_PROJECT_ID + end + end + end +end diff --git a/lib/gitlab/import_export/project_tree_restorer.rb b/lib/gitlab/import_export/project_tree_restorer.rb index cda6ddf0443..df21ff22216 100644 --- a/lib/gitlab/import_export/project_tree_restorer.rb +++ b/lib/gitlab/import_export/project_tree_restorer.rb @@ -119,7 +119,7 @@ module Gitlab relation_hash: parsed_relation_hash(relation_hash), members_mapper: members_mapper, user: @user, - project_id: restored_project.id) + project: restored_project) end.compact relation_hash_list.is_a?(Array) ? relation_array : relation_array.first diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb index d44563333a5..fb43e7ccdbb 100644 --- a/lib/gitlab/import_export/relation_factory.rb +++ b/lib/gitlab/import_export/relation_factory.rb @@ -29,11 +29,12 @@ module Gitlab new(*args).create end - def initialize(relation_sym:, relation_hash:, members_mapper:, user:, project_id:) + def initialize(relation_sym:, relation_hash:, members_mapper:, user:, project:) @relation_name = OVERRIDES[relation_sym] || relation_sym - @relation_hash = relation_hash.except('noteable_id').merge('project_id' => project_id) + @relation_hash = relation_hash.except('noteable_id').merge('project_id' => project.id) @members_mapper = members_mapper @user = user + @project = project @imported_object_retries = 0 end @@ -66,7 +67,7 @@ module Gitlab remove_encrypted_attributes! @relation_hash['data'].deep_symbolize_keys! if @relation_name == :events && @relation_hash['data'] - set_st_diffs if @relation_name == :merge_request_diff + set_st_diff_commits if @relation_name == :merge_request_diff end def update_user_references @@ -105,6 +106,8 @@ module Gitlab imported_object do |object| object.commit_id = nil end + elsif @relation_name == :merge_requests + MergeRequestParser.new(@project, @relation_hash.delete('diff_head_sha'), imported_object, @relation_hash).parse! else imported_object end @@ -115,7 +118,7 @@ module Gitlab # If source and target are the same, populate them with the new project ID. if @relation_hash['source_project_id'] - @relation_hash['source_project_id'] = same_source_and_target? ? project_id : -1 + @relation_hash['source_project_id'] = same_source_and_target? ? project_id : MergeRequestParser::FORKED_PROJECT_ID end # project_id may not be part of the export, but we always need to populate it if required. @@ -166,6 +169,7 @@ module Gitlab def imported_object yield(existing_or_new_object) if block_given? existing_or_new_object.importing = true if existing_or_new_object.respond_to?(:importing) + existing_or_new_object rescue ActiveRecord::RecordNotUnique # as the operation is not atomic, retry in the unlikely scenario an INSERT is @@ -188,8 +192,11 @@ module Gitlab relation_class: relation_class) end - def set_st_diffs + def set_st_diff_commits @relation_hash['st_diffs'] = @relation_hash.delete('utf8_st_diffs') + + HashUtil.deep_symbolize_array!(@relation_hash['st_diffs']) + HashUtil.deep_symbolize_array_with_date!(@relation_hash['st_commits']) end def existing_or_new_object diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb index db325c00705..0b8959f2fb9 100644 --- a/lib/gitlab/project_search_results.rb +++ b/lib/gitlab/project_search_results.rb @@ -62,7 +62,7 @@ module Gitlab data << line.sub(ref, '').sub(filename, '').sub(/^:-\d+-/, '').sub(/^::\d+:/, '') end - OpenStruct.new( + FoundBlob.new( filename: filename, basename: basename, ref: ref, diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb index ccfa517e04b..efe8095beea 100644 --- a/lib/gitlab/search_results.rb +++ b/lib/gitlab/search_results.rb @@ -1,5 +1,26 @@ module Gitlab class SearchResults + class FoundBlob + attr_reader :id, :filename, :basename, :ref, :startline, :data + + def initialize(opts = {}) + @id = opts.fetch(:id, nil) + @filename = opts.fetch(:filename, nil) + @basename = opts.fetch(:basename, nil) + @ref = opts.fetch(:ref, nil) + @startline = opts.fetch(:startline, nil) + @data = opts.fetch(:data, nil) + end + + def path + filename + end + + def no_highlighting? + false + end + end + attr_reader :current_user, :query # Limit search results by passed projects diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb index b631ef11ce7..36a871e5bbc 100644 --- a/lib/gitlab/shell.rb +++ b/lib/gitlab/shell.rb @@ -35,7 +35,7 @@ module Gitlab end def strip_key(key) - key.split(/ /)[0, 2].join(' ') + key.split(/[ ]+/)[0, 2].join(' ') end private diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb index d0637f8b394..08011301d3c 100644 --- a/lib/gitlab/workhorse.rb +++ b/lib/gitlab/workhorse.rb @@ -35,7 +35,8 @@ module Gitlab feature_enabled = case action.to_s when 'git_receive_pack' - Gitlab::GitalyClient.feature_enabled?(:post_receive_pack) + # Disabled for now, see https://gitlab.com/gitlab-org/gitaly/issues/172 + false when 'git_upload_pack' Gitlab::GitalyClient.feature_enabled?(:post_upload_pack) when 'info_refs' @@ -44,7 +45,12 @@ module Gitlab raise "Unsupported action: #{action}" end - params[:GitalySocketPath] = URI(address).path if feature_enabled + if feature_enabled + params[:GitalyAddress] = address + # TODO deprecate GitalySocketPath once GITLAB_WORKHORSE_VERSION points + # to a version that supports GitalyAddress. + params[:GitalySocketPath] = URI(address).path + end end params diff --git a/spec/controllers/import/bitbucket_controller_spec.rb b/spec/controllers/import/bitbucket_controller_spec.rb index 51f23e4eeb9..010e3180ea4 100644 --- a/spec/controllers/import/bitbucket_controller_spec.rb +++ b/spec/controllers/import/bitbucket_controller_spec.rb @@ -200,5 +200,72 @@ describe Import::BitbucketController do end end end + + context 'user has chosen an existing nested namespace and name for the project' do + let(:parent_namespace) { create(:namespace, name: 'foo', owner: user) } + let(:nested_namespace) { create(:namespace, name: 'bar', parent: parent_namespace, owner: user) } + let(:test_name) { 'test_name' } + + it 'takes the selected namespace and name' do + expect(Gitlab::BitbucketImport::ProjectCreator). + to receive(:new).with(bitbucket_repo, test_name, nested_namespace, user, access_params). + and_return(double(execute: true)) + + post :create, { target_namespace: nested_namespace.full_path, new_name: test_name, format: :js } + end + end + + context 'user has chosen a non-existent nested namespaces and name for the project' do + let(:test_name) { 'test_name' } + + it 'takes the selected namespace and name' do + expect(Gitlab::BitbucketImport::ProjectCreator). + to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params). + and_return(double(execute: true)) + + post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js } + end + + it 'creates the namespaces' do + allow(Gitlab::BitbucketImport::ProjectCreator). + to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params). + and_return(double(execute: true)) + + expect { post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js } } + .to change { Namespace.count }.by(2) + end + + it 'new namespace has the right parent' do + allow(Gitlab::BitbucketImport::ProjectCreator). + to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params). + and_return(double(execute: true)) + + post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js } + + expect(Namespace.find_by_path_or_name('bar').parent.path).to eq('foo') + end + end + + context 'user has chosen existent and non-existent nested namespaces and name for the project' do + let(:test_name) { 'test_name' } + let!(:parent_namespace) { create(:namespace, name: 'foo', owner: user) } + + it 'takes the selected namespace and name' do + expect(Gitlab::BitbucketImport::ProjectCreator). + to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params). + and_return(double(execute: true)) + + post :create, { target_namespace: 'foo/foobar/bar', new_name: test_name, format: :js } + end + + it 'creates the namespaces' do + allow(Gitlab::BitbucketImport::ProjectCreator). + to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params). + and_return(double(execute: true)) + + expect { post :create, { target_namespace: 'foo/foobar/bar', new_name: test_name, format: :js } } + .to change { Namespace.count }.by(2) + end + end end end diff --git a/spec/controllers/import/gitlab_controller_spec.rb b/spec/controllers/import/gitlab_controller_spec.rb index 3f73ea000ae..2dbb89219d0 100644 --- a/spec/controllers/import/gitlab_controller_spec.rb +++ b/spec/controllers/import/gitlab_controller_spec.rb @@ -174,6 +174,72 @@ describe Import::GitlabController do end end end + + context 'user has chosen an existing nested namespace for the project' do + let(:parent_namespace) { create(:namespace, name: 'foo', owner: user) } + let(:nested_namespace) { create(:namespace, name: 'bar', parent: parent_namespace, owner: user) } + + it 'takes the selected namespace and name' do + expect(Gitlab::GitlabImport::ProjectCreator). + to receive(:new).with(gitlab_repo, nested_namespace, user, access_params). + and_return(double(execute: true)) + + post :create, { target_namespace: nested_namespace.full_path, format: :js } + end + end + + context 'user has chosen a non-existent nested namespaces for the project' do + let(:test_name) { 'test_name' } + + it 'takes the selected namespace and name' do + expect(Gitlab::GitlabImport::ProjectCreator). + to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params). + and_return(double(execute: true)) + + post :create, { target_namespace: 'foo/bar', format: :js } + end + + it 'creates the namespaces' do + allow(Gitlab::GitlabImport::ProjectCreator). + to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params). + and_return(double(execute: true)) + + expect { post :create, { target_namespace: 'foo/bar', format: :js } } + .to change { Namespace.count }.by(2) + end + + it 'new namespace has the right parent' do + allow(Gitlab::GitlabImport::ProjectCreator). + to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params). + and_return(double(execute: true)) + + post :create, { target_namespace: 'foo/bar', format: :js } + + expect(Namespace.find_by_path_or_name('bar').parent.path).to eq('foo') + end + end + + context 'user has chosen existent and non-existent nested namespaces and name for the project' do + let(:test_name) { 'test_name' } + let!(:parent_namespace) { create(:namespace, name: 'foo', owner: user) } + + it 'takes the selected namespace and name' do + expect(Gitlab::GitlabImport::ProjectCreator). + to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params). + and_return(double(execute: true)) + + post :create, { target_namespace: 'foo/foobar/bar', format: :js } + end + + it 'creates the namespaces' do + allow(Gitlab::GitlabImport::ProjectCreator). + to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params). + and_return(double(execute: true)) + + expect { post :create, { target_namespace: 'foo/foobar/bar', format: :js } } + .to change { Namespace.count }.by(2) + end + end end end end diff --git a/spec/features/admin/admin_health_check_spec.rb b/spec/features/admin/admin_health_check_spec.rb index f7e49a56deb..523afa2318f 100644 --- a/spec/features/admin/admin_health_check_spec.rb +++ b/spec/features/admin/admin_health_check_spec.rb @@ -2,7 +2,6 @@ require 'spec_helper' feature "Admin Health Check", feature: true do include StubENV - include WaitForAjax before do stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') @@ -24,11 +23,12 @@ feature "Admin Health Check", feature: true do expect(page).to have_selector('#health-check-token', text: token) end - describe 'reload access token', js: true do + describe 'reload access token' do it 'changes the access token' do orig_token = current_application_settings.health_check_access_token click_button 'Reset health check access token' - wait_for_ajax + + expect(page).to have_content('New health check access token has been generated!') expect(find('#health-check-token').text).not_to eq orig_token end end diff --git a/spec/features/dashboard/merge_requests_spec.rb b/spec/features/dashboard/merge_requests_spec.rb new file mode 100644 index 00000000000..508ca38d7e5 --- /dev/null +++ b/spec/features/dashboard/merge_requests_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +describe 'Dashboard Merge Requests' do + let(:current_user) { create :user } + let(:project) do + create(:empty_project) do |project| + project.add_master(current_user) + end + end + + before do + login_as(current_user) + end + + it 'should show an empty state' do + visit merge_requests_dashboard_path(assignee_id: current_user.id) + + expect(page).to have_selector('.empty-state') + end + + context 'if there are merge requests' do + before do + create(:merge_request, assignee: current_user, source_project: project) + + visit merge_requests_dashboard_path(assignee_id: current_user.id) + end + + it 'should not show an empty state' do + expect(page).not_to have_selector('.empty-state') + end + end +end diff --git a/spec/features/groups/empty_states_spec.rb b/spec/features/groups/empty_states_spec.rb new file mode 100644 index 00000000000..fef8e41bffe --- /dev/null +++ b/spec/features/groups/empty_states_spec.rb @@ -0,0 +1,70 @@ +require 'spec_helper' + +feature 'Groups Merge Requests Empty States' do + let(:group) { create(:group) } + let(:user) { create(:group_member, :developer, user: create(:user), group: group ).user } + + before do + login_as(user) + end + + context 'group has a project' do + let(:project) { create(:empty_project, namespace: group) } + + before do + project.add_master(user) + end + + context 'the project has a merge request' do + before do + create(:merge_request, source_project: project) + + visit merge_requests_group_path(group) + end + + it 'should not display an empty state' do + expect(page).not_to have_selector('.empty-state') + end + end + + context 'the project has no merge requests', :js do + before do + visit merge_requests_group_path(group) + end + + it 'should display an empty state' do + expect(page).to have_selector('.empty-state') + end + + it 'should show a new merge request button' do + within '.empty-state' do + expect(page).to have_content('New merge request') + end + end + + it 'the new merge request button opens a project dropdown' do + within '.empty-state' do + find('.new-project-item-select-button').click + end + + expect(page).to have_selector('.ajax-project-dropdown') + end + end + end + + context 'group without a project' do + before do + visit merge_requests_group_path(group) + end + + it 'should display an empty state' do + expect(page).to have_selector('.empty-state') + end + + it 'should not show a new merge request button' do + within '.empty-state' do + expect(page).not_to have_link('New merge request') + end + end + end +end diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb index 004e335dd38..2f880c926e7 100644 --- a/spec/features/issues/filtered_search/filter_issues_spec.rb +++ b/spec/features/issues/filtered_search/filter_issues_spec.rb @@ -1,6 +1,7 @@ require 'spec_helper' describe 'Filter issues', js: true, feature: true do + include Devise::Test::IntegrationHelpers include FilteredSearchHelpers include WaitForAjax @@ -42,16 +43,17 @@ describe 'Filter issues', js: true, feature: true do project.team << [user2, :master] group.add_developer(user) group.add_developer(user2) - login_as(user) - create(:issue, project: project) - create(:issue, title: "Bug report 1", project: project) - create(:issue, title: "Bug report 2", project: project) - create(:issue, title: "issue with 'single quotes'", project: project) - create(:issue, title: "issue with \"double quotes\"", project: project) - create(:issue, title: "issue with !@\#{$%^&*()-+", project: project) - create(:issue, title: "issue by assignee", project: project, milestone: milestone, author: user, assignee: user) - create(:issue, title: "issue by assignee with searchTerm", project: project, milestone: milestone, author: user, assignee: user) + sign_in(user) + + create(:issue, project: project) + create(:issue, project: project, title: "Bug report 1") + create(:issue, project: project, title: "Bug report 2") + create(:issue, project: project, title: "issue with 'single quotes'") + create(:issue, project: project, title: "issue with \"double quotes\"") + create(:issue, project: project, title: "issue with !@\#{$%^&*()-+") + create(:issue, project: project, title: "issue by assignee", milestone: milestone, author: user, assignee: user) + create(:issue, project: project, title: "issue by assignee with searchTerm", milestone: milestone, author: user, assignee: user) issue = create(:issue, title: "Bug 2", diff --git a/spec/features/merge_requests/create_new_mr_spec.rb b/spec/features/merge_requests/create_new_mr_spec.rb index f1ad4a55246..f36781167fb 100644 --- a/spec/features/merge_requests/create_new_mr_spec.rb +++ b/spec/features/merge_requests/create_new_mr_spec.rb @@ -15,7 +15,7 @@ feature 'Create New Merge Request', feature: true, js: true do it 'selects the source branch sha when a tag with the same name exists' do visit namespace_project_merge_requests_path(project.namespace, project) - click_link 'New Merge Request' + click_link 'New merge request' expect(page).to have_content('Source branch') expect(page).to have_content('Target branch') @@ -27,8 +27,8 @@ feature 'Create New Merge Request', feature: true, js: true do it 'selects the target branch sha when a tag with the same name exists' do visit namespace_project_merge_requests_path(project.namespace, project) - - click_link 'New Merge Request' + + click_link 'New merge request' expect(page).to have_content('Source branch') expect(page).to have_content('Target branch') @@ -42,7 +42,7 @@ feature 'Create New Merge Request', feature: true, js: true do it 'generates a diff for an orphaned branch' do visit namespace_project_merge_requests_path(project.namespace, project) - click_link 'New Merge Request' + page.has_link?('New Merge Request') ? click_link("New Merge Request") : click_link('New merge request') expect(page).to have_content('Source branch') expect(page).to have_content('Target branch') diff --git a/spec/features/milestones/milestones_spec.rb b/spec/features/milestones/milestones_spec.rb index 8de9942c54e..2fa3e72ab08 100644 --- a/spec/features/milestones/milestones_spec.rb +++ b/spec/features/milestones/milestones_spec.rb @@ -76,6 +76,7 @@ describe 'Milestone draggable', feature: true, js: true do create(:issue, params.merge(title: 'Foo', project: project, milestone: milestone)) visit namespace_project_milestone_path(project.namespace, project, milestone) + scroll_into_view('.milestone-content') drag_to(selector: '.issues-sortable-list', list_to_index: 1) wait_for_ajax @@ -86,8 +87,13 @@ describe 'Milestone draggable', feature: true, js: true do visit namespace_project_milestone_path(project.namespace, project, milestone) page.find("a[href='#tab-merge-requests']").click + scroll_into_view('.milestone-content') drag_to(selector: '.merge_requests-sortable-list', list_to_index: 1) wait_for_ajax end + + def scroll_into_view(selector) + page.evaluate_script("document.querySelector('#{selector}').scrollIntoView();") + end end diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb index 641e2cf7402..cf393afccbb 100644 --- a/spec/features/projects/environments/environments_spec.rb +++ b/spec/features/projects/environments/environments_spec.rb @@ -23,6 +23,46 @@ feature 'Environments page', :feature, :js do expect(page).to have_link('Available') expect(page).to have_link('Stopped') end + + describe 'with one available environment' do + given(:environment) { create(:environment, project: project, state: :available) } + + describe 'in available tab page' do + it 'should show one environment' do + visit namespace_project_environments_path(project.namespace, project, scope: 'available') + expect(page).to have_css('.environments-container') + expect(page.all('tbody > tr').length).to eq(1) + end + end + + describe 'in stopped tab page' do + it 'should show no environments' do + visit namespace_project_environments_path(project.namespace, project, scope: 'stopped') + expect(page).to have_css('.environments-container') + expect(page).to have_content('You don\'t have any environments right now') + end + end + end + + describe 'with one stopped environment' do + given(:environment) { create(:environment, project: project, state: :stopped) } + + describe 'in available tab page' do + it 'should show no environments' do + visit namespace_project_environments_path(project.namespace, project, scope: 'available') + expect(page).to have_css('.environments-container') + expect(page).to have_content('You don\'t have any environments right now') + end + end + + describe 'in stopped tab page' do + it 'should show one environment' do + visit namespace_project_environments_path(project.namespace, project, scope: 'stopped') + expect(page).to have_css('.environments-container') + expect(page.all('tbody > tr').length).to eq(1) + end + end + end end context 'without environments' do diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb index 2d1106ea3e8..583f479ec18 100644 --- a/spec/features/projects/import_export/import_file_spec.rb +++ b/spec/features/projects/import_export/import_file_spec.rb @@ -69,12 +69,8 @@ feature 'Import/Export - project import integration test', feature: true, js: tr select2(namespace.id, from: '#project_namespace_id') - # click on disabled element - find(:link, 'GitLab export').trigger('click') - - page.within('.flash-container') do - expect(page).to have_content('Please enter path and name') - end + # Check for tooltip disabled import button + expect(find('.import_gitlab_project')['title']).to eq('Please enter a valid project name.') end end diff --git a/spec/features/projects/merge_requests/list_spec.rb b/spec/features/projects/merge_requests/list_spec.rb index 5dd58ad66a7..7e8a796c55d 100644 --- a/spec/features/projects/merge_requests/list_spec.rb +++ b/spec/features/projects/merge_requests/list_spec.rb @@ -17,4 +17,28 @@ feature 'Merge Requests List' do expect(page).not_to have_selector('.js-new-board-list') end + + it 'should show an empty state' do + visit namespace_project_merge_requests_path(project.namespace, project) + + expect(page).to have_selector('.empty-state') + end + + it 'empty state should have a create merge request button' do + visit namespace_project_merge_requests_path(project.namespace, project) + + expect(page).to have_link 'New merge request', href: new_namespace_project_merge_request_path(project.namespace, project) + end + + context 'if there are merge requests' do + before do + create(:merge_request, assignee: user, source_project: project) + + visit namespace_project_merge_requests_path(project.namespace, project) + end + + it 'should not show an empty state' do + expect(page).not_to have_selector('.empty-state') + end + end end diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb index fff8b9f3447..7bdaafd1beb 100644 --- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb @@ -15,6 +15,10 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do let(:project) { create(:project, namespace: user.namespace) } context 'when wiki is empty' do + scenario 'commit message field has value "Create home"' do + expect(page).to have_field('wiki[message]', with: 'Create home') + end + scenario 'directly from the wiki home page' do fill_in :wiki_content, with: 'My awesome wiki!' click_button 'Create page' @@ -37,6 +41,9 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do fill_in :new_wiki_path, with: 'foo' click_button 'Create Page' + # Commit message field should have correct value. + expect(page).to have_field('wiki[message]', with: 'Create foo') + fill_in :wiki_content, with: 'My awesome wiki!' click_button 'Create page' @@ -51,6 +58,9 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do fill_in :new_wiki_path, with: 'Spaces in the name' click_button 'Create Page' + # Commit message field should have correct value. + expect(page).to have_field('wiki[message]', with: 'Create spaces in the name') + fill_in :wiki_content, with: 'My awesome wiki!' click_button 'Create page' @@ -65,6 +75,9 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do fill_in :new_wiki_path, with: 'hyphens-in-the-name' click_button 'Create Page' + # Commit message field should have correct value. + expect(page).to have_field('wiki[message]', with: 'Create hyphens in the name') + fill_in :wiki_content, with: 'My awesome wiki!' click_button 'Create page' @@ -80,6 +93,10 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do let(:project) { create(:project, namespace: create(:group, :public)) } context 'when wiki is empty' do + scenario 'commit message field has value "Create home"' do + expect(page).to have_field('wiki[message]', with: 'Create home') + end + scenario 'directly from the wiki home page' do fill_in :wiki_content, with: 'My awesome wiki!' click_button 'Create page' @@ -101,6 +118,9 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do fill_in :new_wiki_path, with: 'foo' click_button 'Create Page' + # Commit message field should have correct value. + expect(page).to have_field('wiki[message]', with: 'Create foo') + fill_in :wiki_content, with: 'My awesome wiki!' click_button 'Create page' diff --git a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb index aedc0333cb9..86cf520ea80 100644 --- a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb @@ -19,6 +19,9 @@ feature 'Projects > Wiki > User updates wiki page', feature: true do scenario 'success when the wiki content is not empty' do click_link 'Edit' + # Commit message field should have correct value. + expect(page).to have_field('wiki[message]', with: 'Update home') + fill_in :wiki_content, with: 'My awesome wiki!' click_button 'Save changes' @@ -48,6 +51,9 @@ feature 'Projects > Wiki > User updates wiki page', feature: true do scenario 'the home page' do click_link 'Edit' + # Commit message field should have correct value. + expect(page).to have_field('wiki[message]', with: 'Update home') + fill_in :wiki_content, with: 'My awesome wiki!' click_button 'Save changes' diff --git a/spec/features/search_spec.rb b/spec/features/search_spec.rb index a6560a81096..e8ad28a00f0 100644 --- a/spec/features/search_spec.rb +++ b/spec/features/search_spec.rb @@ -119,13 +119,15 @@ describe "Search", feature: true do visit namespace_project_path(project.namespace, project) page.within '.search' do - fill_in 'search', with: 'def' + fill_in 'search', with: 'application.js' click_button 'Go' end click_link "Code" expect(page).to have_selector('.file-content .code') + + expect(page).to have_selector("span.line[lang='javascript']") end end @@ -162,6 +164,8 @@ describe "Search", feature: true do end context 'click the links in the category search dropdown', js: true do + let!(:merge_request) { create(:merge_request, source_project: project, author: user, assignee: user) } + before do page.find('#search').click end diff --git a/spec/fixtures/metrics.json b/spec/fixtures/metrics.json new file mode 100644 index 00000000000..06427adce57 --- /dev/null +++ b/spec/fixtures/metrics.json @@ -0,0 +1 @@ +{"success":true,"metrics":{"memory_values":[{"metric":{},"values":[[1490935421.33,"9.832775297619047"],[1490935481.33,"9.8359375"],[1490935541.33,"9.837983630952381"],[1490935601.33,"9.840401785714286"],[1490935661.33,"9.84375"],[1490935721.33,"9.846168154761905"],[1490935781.33,"9.849516369047619"],[1490935841.33,"9.85249255952381"],[1490935901.33,"9.855096726190476"],[1490935961.33,"9.845796130952381"],[1490936021.33,"9.847284226190476"],[1490936081.33,"9.84468005952381"],[1490936141.33,"9.847470238095237"],[1490936201.33,"9.850818452380953"],[1490936261.33,"9.852864583333334"],[1490936321.33,"9.854910714285714"],[1490936381.33,"9.857700892857142"],[1490936441.33,"9.865513392857142"],[1490936501.33,"9.874813988095237"],[1490936561.33,"9.866071428571429"],[1490936621.33,"9.849330357142858"],[1490936681.33,"9.841331845238095"],[1490936741.33,"9.853236607142858"],[1490936801.33,"9.839657738095237"],[1490936861.33,"9.841517857142858"],[1490936921.33,"9.852864583333334"],[1490936981.33,"9.851376488095237"],[1490937041.33,"9.837611607142858"],[1490937101.33,"9.840401785714286"],[1490937161.33,"9.843377976190476"],[1490937221.33,"9.845796130952381"],[1490937281.33,"9.84858630952381"],[1490937341.33,"9.866071428571429"],[1490937401.33,"9.852864583333334"],[1490937461.33,"9.855840773809524"],[1490937521.33,"9.837797619047619"],[1490937581.33,"9.840959821428571"],[1490937641.33,"9.848958333333334"],[1490937701.33,"9.844308035714286"],[1490937761.33,"9.845982142857142"],[1490937821.33,"9.83984375"],[1490937881.33,"9.830171130952381"],[1490937941.33,"9.83686755952381"],[1490938001.33,"9.834263392857142"],[1490938061.33,"9.836309523809524"],[1490938121.33,"9.83984375"],[1490938181.33,"9.832775297619047"],[1490938241.33,"9.818266369047619"],[1490938301.33,"9.820126488095237"],[1490938361.33,"9.824032738095237"],[1490938421.33,"9.826078869047619"],[1490938481.33,"9.817708333333334"],[1490938541.33,"9.811755952380953"],[1490938601.33,"9.811197916666666"],[1490938661.33,"9.81156994047619"],[1490938721.33,"9.812313988095237"],[1490938781.33,"9.813058035714286"],[1490938841.33,"9.81343005952381"],[1490938901.33,"9.81547619047619"],[1490938961.33,"9.818824404761905"],[1490939021.33,"9.819754464285714"],[1490939081.33,"9.820684523809524"],[1490939141.33,"9.824776785714286"],[1490939201.33,"9.826078869047619"],[1490939261.33,"9.828311011904763"],[1490939321.33,"9.820870535714286"],[1490939381.33,"9.823846726190476"],[1490939441.33,"9.824404761904763"],[1490939501.33,"9.82905505952381"],[1490939561.33,"9.832775297619047"],[1490939621.33,"9.835565476190476"],[1490939681.33,"9.833333333333334"],[1490939741.33,"9.835379464285714"],[1490939801.33,"9.837239583333334"],[1490939861.33,"9.839285714285714"],[1490939921.33,"9.829613095238095"],[1490939981.33,"9.832403273809524"],[1490940041.33,"9.835751488095237"],[1490940101.33,"9.837797619047619"],[1490940161.33,"9.840959821428571"],[1490940221.33,"9.84375"],[1490940281.33,"9.846354166666666"],[1490940341.33,"9.853980654761905"],[1490940401.33,"9.852678571428571"],[1490940461.33,"9.861979166666666"],[1490940521.33,"9.857700892857142"],[1490940581.33,"9.861793154761905"],[1490940641.33,"9.86421130952381"],[1490940701.33,"9.867001488095237"],[1490940761.33,"9.867931547619047"],[1490940821.33,"9.859933035714286"],[1490940881.33,"9.86235119047619"],[1490940941.33,"9.865141369047619"],[1490941001.33,"9.866443452380953"],[1490941061.33,"9.868861607142858"],[1490941121.33,"9.871465773809524"],[1490941181.33,"9.873511904761905"],[1490941241.33,"9.875558035714286"],[1490941301.33,"9.87797619047619"],[1490941361.33,"9.881324404761905"],[1490941421.33,"9.888392857142858"],[1490941481.33,"9.888392857142858"],[1490941541.33,"9.89546130952381"],[1490941601.33,"9.898065476190476"],[1490941661.33,"9.885044642857142"],[1490941721.33,"9.872395833333334"],[1490941781.33,"9.870349702380953"],[1490941841.33,"9.873325892857142"],[1490941901.33,"9.875558035714286"],[1490941961.33,"9.878534226190476"],[1490942021.33,"9.87983630952381"],[1490942081.33,"9.884300595238095"],[1490942141.33,"9.891927083333334"],[1490942201.33,"9.890252976190476"],[1490942261.33,"9.891927083333334"],[1490942321.33,"9.893787202380953"],[1490942381.33,"9.892113095238095"],[1490942441.33,"9.900111607142858"],[1490942501.33,"9.893415178571429"],[1490942561.33,"9.895647321428571"],[1490942621.33,"9.889322916666666"],[1490942681.33,"9.883556547619047"],[1490942741.33,"9.885602678571429"],[1490942801.33,"9.88764880952381"],[1490942861.33,"9.898623511904763"],[1490942921.33,"9.89453125"],[1490942981.33,"9.885044642857142"],[1490943041.33,"9.874813988095237"],[1490943101.33,"9.880766369047619"],[1490943161.33,"9.868675595238095"],[1490943221.33,"9.864769345238095"],[1490943281.33,"9.852864583333334"],[1490943341.33,"9.855096726190476"],[1490943401.33,"9.857514880952381"],[1490943461.33,"9.859747023809524"],[1490943521.33,"9.861793154761905"],[1490943581.33,"9.864025297619047"],[1490943641.33,"9.857514880952381"],[1490943701.33,"9.859002976190476"],[1490943761.33,"9.860677083333334"],[1490943821.33,"9.864025297619047"],[1490943881.33,"9.86625744047619"],[1490943941.33,"9.873325892857142"],[1490944001.33,"9.876674107142858"],[1490944061.33,"9.888950892857142"],[1490944121.33,"9.878534226190476"],[1490944181.33,"9.880766369047619"],[1490944241.33,"9.884858630952381"],[1490944301.33,"9.870535714285714"],[1490944361.33,"9.864769345238095"],[1490944421.33,"9.851190476190476"],[1490944481.33,"9.85249255952381"],[1490944541.33,"9.85844494047619"],[1490944601.33,"9.855840773809524"],[1490944661.33,"9.868303571428571"],[1490944721.33,"9.859188988095237"],[1490944781.33,"9.860491071428571"],[1490944841.33,"9.863467261904763"],[1490944901.33,"9.864025297619047"],[1490944961.33,"9.857514880952381"],[1490945021.33,"9.843377976190476"],[1490945081.33,"9.836123511904763"],[1490945141.33,"9.837983630952381"],[1490945201.33,"9.84077380952381"],[1490945261.33,"9.847284226190476"],[1490945321.33,"9.849702380952381"],[1490945381.33,"9.827380952380953"],[1490945441.33,"9.82124255952381"],[1490945501.33,"9.822916666666666"],[1490945561.33,"9.824962797619047"],[1490945621.33,"9.814546130952381"],[1490945681.33,"9.805989583333334"],[1490945741.33,"9.791294642857142"],[1490945801.33,"9.786458333333334"],[1490945861.33,"9.77641369047619"],[1490945921.33,"9.76655505952381"],[1490945981.33,"9.76953125"],[1490946041.33,"9.742745535714286"],[1490946101.33,"9.753162202380953"],[1490946161.33,"9.739583333333334"],[1490946221.33,"9.742931547619047"],[1490946281.33,"9.743489583333334"],[1490946341.33,"9.746837797619047"],[1490946401.33,"9.749255952380953"],[1490946461.33,"9.737165178571429"],[1490946521.33,"9.739583333333334"],[1490946581.33,"9.74311755952381"],[1490946641.33,"9.751302083333334"],[1490946701.33,"9.761346726190476"],[1490946761.33,"9.747953869047619"],[1490946821.33,"9.75093005952381"],[1490946881.33,"9.755580357142858"],[1490946941.33,"9.759858630952381"],[1490947001.33,"9.761904761904763"],[1490947061.33,"9.77641369047619"],[1490947121.33,"9.768787202380953"],[1490947181.33,"9.772879464285714"],[1490947241.33,"9.777715773809524"],[1490947301.33,"9.779947916666666"],[1490947361.33,"9.772135416666666"],[1490947421.33,"9.77641369047619"],[1490947481.33,"9.783668154761905"],[1490947541.33,"9.780505952380953"],[1490947601.33,"9.777157738095237"],[1490947661.33,"9.759114583333334"],[1490947721.33,"9.761532738095237"],[1490947781.33,"9.763392857142858"],[1490947841.33,"9.765252976190476"],[1490947901.33,"9.760602678571429"],[1490947961.33,"9.751488095238095"],[1490948021.33,"9.757998511904763"],[1490948081.33,"9.759486607142858"],[1490948141.33,"9.754650297619047"],[1490948201.33,"9.728050595238095"],[1490948261.33,"9.73530505952381"],[1490948321.33,"9.718005952380953"],[1490948381.33,"9.732142857142858"],[1490948441.33,"9.725260416666666"],[1490948501.33,"9.728422619047619"],[1490948561.33,"9.72953869047619"],[1490948621.33,"9.733072916666666"],[1490948681.33,"9.736421130952381"],[1490948741.33,"9.749627976190476"],[1490948801.33,"9.740141369047619"],[1490948861.33,"9.74311755952381"],[1490948921.33,"9.736607142857142"],[1490948981.33,"9.744233630952381"],[1490949041.33,"9.723772321428571"],[1490949101.33,"9.731956845238095"],[1490949161.33,"9.732514880952381"],[1490949221.33,"9.734747023809524"],[1490949281.33,"9.737723214285714"],[1490949341.33,"9.737909226190476"],[1490949401.33,"9.742373511904763"],[1490949461.33,"9.744977678571429"],[1490949521.33,"9.748139880952381"],[1490949581.33,"9.751302083333334"],[1490949641.33,"9.757440476190476"],[1490949701.33,"9.756324404761905"],[1490949761.33,"9.749813988095237"],[1490949821.33,"9.739025297619047"],[1490949881.33,"9.726004464285714"],[1490949941.33,"9.728236607142858"],[1490950001.33,"9.732514880952381"],[1490950061.33,"9.735119047619047"],[1490950121.33,"9.737165178571429"],[1490950181.33,"9.739025297619047"],[1490950241.33,"9.740513392857142"],[1490950301.33,"9.749441964285714"],[1490950361.33,"9.736979166666666"],[1490950421.33,"9.741629464285714"],[1490950481.33,"9.743303571428571"],[1490950541.33,"9.74609375"],[1490950601.33,"9.75093005952381"],[1490950661.33,"9.724330357142858"],[1490950721.33,"9.726748511904763"],[1490950781.33,"9.733258928571429"],[1490950841.33,"9.744233630952381"],[1490950901.33,"9.734375"],[1490950961.33,"9.737537202380953"],[1490951021.33,"9.741071428571429"],[1490951081.33,"9.757254464285714"],[1490951141.33,"9.760044642857142"],[1490951201.33,"9.755952380952381"],[1490951261.33,"9.745349702380953"],[1490951321.33,"9.746651785714286"],[1490951381.33,"9.749441964285714"],[1490951441.33,"9.751674107142858"],[1490951501.33,"9.757998511904763"],[1490951561.33,"9.756510416666666"],[1490951621.33,"9.76264880952381"],[1490951681.33,"9.765625"],[1490951741.33,"9.757254464285714"],[1490951801.33,"9.751674107142858"],[1490951861.33,"9.754278273809524"],[1490951921.33,"9.744233630952381"],[1490951981.33,"9.745349702380953"],[1490952041.33,"9.748883928571429"],[1490952101.33,"9.753162202380953"],[1490952161.33,"9.747953869047619"],[1490952221.33,"9.750186011904763"],[1490952281.33,"9.751116071428571"],[1490952341.33,"9.753162202380953"],[1490952401.33,"9.758928571428571"],[1490952461.33,"9.758928571428571"],[1490952521.33,"9.755394345238095"],[1490952581.33,"9.758928571428571"],[1490952641.33,"9.761160714285714"],[1490952701.33,"9.763206845238095"],[1490952761.33,"9.767857142857142"],[1490952821.33,"9.765438988095237"],[1490952881.33,"9.768229166666666"],[1490952941.33,"9.780877976190476"],[1490953001.33,"9.77250744047619"],[1490953061.33,"9.784412202380953"],[1490953121.33,"9.77827380952381"],[1490953181.33,"9.781063988095237"],[1490953241.33,"9.783668154761905"],[1490953301.33,"9.787016369047619"],[1490953361.33,"9.784970238095237"],[1490953421.33,"9.787946428571429"],[1490953481.33,"9.788690476190476"],[1490953541.33,"9.790922619047619"],[1490953601.33,"9.792596726190476"],[1490953661.33,"9.79594494047619"],[1490953721.33,"9.79780505952381"],[1490953781.33,"9.800223214285714"],[1490953841.33,"9.794828869047619"],[1490953901.33,"9.799293154761905"],[1490953961.33,"9.801525297619047"],[1490954021.33,"9.786458333333334"],[1490954081.33,"9.773809523809524"],[1490954141.33,"9.767485119047619"],[1490954201.33,"9.760044642857142"],[1490954261.33,"9.751116071428571"],[1490954321.33,"9.752790178571429"],[1490954381.33,"9.753162202380953"],[1490954441.33,"9.744419642857142"],[1490954501.33,"9.73921130952381"],[1490954561.33,"9.74125744047619"],[1490954621.33,"9.743303571428571"],[1490954681.33,"9.745535714285714"],[1490954741.33,"9.746837797619047"],[1490954801.33,"9.749255952380953"],[1490954861.33,"9.744419642857142"],[1490954921.33,"9.745349702380953"],[1490954981.33,"9.74702380952381"],[1490955041.33,"9.738467261904763"],[1490955101.33,"9.740141369047619"],[1490955161.33,"9.747767857142858"],[1490955221.33,"9.750372023809524"],[1490955281.33,"9.747767857142858"],[1490955341.33,"9.739025297619047"],[1490955401.33,"9.745349702380953"],[1490955461.33,"9.730282738095237"],[1490955521.33,"9.73139880952381"],[1490955581.33,"9.722842261904763"],[1490955641.33,"9.725818452380953"],[1490955701.33,"9.72749255952381"],[1490955761.33,"9.72953869047619"],[1490955821.33,"9.731956845238095"],[1490955881.33,"9.735677083333334"],[1490955941.33,"9.738467261904763"],[1490956001.33,"9.735863095238095"],[1490956061.33,"9.743675595238095"],[1490956121.33,"9.730840773809524"],[1490956181.33,"9.734747023809524"],[1490956241.33,"9.736235119047619"],[1490956301.33,"9.736607142857142"],[1490956361.33,"9.73921130952381"],[1490956421.33,"9.742001488095237"],[1490956481.33,"9.743675595238095"],[1490956541.33,"9.744977678571429"],[1490956601.33,"9.748697916666666"],[1490956661.33,"9.760602678571429"],[1490956721.33,"9.751302083333334"],[1490956781.33,"9.754278273809524"],[1490956841.33,"9.756324404761905"],[1490956901.33,"9.758370535714286"],[1490956961.33,"9.760416666666666"],[1490957021.33,"9.763020833333334"],[1490957081.33,"9.766183035714286"],[1490957141.33,"9.764508928571429"],[1490957201.33,"9.767299107142858"],[1490957261.33,"9.768787202380953"],[1490957321.33,"9.771019345238095"],[1490957381.33,"9.773623511904763"],[1490957441.33,"9.775111607142858"],[1490957501.33,"9.779389880952381"],[1490957561.33,"9.780691964285714"],[1490957621.33,"9.788690476190476"],[1490957681.33,"9.794828869047619"],[1490957741.33,"9.779203869047619"],[1490957801.33,"9.787016369047619"],[1490957861.33,"9.783854166666666"],[1490957921.33,"9.78515625"],[1490957981.33,"9.786644345238095"],[1490958041.33,"9.787946428571429"],[1490958101.33,"9.800409226190476"],[1490958161.33,"9.787202380952381"],[1490958221.33,"9.789806547619047"],[1490958281.33,"9.791852678571429"],[1490958341.33,"9.788876488095237"],[1490958401.33,"9.78515625"],[1490958461.33,"9.7890625"],[1490958521.33,"9.791108630952381"],[1490958581.33,"9.792596726190476"],[1490958641.33,"9.794828869047619"],[1490958701.33,"9.793154761904763"],[1490958761.33,"9.799293154761905"],[1490958821.33,"9.797247023809524"],[1490958881.33,"9.794084821428571"],[1490958941.33,"9.796875"],[1490959001.33,"9.763950892857142"],[1490959061.33,"9.765997023809524"],[1490959121.33,"9.767671130952381"],[1490959181.33,"9.77046130952381"],[1490959241.33,"9.773809523809524"],[1490959301.33,"9.765252976190476"],[1490959361.33,"9.767485119047619"],[1490959421.33,"9.76953125"],[1490959481.33,"9.774553571428571"],[1490959541.33,"9.77734375"],[1490959601.33,"9.778459821428571"],[1490959661.33,"9.780877976190476"],[1490959721.33,"9.783296130952381"],[1490959781.33,"9.794828869047619"],[1490959841.33,"9.787016369047619"],[1490959901.33,"9.798735119047619"],[1490959961.33,"9.803013392857142"],[1490960021.33,"9.801525297619047"],[1490960081.33,"9.804873511904763"],[1490960141.33,"9.80078125"],[1490960201.33,"9.80375744047619"],[1490960261.33,"9.805059523809524"],[1490960321.33,"9.807849702380953"],[1490960381.33,"9.810825892857142"],[1490960441.33,"9.813058035714286"],[1490960501.33,"9.813616071428571"],[1490960561.33,"9.815104166666666"],[1490960621.33,"9.81733630952381"],[1490960681.33,"9.812872023809524"],[1490960741.33,"9.814546130952381"],[1490960801.33,"9.808035714285714"],[1490960861.33,"9.810081845238095"],[1490960921.33,"9.813058035714286"],[1490960981.33,"9.825892857142858"],[1490961041.33,"9.816964285714286"],[1490961101.33,"9.82421875"],[1490961161.33,"9.80952380952381"],[1490961221.33,"9.804315476190476"],[1490961281.33,"9.797619047619047"],[1490961341.33,"9.80078125"],[1490961401.33,"9.802827380952381"],[1490961461.33,"9.803199404761905"],[1490961521.33,"9.80952380952381"],[1490961581.33,"9.806919642857142"],[1490961641.33,"9.808779761904763"],[1490961701.33,"9.811197916666666"],[1490961761.33,"9.813244047619047"],[1490961821.33,"9.815662202380953"],[1490961881.33,"9.819940476190476"],[1490961941.33,"9.822172619047619"],[1490962001.33,"9.82328869047619"],[1490962061.33,"9.826822916666666"],[1490962121.33,"9.829241071428571"],[1490962181.33,"9.832589285714286"],[1490962241.33,"9.835565476190476"],[1490962301.33,"9.839471726190476"],[1490962361.33,"9.825520833333334"],[1490962421.33,"9.829427083333334"],[1490962481.33,"9.832217261904763"],[1490962541.33,"9.839285714285714"],[1490962601.33,"9.837611607142858"],[1490962661.33,"9.841145833333334"],[1490962721.33,"9.834077380952381"],[1490962781.33,"9.837239583333334"],[1490962841.33,"9.841703869047619"],[1490962901.33,"9.844308035714286"],[1490962961.33,"9.838727678571429"],[1490963021.33,"9.840587797619047"],[1490963081.33,"9.849516369047619"],[1490963141.33,"9.845238095238095"],[1490963201.33,"9.84375"],[1490963261.33,"9.838541666666666"],[1490963321.33,"9.841889880952381"],[1490963381.33,"9.846354166666666"],[1490963441.33,"9.832403273809524"],[1490963501.33,"9.833891369047619"],[1490963561.33,"9.808221726190476"],[1490963621.33,"9.812686011904763"],[1490963681.33,"9.814918154761905"],[1490963741.33,"9.817708333333334"],[1490963801.33,"9.80561755952381"],[1490963861.33,"9.80859375"],[1490963921.33,"9.811197916666666"],[1490963981.33,"9.802269345238095"],[1490964041.33,"9.798177083333334"],[1490964101.33,"9.80078125"],[1490964161.33,"9.815104166666666"],[1490964221.33,"9.806361607142858"]]}],"memory_current":[{"metric":{},"value":[1490964221.593,"9.806361607142858"]}],"cpu_values":[{"metric":{},"values":[[1490935421.446,"0.011520035833333402"],[1490935481.446,"0.010738020634921052"],[1490935541.446,"0.011830812658730162"],[1490935601.446,"0.011666519206349292"],[1490935661.446,"0.012397734365079505"],[1490935721.446,"0.012264678253967905"],[1490935781.446,"0.011701125396825458"],[1490935841.446,"0.011413869087301435"],[1490935901.446,"0.011355704404762157"],[1490935961.446,"0.01295611777777756"],[1490936021.446,"0.012283088253968812"],[1490936081.446,"0.011711742103174674"],[1490936141.446,"0.011066851150792879"],[1490936201.446,"0.011525933611111726"],[1490936261.446,"0.012260294246031015"],[1490936321.446,"0.011917795238095285"],[1490936381.446,"0.011402582301587626"],[1490936441.446,"0.012311798253968057"],[1490936501.446,"0.011604295476191046"],[1490936561.446,"0.012329014206349137"],[1490936621.446,"0.011401263769840977"],[1490936681.446,"0.012310593492063392"],[1490936741.446,"0.01244334305555575"],[1490936801.446,"0.01176146669320973"],[1490936861.446,"0.011186474629011792"],[1490936921.446,"0.013234800079365536"],[1490936981.446,"0.01217435722222217"],[1490937041.446,"0.011211570753967583"],[1490937101.446,"0.012066252420634934"],[1490937161.446,"0.012175381944444839"],[1490937221.446,"0.011215347936507976"],[1490937281.446,"0.012909065515873003"],[1490937341.446,"0.011718783452381023"],[1490937401.446,"0.011740557499999828"],[1490937461.446,"0.012024899960317205"],[1490937521.446,"0.011518551626984471"],[1490937581.446,"0.013295429607829826"],[1490937641.446,"0.013578758822130006"],[1490937701.446,"0.01170811908668783"],[1490937761.446,"0.011867610238095478"],[1490937821.446,"0.012601599007937034"],[1490937881.446,"0.011028959285714405"],[1490937941.446,"0.011972864523808899"],[1490938001.446,"0.012236090515873134"],[1490938061.446,"0.012468855793650629"],[1490938121.446,"0.012324049999999686"],[1490938181.446,"0.012271810317460288"],[1490938241.446,"0.013109732103174912"],[1490938301.446,"0.01201708535714284"],[1490938361.446,"0.01198280035714318"],[1490938421.446,"0.011631491547618469"],[1490938481.446,"0.012698120317460778"],[1490938541.446,"0.011908042499999686"],[1490938601.446,"0.012941332460317123"],[1490938661.446,"0.012009558055555753"],[1490938721.446,"0.011749238293651211"],[1490938781.446,"0.012597720873015857"],[1490938841.446,"0.012128174365079517"],[1490938901.446,"0.013411003452380428"],[1490938961.446,"0.012712377896825132"],[1490939021.446,"0.0126730261111118"],[1490939081.446,"0.012196438134920173"],[1490939141.446,"0.011617917341270696"],[1490939201.446,"0.012271590992062863"],[1490939261.446,"0.01196238253968261"],[1490939321.446,"0.012446522619048245"],[1490939381.446,"0.013146698134919643"],[1490939441.446,"0.013160663611111774"],[1490939501.446,"0.012921960039682278"],[1490939561.446,"0.012100972380952405"],[1490939621.446,"0.01235039095238153"],[1490939681.446,"0.013303590992062684"],[1490939741.446,"0.012064513055556225"],[1490939801.446,"0.011846763531745252"],[1490939861.446,"0.012280224007936782"],[1490939921.446,"0.012305159166666833"],[1490939981.446,"0.012107076111110887"],[1490940041.446,"0.013109447341269884"],[1490940101.446,"0.011668830198412932"],[1490940161.446,"0.011757771468254286"],[1490940221.446,"0.013607426447330252"],[1490940281.446,"0.012069082212503184"],[1490940341.446,"0.012702448174603309"],[1490940401.446,"0.012915864642857006"],[1490940461.446,"0.012882558941478554"],[1490940521.446,"0.01180430288917485"],[1490940581.446,"0.012561457142856586"],[1490940641.446,"0.013117287261905215"],[1490940701.446,"0.0119707260317455"],[1490940761.446,"0.012110876587301957"],[1490940821.446,"0.012900523174603096"],[1490940881.446,"0.012405300317460836"],[1490940941.446,"0.013397718690476127"],[1490941001.446,"0.011853019404761512"],[1490941061.446,"0.011410178968254279"],[1490941121.446,"0.01385021210317412"],[1490941181.446,"0.012158262658730703"],[1490941241.446,"0.012590782142857021"],[1490941301.446,"0.011902994444444289"],[1490941361.446,"0.012597971468253468"],[1490941421.446,"0.013460530436508394"],[1490941481.446,"0.012871132936507318"],[1490941541.446,"0.012321937023810644"],[1490941601.446,"0.012861435992063004"],[1490941661.446,"0.011904687658730493"],[1490941721.446,"0.013068603849206292"],[1490941781.446,"0.011558027420635053"],[1490941841.446,"0.011785108134920095"],[1490941901.446,"0.013018491984126938"],[1490941961.446,"0.012803318611111494"],[1490942021.446,"0.011276595873015969"],[1490942081.446,"0.012407365753968128"],[1490942141.446,"0.01261537746031769"],[1490942201.446,"0.011981626507936492"],[1490942261.446,"0.011779192579364465"],[1490942321.446,"0.012944439365080001"],[1490942381.446,"0.012563845515873258"],[1490942441.446,"0.012490993809523204"],[1490942501.446,"0.011721826547619399"],[1490942561.446,"0.012376904523809195"],[1490942621.446,"0.012627997539682608"],[1490942681.446,"0.012353236984126971"],[1490942741.446,"0.012143749162511788"],[1490942801.446,"0.01210106380777602"],[1490942861.446,"0.01323092650793727"],[1490942921.446,"0.01217811805555557"],[1490942981.446,"0.011703709655399819"],[1490943041.446,"0.01140056596399108"],[1490943101.446,"0.011589462460317477"],[1490943161.446,"0.011424534784915178"],[1490943221.446,"0.011720420858480131"],[1490943281.446,"0.011956359603174035"],[1490943341.446,"0.011627974444444375"],[1490943401.446,"0.012056417142857899"],[1490943461.446,"0.012875421865079256"],[1490943521.446,"0.011447757222222438"],[1490943581.446,"0.011686728412698438"],[1490943641.446,"0.012264428214285543"],[1490943701.446,"0.011396086150793258"],[1490943761.446,"0.012637377857143453"],[1490943821.446,"0.012229487817460189"],[1490943881.446,"0.012519327516820155"],[1490943941.446,"0.011632154440677021"],[1490944001.446,"0.0127011905614214"],[1490944061.446,"0.012041664776432408"],[1490944121.446,"0.011550796183789442"],[1490944181.446,"0.012340807579364546"],[1490944241.446,"0.012514561706348858"],[1490944301.446,"0.011591095515873378"],[1490944361.446,"0.011562522896825472"],[1490944421.446,"0.012653687499999684"],[1490944481.446,"0.012597878095237767"],[1490944541.446,"0.011373836746032411"],[1490944601.446,"0.011489111309523512"],[1490944661.446,"0.012365606547618906"],[1490944721.446,"0.011246835793650788"],[1490944781.446,"0.011556645833333596"],[1490944841.446,"0.0114839880952384"],[1490944901.446,"0.011559932103174322"],[1490944961.446,"0.011456621547618827"],[1490945021.446,"0.011137903531746323"],[1490945081.446,"0.011371503134920238"],[1490945141.446,"0.01262392527777806"],[1490945201.446,"0.011231213571428417"],[1490945261.446,"0.011834045595238011"],[1490945321.446,"0.011222574087301793"],[1490945381.446,"0.01139294579365124"],[1490945441.446,"0.011876671865079205"],[1490945501.446,"0.012003088888888104"],[1490945561.446,"0.011232171746032069"],[1490945621.446,"0.01189458067460394"],[1490945681.446,"0.011593709801586787"],[1490945741.446,"0.01179023611111146"],[1490945801.446,"0.012056340952381187"],[1490945861.446,"0.011755026706348978"],[1490945921.446,"0.011906753412698057"],[1490945981.446,"0.011362850850868408"],[1490946041.446,"0.011567284784873766"],[1490946101.446,"0.01159940924603172"],[1490946161.446,"0.01169248444646143"],[1490946221.446,"0.011294826570231075"],[1490946281.446,"0.011797972936507535"],[1490946341.446,"0.011732454126984091"],[1490946401.446,"0.011992103412699077"],[1490946461.446,"0.011787900634920185"],[1490946521.446,"0.01170581265873045"],[1490946581.446,"0.011391009603175007"],[1490946641.446,"0.01205839841269773"],[1490946701.446,"0.01188169805555573"],[1490946761.446,"0.011459351746031153"],[1490946821.446,"0.012089251071429255"],[1490946881.446,"0.011159798611111122"],[1490946941.446,"0.012261993650793439"],[1490947001.446,"0.011150941865079526"],[1490947061.446,"0.011784560238095428"],[1490947121.446,"0.01146369333333352"],[1490947181.446,"0.011946112341269969"],[1490947241.446,"0.012244168452380742"],[1490947301.446,"0.01108276087301507"],[1490947361.446,"0.011391418571428976"],[1490947421.446,"0.012042411525379642"],[1490947481.446,"0.012082919141039653"],[1490947541.446,"0.011615924682540189"],[1490947601.446,"0.01218819496031727"],[1490947661.446,"0.011292488293650517"],[1490947721.446,"0.011232974365079479"],[1490947781.446,"0.011638264880952223"],[1490947841.446,"0.0115353722619047"],[1490947901.446,"0.011426710952381045"],[1490947961.446,"0.0121381246428574"],[1490948021.446,"0.011812514087301832"],[1490948081.446,"0.012050580317459442"],[1490948141.446,"0.011855329166666742"],[1490948201.446,"0.011649919960317898"],[1490948261.446,"0.01163187396825391"],[1490948321.446,"0.011266725634920935"],[1490948381.446,"0.011934722460317146"],[1490948441.446,"0.011368148333333088"],[1490948501.446,"0.011662377698413048"],[1490948561.446,"0.011039417341269188"],[1490948621.446,"0.012176113174603589"],[1490948681.446,"0.011265313531746158"],[1490948741.446,"0.01158711781746033"],[1490948801.446,"0.011557390912698215"],[1490948861.446,"0.012131684804188454"],[1490948921.446,"0.011474324082027133"],[1490948981.446,"0.011376334484127639"],[1490949041.446,"0.011627233571428175"],[1490949101.446,"0.012499916785714077"],[1490949161.446,"0.011920621706348947"],[1490949221.446,"0.011574053410790661"],[1490949281.446,"0.011837460242165967"],[1490949341.446,"0.011227153174603937"],[1490949401.446,"0.011635896944444115"],[1490949461.446,"0.011701339047618983"],[1490949521.446,"0.011847283650793895"],[1490949581.446,"0.0116057894841271"],[1490949641.446,"0.011789695753968094"],[1490949701.446,"0.011279284841269992"],[1490949761.446,"0.011470807460317041"],[1490949821.446,"0.012172255515873568"],[1490949881.446,"0.011721892103174175"],[1490949941.446,"0.010727560317460336"],[1490950001.446,"0.011509186269841303"],[1490950061.446,"0.01188623087301566"],[1490950121.446,"0.011476948452380968"],[1490950181.446,"0.01211593166666722"],[1490950241.446,"0.011757469444444444"],[1490950301.446,"0.011519936865079109"],[1490950361.446,"0.01165834781746044"],[1490950421.446,"0.010831068928571068"],[1490950481.446,"0.011977692023809912"],[1490950541.446,"0.011828264880952136"],[1490950601.446,"0.01191921916666625"],[1490950661.446,"0.011901336547619379"],[1490950721.446,"0.011776620238095158"],[1490950781.446,"0.011911536031746153"],[1490950841.446,"0.011467936309523809"],[1490950901.446,"0.012163667023809579"],[1490950961.446,"0.0116551746825399"],[1490951021.446,"0.011799408095237739"],[1490951081.446,"0.011845631309524084"],[1490951141.446,"0.011289116626983809"],[1490951201.446,"0.012258327777777984"],[1490951261.446,"0.012265819682539036"],[1490951321.446,"0.011346034166667811"],[1490951381.446,"0.011996446111110597"],[1490951441.446,"0.011511485714285046"],[1490951501.446,"0.011980616349206635"],[1490951561.446,"0.011565376031746316"],[1490951621.446,"0.010918043373016443"],[1490951681.446,"0.011479107380951632"],[1490951741.446,"0.012467024051997748"],[1490951801.446,"0.01235313125400671"],[1490951861.446,"0.012167793061507889"],[1490951921.446,"0.01249734373015914"],[1490951981.446,"0.011414617499999877"],[1490952041.446,"0.012559693849205949"],[1490952101.446,"0.012135384801587835"],[1490952161.446,"0.01195310698412663"],[1490952221.446,"0.011996730515873409"],[1490952281.446,"0.012245181626984071"],[1490952341.446,"0.01172794166666644"],[1490952401.446,"0.012153839325397124"],[1490952461.446,"0.01287662682539674"],[1490952521.446,"0.011412833611110576"],[1490952581.446,"0.0115385753968256"],[1490952641.446,"0.011953797142857927"],[1490952701.446,"0.012210606230158325"],[1490952761.446,"0.012193429836568915"],[1490952821.446,"0.01175164000191546"],[1490952881.446,"0.011686968928571266"],[1490952941.446,"0.01204885615079335"],[1490953001.446,"0.010858237182540066"],[1490953061.446,"0.012570554523809901"],[1490953121.446,"0.011606933412697877"],[1490953181.446,"0.011895175039682713"],[1490953241.446,"0.011877423888888992"],[1490953301.446,"0.01134354857142876"],[1490953361.446,"0.011999752857142089"],[1490953421.446,"0.011927079960317739"],[1490953481.446,"0.01172722273809559"],[1490953541.446,"0.0114388174999997"],[1490953601.446,"0.012584772738095138"],[1490953661.446,"0.011858990837323214"],[1490953721.446,"0.011489406427467985"],[1490953781.446,"0.011673106071428765"],[1490953841.446,"0.012389803452380168"],[1490953901.446,"0.010877735714285755"],[1490953961.446,"0.012098601984127518"],[1490954021.446,"0.011876002539682478"],[1490954081.446,"0.0119792138492057"],[1490954141.446,"0.01116768142857198"],[1490954201.446,"0.011819058452381173"],[1490954261.446,"0.011543723055555002"],[1490954321.446,"0.011877097777778114"],[1490954381.446,"0.011255818690476465"],[1490954441.446,"0.011544411269840424"],[1490954501.446,"0.011844739246031948"],[1490954561.446,"0.012498686626984624"],[1490954621.446,"0.011012790753967753"],[1490954681.446,"0.011763483769841236"],[1490954741.446,"0.011742064880952764"],[1490954801.446,"0.011329697023809454"],[1490954861.446,"0.011616721150793869"],[1490954921.446,"0.011935843650793056"],[1490954981.446,"0.012041806150794254"],[1490955041.446,"0.011776362817460298"],[1490955101.446,"0.011507964920634838"],[1490955161.446,"0.012249892380951723"],[1490955221.446,"0.011680689451964254"],[1490955281.446,"0.011966289381797203"],[1490955341.446,"0.011113054447726804"],[1490955401.446,"0.012155607703748966"],[1490955461.446,"0.011851554722222412"],[1490955521.446,"0.011899298531746077"],[1490955581.446,"0.01202313674603201"],[1490955641.446,"0.011739823253968055"],[1490955701.446,"0.011866135595237215"],[1490955761.446,"0.012171682563083994"],[1490955821.446,"0.01125473955952014"],[1490955881.446,"0.011791852817460289"],[1490955941.446,"0.011389896547619342"],[1490956001.446,"0.011801524404761971"],[1490956061.446,"0.011788201388888577"],[1490956121.446,"0.011472721388889214"],[1490956181.446,"0.012352298174603236"],[1490956241.446,"0.011831984404761721"],[1490956301.446,"0.0114478640476188"],[1490956361.446,"0.012315896944444986"],[1490956421.446,"0.01184387992063444"],[1490956481.446,"0.0108170579365078"],[1490956541.446,"0.012441825119047971"],[1490956601.446,"0.011650502579365023"],[1490956661.446,"0.011244622936507553"],[1490956721.446,"0.01138462460317496"],[1490956781.446,"0.012361013348424437"],[1490956841.446,"0.011687763677888905"],[1490956901.446,"0.011387440952381297"],[1490956961.446,"0.012246620039682158"],[1490957021.446,"0.010769535198412467"],[1490957081.446,"0.012311013690477024"],[1490957141.446,"0.011455958968253554"],[1490957201.446,"0.012126715198413286"],[1490957261.446,"0.011078292499999627"],[1490957321.446,"0.012041933253967746"],[1490957381.446,"0.01147051317460329"],[1490957441.446,"0.01173451460317538"],[1490957501.446,"0.011660740317459825"],[1490957561.446,"0.011851131269840753"],[1490957621.446,"0.012117949444444812"],[1490957681.446,"0.011214277301587397"],[1490957741.446,"0.011935565277777841"],[1490957801.446,"0.011180848809523986"],[1490957861.446,"0.011540955039682404"],[1490957921.446,"0.011678924523809829"],[1490957981.446,"0.01175049698412655"],[1490958041.446,"0.01179233821428546"],[1490958101.446,"0.011217207341269743"],[1490958161.446,"0.011623496111110998"],[1490958221.446,"0.011751017182540137"],[1490958281.446,"0.011548055515872839"],[1490958341.446,"0.01157145297619062"],[1490958401.446,"0.011809365079364814"],[1490958461.446,"0.011367088134920926"],[1490958521.446,"0.011220626785714515"],[1490958581.446,"0.012502413531745657"],[1490958641.446,"0.011674712222222085"],[1490958701.446,"0.010840117777778147"],[1490958761.446,"0.01169669242063464"],[1490958821.446,"0.01206404448412709"],[1490958881.446,"0.011476003253967956"],[1490958941.446,"0.011927363650794281"],[1490959001.446,"0.011834540039682623"],[1490959061.446,"0.011952310396106811"],[1490959121.446,"0.011641002569963536"],[1490959181.446,"0.011215335912698408"],[1490959241.446,"0.011801235515873079"],[1490959301.446,"0.012109150079365269"],[1490959361.446,"0.011696530238095701"],[1490959421.446,"0.01188721699431308"],[1490959481.446,"0.011013023946272025"],[1490959541.446,"0.011927455988174854"],[1490959601.446,"0.011773952156046168"],[1490959661.446,"0.011311449525742057"],[1490959721.446,"0.011926485873016056"],[1490959781.446,"0.012208613174603443"],[1490959841.446,"0.011077256706349554"],[1490959901.446,"0.012141572896825473"],[1490959961.446,"0.011884196547619123"],[1490960021.446,"0.01182910611111061"],[1490960081.446,"0.011089906190476237"],[1490960141.446,"0.011485851349206303"],[1490960201.446,"0.011621675079365073"],[1490960261.446,"0.011420984246031282"],[1490960321.446,"0.011702707664224543"],[1490960381.446,"0.011122996101531552"],[1490960441.446,"0.011923133293650747"],[1490960501.446,"0.012209551587301823"],[1490960561.446,"0.011541768293650705"],[1490960621.446,"0.01133343007936486"],[1490960681.446,"0.011718844880952742"],[1490960741.446,"0.01170618126984048"],[1490960801.446,"0.01158023575396868"],[1490960861.446,"0.012154581865079351"],[1490960921.446,"0.011287024246031918"],[1490960981.446,"0.012035483412697787"],[1490961041.446,"0.01206407186508005"],[1490961101.446,"0.011742228333332922"],[1490961161.446,"0.011460450952381294"],[1490961221.446,"0.011752177539682223"],[1490961281.446,"0.012416623373015778"],[1490961341.446,"0.01134374146825419"],[1490961401.446,"0.011742214642857577"],[1490961461.446,"0.01157076337301528"],[1490961521.446,"0.011251291190475883"],[1490961581.446,"0.010835279404761772"],[1490961641.446,"0.012082314722223412"],[1490961701.446,"0.011244282817460054"],[1490961761.446,"0.012600352738094536"],[1490961821.446,"0.011595374841270692"],[1490961881.446,"0.012047435158729298"],[1490961941.446,"0.012117879285714984"],[1490962001.446,"0.011105805912698236"],[1490962061.446,"0.011228379365079935"],[1490962121.446,"0.012051188888888457"],[1490962181.446,"0.011811605198411965"],[1490962241.446,"0.011438638690477312"],[1490962301.446,"0.011535638928571016"],[1490962361.446,"0.011846252277212543"],[1490962421.446,"0.011137096779830425"],[1490962481.446,"0.011301488807399701"],[1490962541.446,"0.011706436349206364"],[1490962601.446,"0.011607870952381014"],[1490962661.446,"0.01165941666666676"],[1490962721.446,"0.011457761706349363"],[1490962781.446,"0.012004376428571304"],[1490962841.446,"0.012380191230158676"],[1490962901.446,"0.011650816111111262"],[1490962961.446,"0.011339834484126858"],[1490963021.446,"0.011815001031746352"],[1490963081.446,"0.01215702742063424"],[1490963141.446,"0.011112767612387399"],[1490963201.446,"0.011991515394890143"],[1490963261.446,"0.011573327579365182"],[1490963321.446,"0.011559778809523533"],[1490963381.446,"0.012400119444444207"],[1490963441.446,"0.011127036507936056"],[1490963501.446,"0.012095518055556944"],[1490963561.446,"0.011203742460316668"],[1490963621.446,"0.012493672738095584"],[1490963681.446,"0.012086427023809085"],[1490963741.446,"0.01073350408730215"],[1490963801.446,"0.011784052619047683"],[1490963861.446,"0.011817165277777068"],[1490963921.446,"0.01162805619047661"],[1490963981.446,"0.01141054027777739"],[1490964041.446,"0.012398790952381392"],[1490964101.446,"0.011081906428571691"],[1490964161.446,"0.012049610714285322"],[1490964221.446,"0.011764468492063805"]]}],"cpu_current":[{"metric":{},"value":[1490964221.765,"0.011764468492063801"]}]},"last_update":"2017-03-31T12:43:41.618Z"} diff --git a/spec/javascripts/boards/board_list_spec.js b/spec/javascripts/boards/board_list_spec.js new file mode 100644 index 00000000000..3f598887603 --- /dev/null +++ b/spec/javascripts/boards/board_list_spec.js @@ -0,0 +1,201 @@ +/* global BoardService */ +/* global boardsMockInterceptor */ +/* global List */ +/* global listObj */ +/* global ListIssue */ +import Vue from 'vue'; +import _ from 'underscore'; +import Sortable from 'vendor/Sortable'; +import BoardList from '~/boards/components/board_list'; +import eventHub from '~/boards/eventhub'; +import '~/boards/mixins/sortable_default_options'; +import '~/boards/models/issue'; +import '~/boards/models/list'; +import '~/boards/stores/boards_store'; +import './mock_data'; + +window.Sortable = Sortable; + +describe('Board list component', () => { + let component; + + beforeEach((done) => { + const el = document.createElement('div'); + + document.body.appendChild(el); + Vue.http.interceptors.push(boardsMockInterceptor); + gl.boardService = new BoardService('/test/issue-boards/board', '', '1'); + gl.issueBoards.BoardsStore.create(); + gl.IssueBoardsApp = new Vue(); + + const BoardListComp = Vue.extend(BoardList); + const list = new List(listObj); + const issue = new ListIssue({ + title: 'Testing', + iid: 1, + confidential: false, + labels: [], + }); + list.issuesSize = 1; + list.issues.push(issue); + + component = new BoardListComp({ + el, + propsData: { + disabled: false, + list, + issues: list.issues, + loading: false, + issueLinkBase: '/issues', + rootPath: '/', + }, + }).$mount(); + + Vue.nextTick(() => { + done(); + }); + }); + + afterEach(() => { + Vue.http.interceptors = _.without(Vue.http.interceptors, boardsMockInterceptor); + }); + + it('renders component', () => { + expect( + component.$el.classList.contains('board-list-component'), + ).toBe(true); + }); + + it('renders loading icon', (done) => { + component.loading = true; + + Vue.nextTick(() => { + expect( + component.$el.querySelector('.board-list-loading'), + ).not.toBeNull(); + + done(); + }); + }); + + it('renders issues', () => { + expect( + component.$el.querySelectorAll('.card').length, + ).toBe(1); + }); + + it('sets data attribute with issue id', () => { + expect( + component.$el.querySelector('.card').getAttribute('data-issue-id'), + ).toBe('1'); + }); + + it('shows new issue form', (done) => { + component.toggleForm(); + + Vue.nextTick(() => { + expect( + component.$el.querySelector('.board-new-issue-form'), + ).not.toBeNull(); + + expect( + component.$el.querySelector('.is-smaller'), + ).not.toBeNull(); + + done(); + }); + }); + + it('shows new issue form after eventhub event', (done) => { + eventHub.$emit(`hide-issue-form-${component.list.id}`); + + Vue.nextTick(() => { + expect( + component.$el.querySelector('.board-new-issue-form'), + ).not.toBeNull(); + + expect( + component.$el.querySelector('.is-smaller'), + ).not.toBeNull(); + + done(); + }); + }); + + it('does not show new issue form for closed list', (done) => { + component.list.type = 'closed'; + component.toggleForm(); + + Vue.nextTick(() => { + expect( + component.$el.querySelector('.board-new-issue-form'), + ).toBeNull(); + + done(); + }); + }); + + it('shows count list item', (done) => { + component.showCount = true; + + Vue.nextTick(() => { + expect( + component.$el.querySelector('.board-list-count'), + ).not.toBeNull(); + + expect( + component.$el.querySelector('.board-list-count').textContent.trim(), + ).toBe('Showing all issues'); + + done(); + }); + }); + + it('shows how many more issues to load', (done) => { + component.showCount = true; + component.list.issuesSize = 20; + + Vue.nextTick(() => { + expect( + component.$el.querySelector('.board-list-count').textContent.trim(), + ).toBe('Showing 1 of 20 issues'); + + done(); + }); + }); + + it('loads more issues after scrolling', (done) => { + spyOn(component.list, 'nextPage'); + component.$refs.list.style.height = '100px'; + component.$refs.list.style.overflow = 'scroll'; + + for (let i = 0; i < 19; i += 1) { + const issue = component.list.issues[0]; + issue.id += 1; + component.list.issues.push(issue); + } + + Vue.nextTick(() => { + component.$refs.list.scrollTop = 20000; + + setTimeout(() => { + expect(component.list.nextPage).toHaveBeenCalled(); + + done(); + }); + }); + }); + + it('shows loading more spinner', (done) => { + component.showCount = true; + component.list.loadingMore = true; + + Vue.nextTick(() => { + expect( + component.$el.querySelector('.board-list-count .fa-spinner'), + ).not.toBeNull(); + + done(); + }); + }); +}); diff --git a/spec/javascripts/environments/environment_spec.js b/spec/javascripts/environments/environment_spec.js index 9bcf617fcd8..4431baa4b96 100644 --- a/spec/javascripts/environments/environment_spec.js +++ b/spec/javascripts/environments/environment_spec.js @@ -1,7 +1,7 @@ import Vue from 'vue'; import '~/flash'; import EnvironmentsComponent from '~/environments/components/environment'; -import { environment } from './mock_data'; +import { environment, folder } from './mock_data'; describe('Environment', () => { preloadFixtures('static/environments/environments.html.raw'); @@ -179,4 +179,101 @@ describe('Environment', () => { }, 0); }); }); + + describe('expandable folders', () => { + const environmentsResponseInterceptor = (request, next) => { + next(request.respondWith(JSON.stringify({ + environments: [folder], + stopped_count: 0, + available_count: 1, + }), { + status: 200, + headers: { + 'X-nExt-pAge': '2', + 'x-page': '1', + 'X-Per-Page': '1', + 'X-Prev-Page': '', + 'X-TOTAL': '37', + 'X-Total-Pages': '2', + }, + })); + }; + + beforeEach(() => { + Vue.http.interceptors.push(environmentsResponseInterceptor); + component = new EnvironmentsComponent({ + el: document.querySelector('#environments-list-view'), + }); + }); + + afterEach(() => { + Vue.http.interceptors = _.without( + Vue.http.interceptors, environmentsResponseInterceptor, + ); + }); + + it('should open a closed folder', (done) => { + setTimeout(() => { + component.$el.querySelector('.folder-name').click(); + + Vue.nextTick(() => { + expect( + component.$el.querySelector('.folder-icon i.fa-caret-right').getAttribute('style'), + ).toContain('display: none'); + expect( + component.$el.querySelector('.folder-icon i.fa-caret-down').getAttribute('style'), + ).not.toContain('display: none'); + done(); + }); + }); + }); + + it('should close an opened folder', (done) => { + setTimeout(() => { + // open folder + component.$el.querySelector('.folder-name').click(); + + Vue.nextTick(() => { + // close folder + component.$el.querySelector('.folder-name').click(); + + Vue.nextTick(() => { + expect( + component.$el.querySelector('.folder-icon i.fa-caret-down').getAttribute('style'), + ).toContain('display: none'); + expect( + component.$el.querySelector('.folder-icon i.fa-caret-right').getAttribute('style'), + ).not.toContain('display: none'); + done(); + }); + }); + }); + }); + + it('should show children environments and a button to show all environments', (done) => { + setTimeout(() => { + // open folder + component.$el.querySelector('.folder-name').click(); + + Vue.nextTick(() => { + const folderInterceptor = (request, next) => { + next(request.respondWith(JSON.stringify({ + environments: [environment], + }), { status: 200 })); + }; + + Vue.http.interceptors.push(folderInterceptor); + + // wait for next async request + setTimeout(() => { + expect(component.$el.querySelectorAll('.js-child-row').length).toEqual(1); + expect(component.$el.querySelector('td.text-center > a.btn').textContent).toContain('Show all'); + + Vue.http.interceptors = _.without(Vue.http.interceptors, folderInterceptor); + done(); + }); + }); + }); + }); + }); }); diff --git a/spec/javascripts/environments/environments_store_spec.js b/spec/javascripts/environments/environments_store_spec.js index 115d84b50f5..f617c4bdffe 100644 --- a/spec/javascripts/environments/environments_store_spec.js +++ b/spec/javascripts/environments/environments_store_spec.js @@ -1,38 +1,106 @@ import Store from '~/environments/stores/environments_store'; import { environmentsList, serverData } from './mock_data'; -(() => { - describe('Store', () => { - let store; +describe('Store', () => { + let store; - beforeEach(() => { - store = new Store(); - }); + beforeEach(() => { + store = new Store(); + }); - it('should start with a blank state', () => { - expect(store.state.environments.length).toEqual(0); - expect(store.state.stoppedCounter).toEqual(0); - expect(store.state.availableCounter).toEqual(0); - expect(store.state.paginationInformation).toEqual({}); - }); + it('should start with a blank state', () => { + expect(store.state.environments.length).toEqual(0); + expect(store.state.stoppedCounter).toEqual(0); + expect(store.state.availableCounter).toEqual(0); + expect(store.state.paginationInformation).toEqual({}); + }); + it('should store environments', () => { + store.storeEnvironments(serverData); + expect(store.state.environments.length).toEqual(serverData.length); + expect(store.state.environments[0]).toEqual(environmentsList[0]); + }); + + it('should store available count', () => { + store.storeAvailableCount(2); + expect(store.state.availableCounter).toEqual(2); + }); + + it('should store stopped count', () => { + store.storeStoppedCount(2); + expect(store.state.stoppedCounter).toEqual(2); + }); + + describe('store environments', () => { it('should store environments', () => { store.storeEnvironments(serverData); expect(store.state.environments.length).toEqual(serverData.length); - expect(store.state.environments[0]).toEqual(environmentsList[0]); }); - it('should store available count', () => { - store.storeAvailableCount(2); - expect(store.state.availableCounter).toEqual(2); + it('should add folder keys when environment is a folder', () => { + const environment = { + name: 'bar', + size: 3, + id: 2, + }; + + store.storeEnvironments([environment]); + expect(store.state.environments[0].isFolder).toEqual(true); + expect(store.state.environments[0].folderName).toEqual('bar'); + }); + + it('should extract content of `latest` key when provided', () => { + const environment = { + name: 'bar', + size: 3, + id: 2, + latest: { + last_deployment: {}, + isStoppable: true, + }, + }; + + store.storeEnvironments([environment]); + expect(store.state.environments[0].last_deployment).toEqual({}); + expect(store.state.environments[0].isStoppable).toEqual(true); }); - it('should store stopped count', () => { - store.storeStoppedCount(2); - expect(store.state.stoppedCounter).toEqual(2); + it('should store latest.name when the environment is not a folder', () => { + store.storeEnvironments(serverData); + expect(store.state.environments[0].name).toEqual(serverData[0].latest.name); }); - it('should store pagination information', () => { + it('should store root level name when environment is a folder', () => { + store.storeEnvironments(serverData); + expect(store.state.environments[1].folderName).toEqual(serverData[1].name); + }); + }); + + describe('toggleFolder', () => { + it('should toggle folder', () => { + store.storeEnvironments(serverData); + + store.toggleFolder(store.state.environments[1]); + expect(store.state.environments[1].isOpen).toEqual(true); + + store.toggleFolder(store.state.environments[1]); + expect(store.state.environments[1].isOpen).toEqual(false); + }); + }); + + describe('setfolderContent', () => { + it('should store folder content', () => { + store.storeEnvironments(serverData); + + store.setfolderContent(store.state.environments[1], serverData); + + expect(store.state.environments[1].children.length).toEqual(serverData.length); + expect(store.state.environments[1].children[0].isChildren).toEqual(true); + }); + }); + + describe('store pagination', () => { + it('should store normalized and integer pagination information', () => { const pagination = { 'X-nExt-pAge': '2', 'X-page': '1', @@ -55,4 +123,4 @@ import { environmentsList, serverData } from './mock_data'; expect(store.state.paginationInformation).toEqual(expectedResult); }); }); -})(); +}); diff --git a/spec/javascripts/environments/mock_data.js b/spec/javascripts/environments/mock_data.js index 30861481cc5..15e11aa686b 100644 --- a/spec/javascripts/environments/mock_data.js +++ b/spec/javascripts/environments/mock_data.js @@ -84,3 +84,19 @@ export const environment = { updated_at: '2017-01-31T10:53:46.894Z', }, }; + +export const folder = { + folderName: 'build', + size: 5, + id: 12, + name: 'build/update-README', + state: 'available', + external_url: null, + environment_type: 'build', + last_deployment: null, + 'stop_action?': false, + environment_path: '/root/review-app/environments/12', + stop_path: '/root/review-app/environments/12/stop', + created_at: '2017-02-01T19:42:18.400Z', + updated_at: '2017-02-01T19:42:18.400Z', +}; diff --git a/spec/javascripts/lib/utils/number_utility_spec.js b/spec/javascripts/lib/utils/number_utility_spec.js new file mode 100644 index 00000000000..5fde8be9123 --- /dev/null +++ b/spec/javascripts/lib/utils/number_utility_spec.js @@ -0,0 +1,41 @@ +import { formatRelevantDigits } from '~/lib/utils/number_utils'; + +describe('Number Utils', () => { + describe('formatRelevantDigits', () => { + it('returns an empty string when the number is NaN', () => { + expect(formatRelevantDigits('fail')).toBe(''); + }); + + it('returns 4 decimals when there is 4 plus digits to the left', () => { + const formattedNumber = formatRelevantDigits('1000.1234567'); + const rightFromDecimal = formattedNumber.split('.')[1]; + const leftFromDecimal = formattedNumber.split('.')[0]; + expect(rightFromDecimal.length).toBe(4); + expect(leftFromDecimal.length).toBe(4); + }); + + it('returns 3 decimals when there is 1 digit to the left', () => { + const formattedNumber = formatRelevantDigits('0.1234567'); + const rightFromDecimal = formattedNumber.split('.')[1]; + const leftFromDecimal = formattedNumber.split('.')[0]; + expect(rightFromDecimal.length).toBe(3); + expect(leftFromDecimal.length).toBe(1); + }); + + it('returns 2 decimals when there is 2 digits to the left', () => { + const formattedNumber = formatRelevantDigits('10.1234567'); + const rightFromDecimal = formattedNumber.split('.')[1]; + const leftFromDecimal = formattedNumber.split('.')[0]; + expect(rightFromDecimal.length).toBe(2); + expect(leftFromDecimal.length).toBe(2); + }); + + it('returns 1 decimal when there is 3 digits to the left', () => { + const formattedNumber = formatRelevantDigits('100.1234567'); + const rightFromDecimal = formattedNumber.split('.')[1]; + const leftFromDecimal = formattedNumber.split('.')[0]; + expect(rightFromDecimal.length).toBe(1); + expect(leftFromDecimal.length).toBe(3); + }); + }); +}); diff --git a/spec/javascripts/monitoring/prometheus_graph_spec.js b/spec/javascripts/monitoring/prometheus_graph_spec.js index a3c1c5e1b7c..c2bcd9c0f7c 100644 --- a/spec/javascripts/monitoring/prometheus_graph_spec.js +++ b/spec/javascripts/monitoring/prometheus_graph_spec.js @@ -37,9 +37,11 @@ describe('PrometheusGraph', () => { it('transforms the data', () => { this.prometheusGraph.init(prometheusMockData.metrics); - expect(this.prometheusGraph.data).toBeDefined(); - expect(this.prometheusGraph.data.cpu_values.length).toBe(121); - expect(this.prometheusGraph.data.memory_values.length).toBe(121); + Object.keys(this.prometheusGraph.graphSpecificProperties, (key) => { + const graphProps = this.prometheusGraph.graphSpecificProperties[key]; + expect(graphProps.data).toBeDefined(); + expect(graphProps.data.length).toBe(121); + }); }); it('creates two graphs', () => { @@ -68,7 +70,7 @@ describe('PrometheusGraph', () => { expect($prometheusGraphContents.find('.label-y-axis-line')).toBeDefined(); expect($prometheusGraphContents.find('.label-axis-text')).toBeDefined(); expect($prometheusGraphContents.find('.rect-axis-text')).toBeDefined(); - expect($axisLabelContainer.find('rect').length).toBe(2); + expect($axisLabelContainer.find('rect').length).toBe(3); expect($axisLabelContainer.find('text').length).toBe(4); }); }); diff --git a/spec/lib/gitlab/backend/shell_spec.rb b/spec/lib/gitlab/backend/shell_spec.rb index 4b08a02ec73..6675d26734e 100644 --- a/spec/lib/gitlab/backend/shell_spec.rb +++ b/spec/lib/gitlab/backend/shell_spec.rb @@ -69,6 +69,15 @@ describe Gitlab::Shell, lib: true do expect(io).to have_received(:puts).with("key-42\tssh-rsa foo") end + it 'handles multiple spaces in the key' do + io = spy(:io) + adder = described_class.new(io) + + adder.add_key('key-42', "ssh-rsa foo") + + expect(io).to have_received(:puts).with("key-42\tssh-rsa foo") + end + it 'raises an exception if the key contains a tab' do expect do described_class.new(StringIO.new).add_key('key-42', "ssh-rsa\tfoobar") diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 002cffd3062..24654bf6afd 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -156,6 +156,8 @@ project: - external_wiki_service - kubernetes_service - mock_ci_service +- mock_deployment_service +- mock_monitoring_service - forked_project_link - forked_from_project - forked_project_links diff --git a/spec/lib/gitlab/import_export/fork_spec.rb b/spec/lib/gitlab/import_export/fork_spec.rb new file mode 100644 index 00000000000..c5ce06afd73 --- /dev/null +++ b/spec/lib/gitlab/import_export/fork_spec.rb @@ -0,0 +1,49 @@ +require 'spec_helper' + +describe 'forked project import', services: true do + let(:user) { create(:user) } + let!(:project_with_repo) { create(:project, :test_repo, name: 'test-repo-restorer', path: 'test-repo-restorer') } + let!(:project) { create(:empty_project) } + let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } + let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) } + let(:forked_from_project) { create(:project) } + let(:fork_link) { create(:forked_project_link, forked_from_project: project_with_repo) } + let(:repo_saver) { Gitlab::ImportExport::RepoSaver.new(project: project_with_repo, shared: shared) } + let(:bundle_path) { File.join(shared.export_path, Gitlab::ImportExport.project_bundle_filename) } + + let(:repo_restorer) do + Gitlab::ImportExport::RepoRestorer.new(path_to_bundle: bundle_path, shared: shared, project: project) + end + + let!(:merge_request) do + create(:merge_request, source_project: fork_link.forked_to_project, target_project: project_with_repo) + end + + let(:saver) do + Gitlab::ImportExport::ProjectTreeSaver.new(project: project_with_repo, current_user: user, shared: shared) + end + + let(:restorer) do + Gitlab::ImportExport::ProjectTreeRestorer.new(user: user, shared: shared, project: project) + end + + before do + allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) + + saver.save + repo_saver.save + + repo_restorer.restore + restorer.restore + end + + after do + FileUtils.rm_rf(export_path) + FileUtils.rm_rf(project_with_repo.repository.path_to_repo) + FileUtils.rm_rf(project.repository.path_to_repo) + end + + it 'can access the MR' do + expect(project.merge_requests.first.ensure_ref_fetched.first).to include('refs/merge-requests/1/head') + end +end diff --git a/spec/lib/gitlab/import_export/hash_util_spec.rb b/spec/lib/gitlab/import_export/hash_util_spec.rb new file mode 100644 index 00000000000..1c3a0b23ece --- /dev/null +++ b/spec/lib/gitlab/import_export/hash_util_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +describe Gitlab::ImportExport::HashUtil, lib: true do + let(:stringified_array) { [{ 'test' => 1 }] } + let(:stringified_array_with_date) { [{ 'test_date' => '2016-04-06 06:17:44 +0200' }] } + + describe '.deep_symbolize_array!' do + it 'symbolizes keys' do + expect { described_class.deep_symbolize_array!(stringified_array) }.to change { + stringified_array.first.keys.first + }.from('test').to(:test) + end + end + + describe '.deep_symbolize_array_with_date!' do + it 'symbolizes keys' do + expect { described_class.deep_symbolize_array_with_date!(stringified_array_with_date) }.to change { + stringified_array_with_date.first.keys.first + }.from('test_date').to(:test_date) + end + + it 'transforms date strings into Time objects' do + expect { described_class.deep_symbolize_array_with_date!(stringified_array_with_date) }.to change { + stringified_array_with_date.first.values.first.class + }.from(String).to(ActiveSupport::TimeWithZone) + end + end +end diff --git a/spec/lib/gitlab/import_export/merge_request_parser_spec.rb b/spec/lib/gitlab/import_export/merge_request_parser_spec.rb new file mode 100644 index 00000000000..349be4596b6 --- /dev/null +++ b/spec/lib/gitlab/import_export/merge_request_parser_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +describe Gitlab::ImportExport::MergeRequestParser do + let(:user) { create(:user) } + let!(:project) { create(:project, :test_repo, name: 'test-repo-restorer', path: 'test-repo-restorer') } + let(:forked_from_project) { create(:project) } + let(:fork_link) { create(:forked_project_link, forked_from_project: project) } + + let!(:merge_request) do + create(:merge_request, source_project: fork_link.forked_to_project, target_project: project) + end + + let(:parsed_merge_request) do + described_class.new(project, + merge_request.diff_head_sha, + merge_request, + merge_request.as_json).parse! + end + + after do + FileUtils.rm_rf(project.repository.path_to_repo) + end + + it 'has a source branch' do + expect(project.repository.branch_exists?(parsed_merge_request.source_branch)).to be true + end + + it 'has a target branch' do + expect(project.repository.branch_exists?(parsed_merge_request.target_branch)).to be true + end +end diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index c36f12dbd82..af9c25acb02 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -82,6 +82,12 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do expect(MergeRequestDiff.where.not(st_diffs: nil).count).to eq(9) end + it 'has the correct time for merge request st_commits' do + st_commits = MergeRequestDiff.where.not(st_commits: nil).first.st_commits + + expect(st_commits.first[:committed_date]).to be_kind_of(Time) + end + it 'has labels associated to label links, associated to issues' do expect(Label.first.label_links.first.target).not_to be_nil end diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb index 012c22ec5ad..d2d89e3b019 100644 --- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb @@ -79,6 +79,10 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do expect(saved_project_json['merge_requests'].first['merge_request_diff']).not_to be_empty end + it 'has merge requests diff st_diffs' do + expect(saved_project_json['merge_requests'].first['merge_request_diff']['utf8_st_diffs']).not_to be_nil + end + it 'has merge requests comments' do expect(saved_project_json['merge_requests'].first['notes']).not_to be_empty end diff --git a/spec/lib/gitlab/import_export/relation_factory_spec.rb b/spec/lib/gitlab/import_export/relation_factory_spec.rb index 57e412b0cef..fcc23a75ca1 100644 --- a/spec/lib/gitlab/import_export/relation_factory_spec.rb +++ b/spec/lib/gitlab/import_export/relation_factory_spec.rb @@ -9,7 +9,7 @@ describe Gitlab::ImportExport::RelationFactory, lib: true do relation_hash: relation_hash, members_mapper: members_mapper, user: user, - project_id: project.id) + project: project) end context 'hook object' do diff --git a/spec/lib/gitlab/import_export/repo_bundler_spec.rb b/spec/lib/gitlab/import_export/repo_saver_spec.rb index a7f4e11271e..a7f4e11271e 100644 --- a/spec/lib/gitlab/import_export/repo_bundler_spec.rb +++ b/spec/lib/gitlab/import_export/repo_saver_spec.rb diff --git a/spec/lib/gitlab/o_auth/user_spec.rb b/spec/lib/gitlab/o_auth/user_spec.rb index 6c84a4c8b73..8f09266c3b3 100644 --- a/spec/lib/gitlab/o_auth/user_spec.rb +++ b/spec/lib/gitlab/o_auth/user_spec.rb @@ -40,6 +40,15 @@ describe Gitlab::OAuth::User, lib: true do let(:provider) { 'twitter' } describe 'signup' do + it 'marks user as having password_automatically_set' do + stub_omniauth_config(allow_single_sign_on: ['twitter'], external_providers: ['twitter']) + + oauth_user.save + + expect(gl_user).to be_persisted + expect(gl_user).to be_password_automatically_set + end + shared_examples 'to verify compliance with allow_single_sign_on' do context 'provider is marked as external' do it 'marks user as external' do diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb index 9a8096208db..e0ebea63eb4 100644 --- a/spec/lib/gitlab/project_search_results_spec.rb +++ b/spec/lib/gitlab/project_search_results_spec.rb @@ -41,8 +41,10 @@ describe Gitlab::ProjectSearchResults, lib: true do subject { described_class.parse_search_result(search_result) } - it "returns a valid OpenStruct object" do - is_expected.to be_an OpenStruct + it "returns a valid FoundBlob" do + is_expected.to be_an Gitlab::SearchResults::FoundBlob + expect(subject.id).to be_nil + expect(subject.path).to eq('CHANGELOG') expect(subject.filename).to eq('CHANGELOG') expect(subject.basename).to eq('CHANGELOG') expect(subject.ref).to eq('master') @@ -53,6 +55,7 @@ describe Gitlab::ProjectSearchResults, lib: true do context "when filename has extension" do let(:search_result) { "master:CONTRIBUTE.md:5:- [Contribute to GitLab](#contribute-to-gitlab)\n" } + it { expect(subject.path).to eq('CONTRIBUTE.md') } it { expect(subject.filename).to eq('CONTRIBUTE.md') } it { expect(subject.basename).to eq('CONTRIBUTE') } end @@ -60,6 +63,7 @@ describe Gitlab::ProjectSearchResults, lib: true do context "when file under directory" do let(:search_result) { "master:a/b/c.md:5:a b c\n" } + it { expect(subject.path).to eq('a/b/c.md') } it { expect(subject.filename).to eq('a/b/c.md') } it { expect(subject.basename).to eq('a/b/c') } end diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb index 3bd2a3238fe..9fbcb1fee69 100644 --- a/spec/lib/gitlab/workhorse_spec.rb +++ b/spec/lib/gitlab/workhorse_spec.rb @@ -188,8 +188,10 @@ describe Gitlab::Workhorse, lib: true do context 'when Gitaly is enabled' do let(:gitaly_params) do + address = Gitlab::GitalyClient.get_address('default') { - GitalySocketPath: URI(Gitlab::GitalyClient.get_address('default')).path, + GitalySocketPath: URI(address).path, + GitalyAddress: address, } end @@ -207,31 +209,33 @@ describe Gitlab::Workhorse, lib: true do expect(subject).to include(repo_param) end - { - git_receive_pack: :post_receive_pack, - git_upload_pack: :post_upload_pack - }.each do |action_name, feature_flag| - context "when #{action_name} action is passed" do - let(:action) { action_name } + context "when git_upload_pack action is passed" do + let(:action) { 'git_upload_pack' } + let(:feature_flag) { :post_upload_pack } - context 'when action is enabled by feature flag' do - it 'includes Gitaly params in the returned value' do - allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(feature_flag).and_return(true) + context 'when action is enabled by feature flag' do + it 'includes Gitaly params in the returned value' do + allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(feature_flag).and_return(true) - expect(subject).to include(gitaly_params) - end + expect(subject).to include(gitaly_params) end + end - context 'when action is not enabled by feature flag' do - it 'does not include Gitaly params in the returned value' do - allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(feature_flag).and_return(false) + context 'when action is not enabled by feature flag' do + it 'does not include Gitaly params in the returned value' do + allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(feature_flag).and_return(false) - expect(subject).not_to include(gitaly_params) - end + expect(subject).not_to include(gitaly_params) end end end + context "when git_receive_pack action is passed" do + let(:action) { 'git_receive_pack' } + + it { expect(subject).not_to include(gitaly_params) } + end + context "when info_refs action is passed" do let(:action) { 'info_refs' } diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index b8584301baa..4bdd46a581d 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -51,14 +51,6 @@ describe Issue, models: true do expect(issue.closed_at).to eq(now) end - - it 'sets closed_at to nil when issue is reopened' do - issue = create(:issue, state: 'closed') - - issue.reopen - - expect(issue.closed_at).to be_nil - end end describe '#to_reference' do diff --git a/spec/models/project_services/kubernetes_service_spec.rb b/spec/models/project_services/kubernetes_service_spec.rb index bf7950ef1c9..e69eb0098dd 100644 --- a/spec/models/project_services/kubernetes_service_spec.rb +++ b/spec/models/project_services/kubernetes_service_spec.rb @@ -4,7 +4,7 @@ describe KubernetesService, models: true, caching: true do include KubernetesHelpers include ReactiveCachingHelpers - let(:project) { create(:kubernetes_project) } + let(:project) { build_stubbed(:kubernetes_project) } let(:service) { project.kubernetes_service } # We use Kubeclient to interactive with the Kubernetes API. It will @@ -32,7 +32,8 @@ describe KubernetesService, models: true, caching: true do describe 'Validations' do context 'when service is active' do before { subject.active = true } - it { is_expected.to validate_presence_of(:namespace) } + + it { is_expected.not_to validate_presence_of(:namespace) } it { is_expected.to validate_presence_of(:api_url) } it { is_expected.to validate_presence_of(:token) } @@ -55,7 +56,7 @@ describe KubernetesService, models: true, caching: true do 'a.b' => false, 'a*b' => false, }.each do |namespace, validity| - it "should validate #{namespace} as #{validity ? 'valid' : 'invalid'}" do + it "validates #{namespace} as #{validity ? 'valid' : 'invalid'}" do subject.namespace = namespace expect(subject.valid?).to eq(validity) @@ -66,24 +67,40 @@ describe KubernetesService, models: true, caching: true do context 'when service is inactive' do before { subject.active = false } - it { is_expected.not_to validate_presence_of(:namespace) } + it { is_expected.not_to validate_presence_of(:api_url) } it { is_expected.not_to validate_presence_of(:token) } end end describe '#initialize_properties' do - context 'with a project' do - let(:namespace_name) { "#{project.path}-#{project.id}" } + context 'without a project' do + it 'leaves the namespace unset' do + expect(described_class.new.namespace).to be_nil + end + end + end + + describe '#fields' do + let(:kube_namespace) do + subject.fields.find { |h| h[:name] == 'namespace' } + end + + context 'as template' do + before { subject.template = true } - it 'defaults to the project name with ID' do - expect(described_class.new(project: project).namespace).to eq(namespace_name) + it 'sets the namespace to the default' do + expect(kube_namespace).not_to be_nil + expect(kube_namespace[:placeholder]).to eq(subject.class::TEMPLATE_PLACEHOLDER) end end - context 'without a project' do - it 'leaves the namespace unset' do - expect(described_class.new.namespace).to be_nil + context 'with associated project' do + before { subject.project = project } + + it 'sets the namespace to the default' do + expect(kube_namespace).not_to be_nil + expect(kube_namespace[:placeholder]).to match(/\A#{Gitlab::Regex::PATH_REGEX_STR}-\d+\z/) end end end @@ -138,38 +155,40 @@ describe KubernetesService, models: true, caching: true do before do subject.api_url = 'https://kube.domain.com' subject.token = 'token' - subject.namespace = 'my-project' subject.ca_pem = 'CA PEM DATA' + subject.project = project end - it 'sets KUBE_URL' do - expect(subject.predefined_variables).to include( - { key: 'KUBE_URL', value: 'https://kube.domain.com', public: true } - ) - end + context 'namespace is provided' do + before { subject.namespace = 'my-project' } - it 'sets KUBE_TOKEN' do - expect(subject.predefined_variables).to include( - { key: 'KUBE_TOKEN', value: 'token', public: false } - ) + it 'sets the variables' do + expect(subject.predefined_variables).to include( + { key: 'KUBE_URL', value: 'https://kube.domain.com', public: true }, + { key: 'KUBE_TOKEN', value: 'token', public: false }, + { key: 'KUBE_NAMESPACE', value: 'my-project', public: true }, + { key: 'KUBE_CA_PEM', value: 'CA PEM DATA', public: true }, + { key: 'KUBE_CA_PEM_FILE', value: 'CA PEM DATA', public: true, file: true }, + ) + end end - it 'sets KUBE_NAMESPACE' do - expect(subject.predefined_variables).to include( - { key: 'KUBE_NAMESPACE', value: 'my-project', public: true } - ) - end + context 'no namespace provided' do + it 'sets the variables' do + expect(subject.predefined_variables).to include( + { key: 'KUBE_URL', value: 'https://kube.domain.com', public: true }, + { key: 'KUBE_TOKEN', value: 'token', public: false }, + { key: 'KUBE_CA_PEM', value: 'CA PEM DATA', public: true }, + { key: 'KUBE_CA_PEM_FILE', value: 'CA PEM DATA', public: true, file: true }, + ) + end - it 'sets KUBE_CA_PEM' do - expect(subject.predefined_variables).to include( - { key: 'KUBE_CA_PEM', value: 'CA PEM DATA', public: true } - ) - end + it 'sets the KUBE_NAMESPACE' do + kube_namespace = subject.predefined_variables.find { |h| h[:key] == 'KUBE_NAMESPACE' } - it 'sets KUBE_CA_PEM_FILE' do - expect(subject.predefined_variables).to include( - { key: 'KUBE_CA_PEM_FILE', value: 'CA PEM DATA', public: true, file: true } - ) + expect(kube_namespace).not_to be_nil + expect(kube_namespace[:value]).to match(/\A#{Gitlab::Regex::PATH_REGEX_STR}-\d+\z/) + end end end diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb index 7fb728fed6f..598968aff70 100644 --- a/spec/requests/api/milestones_spec.rb +++ b/spec/requests/api/milestones_spec.rb @@ -306,6 +306,8 @@ describe API::Milestones, api: true do end it 'returns project merge_requests for a particular milestone' do + # eager-load another_merge_request + another_merge_request get api("/projects/#{project.id}/milestones/#{milestone.id}/merge_requests", user) expect(response).to have_http_status(200) diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb index 044b989e5ba..1cfac7353d4 100644 --- a/spec/requests/api/runner_spec.rb +++ b/spec/requests/api/runner_spec.rb @@ -461,6 +461,29 @@ describe API::Runner do end end + context 'when dependencies is an empty array' do + let!(:job) { create(:ci_build_tag, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) } + let!(:job2) { create(:ci_build_tag, pipeline: pipeline, name: 'rubocop', stage: 'test', stage_idx: 0) } + let!(:empty_dependencies_job) do + create(:ci_build, pipeline: pipeline, token: 'test-job-token', name: 'empty_dependencies_job', + stage: 'deploy', stage_idx: 1, + options: { dependencies: [] }) + end + + before do + job.success + job2.success + end + + it 'returns an empty array' do + request_job + + expect(response).to have_http_status(201) + expect(json_response['id']).to eq(empty_dependencies_job.id) + expect(json_response['dependencies'].count).to eq(0) + end + end + context 'when job has no tags' do before { job.update(tags: []) } diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index 90cde705b85..5ec1ed8237b 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -221,26 +221,23 @@ describe SystemNoteService, services: true do describe '.change_status' do subject { described_class.change_status(noteable, project, author, status, source) } - let(:status) { 'new_status' } - let(:source) { nil } + context 'with status reopened' do + let(:status) { 'reopened' } + let(:source) { nil } - it_behaves_like 'a system note' do - let(:action) { 'status' } + it_behaves_like 'a system note' do + let(:action) { 'opened' } + end end context 'with a source' do + let(:status) { 'opened' } let(:source) { double('commit', gfm_reference: 'commit 123456') } it 'sets the note text' do expect(subject.note).to eq "#{status} via commit 123456" end end - - context 'without a source' do - it 'sets the note text' do - expect(subject.note).to eq status - end - end end describe '.merge_when_pipeline_succeeds' do @@ -298,9 +295,23 @@ describe SystemNoteService, services: true do describe '.change_issue_confidentiality' do subject { described_class.change_issue_confidentiality(noteable, project, author) } - context 'when noteable responds to `confidential`' do + context 'issue has been made confidential' do + before do + noteable.update_attribute(:confidential, true) + end + + it_behaves_like 'a system note' do + let(:action) { 'confidential' } + end + + it 'sets the note text' do + expect(subject.note).to eq 'made the issue confidential' + end + end + + context 'issue has been made visible' do it_behaves_like 'a system note' do - let(:action) { 'confidentiality' } + let(:action) { 'visible' } end it 'sets the note text' do diff --git a/spec/services/users/create_service_spec.rb b/spec/services/users/create_service_spec.rb index 66f68650f81..a111aec2f89 100644 --- a/spec/services/users/create_service_spec.rb +++ b/spec/services/users/create_service_spec.rb @@ -122,6 +122,32 @@ describe Users::CreateService, services: true do end end + context 'when password_automatically_set parameter is true' do + let(:params) do + { + name: 'John Doe', + username: 'jduser', + email: 'jd@example.com', + password: 'mydummypass', + password_automatically_set: true + } + end + + it 'persists the given attributes' do + user = service.execute + user.reload + + expect(user).to have_attributes( + name: params[:name], + username: params[:username], + email: params[:email], + password: params[:password], + created_by_id: admin_user.id, + password_automatically_set: params[:password_automatically_set] + ) + end + end + context 'when skip_confirmation parameter is true' do let(:params) do { name: 'John Doe', username: 'jduser', email: 'jd@example.com', password: 'mydummypass', skip_confirmation: true } diff --git a/spec/support/controllers/githubish_import_controller_shared_examples.rb b/spec/support/controllers/githubish_import_controller_shared_examples.rb index 51f1015f43c..c59b30c772d 100644 --- a/spec/support/controllers/githubish_import_controller_shared_examples.rb +++ b/spec/support/controllers/githubish_import_controller_shared_examples.rb @@ -180,7 +180,7 @@ shared_examples 'a GitHub-ish import controller: POST create' do it "takes the new namespace" do expect(Gitlab::GithubImport::ProjectCreator). to receive(:new).with(provider_repo, provider_repo.name, an_instance_of(Group), user, access_params, type: provider). - and_return(double(execute: true)) + and_return(double(execute: true)) post :create, target_namespace: provider_repo.name, format: :js end @@ -201,7 +201,7 @@ shared_examples 'a GitHub-ish import controller: POST create' do it "takes the current user's namespace" do expect(Gitlab::GithubImport::ProjectCreator). to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider). - and_return(double(execute: true)) + and_return(double(execute: true)) post :create, format: :js end @@ -229,7 +229,7 @@ shared_examples 'a GitHub-ish import controller: POST create' do end end - context 'user has chosen a nested namespace and name for the project' do + context 'user has chosen an existing nested namespace and name for the project' do let(:parent_namespace) { create(:namespace, name: 'foo', owner: user) } let(:nested_namespace) { create(:namespace, name: 'bar', parent: parent_namespace, owner: user) } let(:test_name) { 'test_name' } @@ -242,5 +242,58 @@ shared_examples 'a GitHub-ish import controller: POST create' do post :create, { target_namespace: nested_namespace.full_path, new_name: test_name, format: :js } end end + + context 'user has chosen a non-existent nested namespaces and name for the project' do + let(:test_name) { 'test_name' } + + it 'takes the selected namespace and name' do + expect(Gitlab::GithubImport::ProjectCreator). + to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider). + and_return(double(execute: true)) + + post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js } + end + + it 'creates the namespaces' do + allow(Gitlab::GithubImport::ProjectCreator). + to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider). + and_return(double(execute: true)) + + expect { post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js } } + .to change { Namespace.count }.by(2) + end + + it 'new namespace has the right parent' do + allow(Gitlab::GithubImport::ProjectCreator). + to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider). + and_return(double(execute: true)) + + post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js } + + expect(Namespace.find_by_path_or_name('bar').parent.path).to eq('foo') + end + end + + context 'user has chosen existent and non-existent nested namespaces and name for the project' do + let(:test_name) { 'test_name' } + let!(:parent_namespace) { create(:namespace, name: 'foo', owner: user) } + + it 'takes the selected namespace and name' do + expect(Gitlab::GithubImport::ProjectCreator). + to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider). + and_return(double(execute: true)) + + post :create, { target_namespace: 'foo/foobar/bar', new_name: test_name, format: :js } + end + + it 'creates the namespaces' do + allow(Gitlab::GithubImport::ProjectCreator). + to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider). + and_return(double(execute: true)) + + expect { post :create, { target_namespace: 'foo/foobar/bar', new_name: test_name, format: :js } } + .to change { Namespace.count }.by(2) + end + end end end diff --git a/spec/support/drag_to_helper.rb b/spec/support/drag_to_helper.rb index 0c0659d3ecd..ae149631ed9 100644 --- a/spec/support/drag_to_helper.rb +++ b/spec/support/drag_to_helper.rb @@ -3,11 +3,11 @@ module DragTo evaluate_script("simulateDrag({scrollable: $('#{scrollable}').get(0), from: {el: $('#{selector}').eq(#{list_from_index}).get(0), index: #{from_index}}, to: {el: $('#{selector}').eq(#{list_to_index}).get(0), index: #{to_index}}});") Timeout.timeout(Capybara.default_max_wait_time) do - loop until drag_active? + loop while drag_active? end end def drag_active? - page.evaluate_script('window.SIMULATE_DRAG_ACTIVE').zero? + page.evaluate_script('window.SIMULATE_DRAG_ACTIVE').nonzero? end end diff --git a/spec/support/issuables_list_metadata_shared_examples.rb b/spec/support/issuables_list_metadata_shared_examples.rb index 7ea4073ef2b..3406e4c3161 100644 --- a/spec/support/issuables_list_metadata_shared_examples.rb +++ b/spec/support/issuables_list_metadata_shared_examples.rb @@ -33,4 +33,19 @@ shared_examples 'issuables list meta-data' do |issuable_type, action = nil| expect(meta_data[id].upvotes).to eq(id + 2) end end + + describe "when given empty collection" do + let(:project2) { create(:empty_project, :public) } + + it "doesn't execute any queries with false conditions" do + get_action = + if action + proc { get action } + else + proc { get :index, namespace_id: project2.namespace, project_id: project2 } + end + + expect(&get_action).not_to make_queries_matching(/WHERE (?:1=0|0=1)/) + end + end end diff --git a/spec/support/matchers/query_matcher.rb b/spec/support/matchers/query_matcher.rb new file mode 100644 index 00000000000..ac8c4ab91d9 --- /dev/null +++ b/spec/support/matchers/query_matcher.rb @@ -0,0 +1,33 @@ +RSpec::Matchers.define :make_queries_matching do |matcher, expected_count = nil| + supports_block_expectations + + match do |block| + @counter = query_count(matcher, &block) + if expected_count + @counter.count == expected_count + else + @counter.count > 0 + end + end + + failure_message_when_negated do |_| + if expected_count + "expected #{matcher} not to match #{expected_count} queries, got #{@counter.count} matches:\n\n#{@counter.inspect}" + else + "expected #{matcher} not to match any query, got #{@counter.count} matches:\n\n#{@counter.inspect}" + end + end + + failure_message do |_| + if expected_count + "expected #{matcher} to match #{expected_count} queries, got #{@counter.count} matches:\n\n#{@counter.inspect}" + else + "expected #{matcher} to match at least one query, got #{@counter.count} matches:\n\n#{@counter.inspect}" + end + end + + def query_count(regex, &block) + @recorder = ActiveRecord::QueryRecorder.new(&block).log + @recorder.select{ |q| q.match(regex) } + end +end diff --git a/spec/workers/process_commit_worker_spec.rb b/spec/workers/process_commit_worker_spec.rb index 1c383d0514d..9afe2e610b9 100644 --- a/spec/workers/process_commit_worker_spec.rb +++ b/spec/workers/process_commit_worker_spec.rb @@ -99,6 +99,13 @@ describe ProcessCommitWorker do expect(metric.first_mentioned_in_commit_at).to eq(commit.committed_date) end + + it "doesn't execute any queries with false conditions" do + allow(commit).to receive(:safe_message). + and_return("Lorem Ipsum") + + expect { worker.update_issue_metrics(commit, user) }.not_to make_queries_matching(/WHERE (?:1=0|0=1)/) + end end describe '#build_commit' do |