diff options
56 files changed, 1259 insertions, 198 deletions
diff --git a/app/assets/javascripts/blob/sketch/index.js b/app/assets/javascripts/blob/sketch/index.js new file mode 100644 index 00000000000..0799991aa40 --- /dev/null +++ b/app/assets/javascripts/blob/sketch/index.js @@ -0,0 +1,73 @@ +import JSZip from 'jszip'; +import JSZipUtils from 'jszip-utils'; + +export default class SketchLoader { + constructor(container) { + this.container = container; + this.loadingIcon = this.container.querySelector('.js-loading-icon'); + + this.load(); + } + + load() { + return this.getZipFile() + .then(data => JSZip.loadAsync(data)) + .then(asyncResult => asyncResult.files['previews/preview.png'].async('uint8array')) + .then((content) => { + const url = window.URL || window.webkitURL; + const blob = new Blob([new Uint8Array(content)], { + type: 'image/png', + }); + const previewUrl = url.createObjectURL(blob); + + this.render(previewUrl); + }) + .catch(this.error.bind(this)); + } + + getZipFile() { + return new JSZip.external.Promise((resolve, reject) => { + JSZipUtils.getBinaryContent(this.container.dataset.endpoint, (err, data) => { + if (err) { + reject(err); + } else { + resolve(data); + } + }); + }); + } + + render(previewUrl) { + const previewLink = document.createElement('a'); + const previewImage = document.createElement('img'); + + previewLink.href = previewUrl; + previewLink.target = '_blank'; + previewImage.src = previewUrl; + previewImage.className = 'img-responsive'; + + previewLink.appendChild(previewImage); + this.container.appendChild(previewLink); + + this.removeLoadingIcon(); + } + + error() { + const errorMsg = document.createElement('p'); + + errorMsg.className = 'prepend-top-default append-bottom-default text-center'; + errorMsg.textContent = ` + Cannot show preview. For previews on sketch files, they must have the file format + introduced by Sketch version 43 and above. + `; + this.container.appendChild(errorMsg); + + this.removeLoadingIcon(); + } + + removeLoadingIcon() { + if (this.loadingIcon) { + this.loadingIcon.remove(); + } + } +} diff --git a/app/assets/javascripts/blob/sketch_viewer.js b/app/assets/javascripts/blob/sketch_viewer.js new file mode 100644 index 00000000000..0640dd26855 --- /dev/null +++ b/app/assets/javascripts/blob/sketch_viewer.js @@ -0,0 +1,8 @@ +/* eslint-disable no-new */ +import SketchLoader from './sketch'; + +document.addEventListener('DOMContentLoaded', () => { + const el = document.getElementById('js-sketch-viewer'); + + new SketchLoader(el); +}); 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/groups_select.js b/app/assets/javascripts/groups_select.js index 602a3b78189..10363c16bae 100644 --- a/app/assets/javascripts/groups_select.js +++ b/app/assets/javascripts/groups_select.js @@ -45,14 +45,14 @@ window.GroupsSelect = (function() { page, per_page: GroupsSelect.PER_PAGE, all_available, - skip_groups, }; }, results: function (data, page) { if (data.length) return { results: [] }; - const results = data.length ? data : data.results || []; + const groups = data.length ? data : data.results || []; const more = data.pagination ? data.pagination.more : false; + const results = groups.filter(group => skip_groups.indexOf(group.id) === -1); return { results, diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index 9a4129cdc8d..52425262925 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -292,6 +292,10 @@ } @media(min-width: $screen-xs-max) { + &.merge-requests .text-content { + margin-top: 40px; + } + &.labels .text-content { margin-top: 70px; } 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/models/blob.rb b/app/models/blob.rb index 95d2111a992..1c451a3976f 100644 --- a/app/models/blob.rb +++ b/app/models/blob.rb @@ -50,6 +50,10 @@ class Blob < SimpleDelegator text? && language&.name == 'Jupyter Notebook' end + def sketch? + binary? && extname.downcase.delete('.') == 'sketch' + end + def size_within_svg_limits? size <= MAXIMUM_SVG_SIZE end @@ -69,6 +73,8 @@ class Blob < SimpleDelegator 'image' elsif ipython_notebook? 'notebook' + elsif sketch? + 'sketch' elsif text? 'text' else 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/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/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/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/projects/blob/_sketch.html.haml b/app/views/projects/blob/_sketch.html.haml new file mode 100644 index 00000000000..dad9369cb2a --- /dev/null +++ b/app/views/projects/blob/_sketch.html.haml @@ -0,0 +1,7 @@ +- content_for :page_specific_javascripts do + = page_specific_javascript_bundle_tag('common_vue') + = page_specific_javascript_bundle_tag('sketch_viewer') + +.file-content#js-sketch-viewer{ data: { endpoint: namespace_project_raw_path(@project.namespace, @project, @id) } } + .js-loading-icon.text-center.prepend-top-default.append-bottom-default.js-loading-icon{ 'aria-label' => 'Loading Sketch preview' } + = icon('spinner spin 2x', 'aria-hidden' => 'true'); diff --git a/app/views/projects/boards/_show.html.haml b/app/views/projects/boards/_show.html.haml index d93eac63bd3..7ca0ec8ed2b 100644 --- a/app/views/projects/boards/_show.html.haml +++ b/app/views/projects/boards/_show.html.haml @@ -8,7 +8,6 @@ = page_specific_javascript_bundle_tag('boards') %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/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/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/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/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/fix-groups-long-url.yml b/changelogs/unreleased/fix-groups-long-url.yml new file mode 100644 index 00000000000..f0f1296ad40 --- /dev/null +++ b/changelogs/unreleased/fix-groups-long-url.yml @@ -0,0 +1,4 @@ +--- +title: Skip groups validation on the client +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/config/dependency_decisions.yml b/config/dependency_decisions.yml index 072ed8a3864..fdba1f6541e 100644 --- a/config/dependency_decisions.yml +++ b/config/dependency_decisions.yml @@ -326,3 +326,21 @@ :why: https://github.com/domenic/opener/blob/1.4.3/LICENSE.txt :versions: [] :when: 2017-02-21 22:33:41.729629000 Z +- - :approve + - jszip + - :who: Phil Hughes + :why: https://github.com/Stuk/jszip/blob/master/LICENSE.markdown + :versions: [] + :when: 2017-04-05 10:38:46.275721000 Z +- - :approve + - jszip-utils + - :who: Phil Hughes + :why: https://github.com/Stuk/jszip-utils/blob/master/LICENSE.markdown + :versions: [] + :when: 2017-04-05 10:39:32.676232000 Z +- - :approve + - pako + - :who: Phil Hughes + :why: https://github.com/nodeca/pako/blob/master/LICENSE + :versions: [] + :when: 2017-04-05 10:43:45.897720000 Z diff --git a/config/webpack.config.js b/config/webpack.config.js index 36c09c14d56..99596ce2197 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -37,6 +37,7 @@ var config = { monitoring: './monitoring/monitoring_bundle.js', network: './network/network_bundle.js', notebook_viewer: './blob/notebook_viewer.js', + sketch_viewer: './blob/sketch_viewer.js', profile: './profile/profile_bundle.js', protected_branches: './protected_branches/protected_branches_bundle.js', snippet: './snippet/snippet_bundle.js', 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/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/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/package.json b/package.json index 7b6c4556e2c..2c8c4e126cd 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,8 @@ "jquery": "^2.2.1", "jquery-ujs": "^1.2.1", "js-cookie": "^2.1.3", + "jszip": "^3.1.3", + "jszip-utils": "^0.0.2", "mousetrap": "^1.4.6", "pikaday": "^1.5.1", "raphael": "^2.2.7", 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/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/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/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/search_spec.rb b/spec/features/search_spec.rb index 40ef4c098b9..e8ad28a00f0 100644 --- a/spec/features/search_spec.rb +++ b/spec/features/search_spec.rb @@ -164,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/blob/sketch/index_spec.js b/spec/javascripts/blob/sketch/index_spec.js new file mode 100644 index 00000000000..0e4431548c4 --- /dev/null +++ b/spec/javascripts/blob/sketch/index_spec.js @@ -0,0 +1,118 @@ +/* eslint-disable no-new */ +import JSZip from 'jszip'; +import SketchLoader from '~/blob/sketch'; + +describe('Sketch viewer', () => { + const generateZipFileArrayBuffer = (zipFile, resolve, done) => { + zipFile + .generateAsync({ type: 'arrayBuffer' }) + .then((content) => { + resolve(content); + + setTimeout(() => { + done(); + }, 100); + }); + }; + + preloadFixtures('static/sketch_viewer.html.raw'); + + beforeEach(() => { + loadFixtures('static/sketch_viewer.html.raw'); + }); + + describe('with error message', () => { + beforeEach((done) => { + spyOn(SketchLoader.prototype, 'getZipFile').and.callFake(() => new Promise((resolve, reject) => { + reject(); + + setTimeout(() => { + done(); + }); + })); + + new SketchLoader(document.getElementById('js-sketch-viewer')); + }); + + it('renders error message', () => { + expect( + document.querySelector('#js-sketch-viewer p'), + ).not.toBeNull(); + + expect( + document.querySelector('#js-sketch-viewer p').textContent.trim(), + ).toContain('Cannot show preview.'); + }); + + it('removes render the loading icon', () => { + expect( + document.querySelector('.js-loading-icon'), + ).toBeNull(); + }); + }); + + describe('success', () => { + beforeEach((done) => { + spyOn(SketchLoader.prototype, 'getZipFile').and.callFake(() => new Promise((resolve) => { + const zipFile = new JSZip(); + zipFile.folder('previews') + .file('preview.png', 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAMAAAAoyzS7AAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAA1JREFUeNoBAgD9/wAAAAIAAVMrnDAAAAAASUVORK5CYII=', { + base64: true, + }); + + generateZipFileArrayBuffer(zipFile, resolve, done); + })); + + new SketchLoader(document.getElementById('js-sketch-viewer')); + }); + + it('does not render error message', () => { + expect( + document.querySelector('#js-sketch-viewer p'), + ).toBeNull(); + }); + + it('removes render the loading icon', () => { + expect( + document.querySelector('.js-loading-icon'), + ).toBeNull(); + }); + + it('renders preview img', () => { + const img = document.querySelector('#js-sketch-viewer img'); + + expect(img).not.toBeNull(); + expect(img.classList.contains('img-responsive')).toBeTruthy(); + }); + + it('renders link to image', () => { + const img = document.querySelector('#js-sketch-viewer img'); + const link = document.querySelector('#js-sketch-viewer a'); + + expect(link.href).toBe(img.src); + expect(link.target).toBe('_blank'); + }); + }); + + describe('incorrect file', () => { + beforeEach((done) => { + spyOn(SketchLoader.prototype, 'getZipFile').and.callFake(() => new Promise((resolve) => { + const zipFile = new JSZip(); + + generateZipFileArrayBuffer(zipFile, resolve, done); + })); + + new SketchLoader(document.getElementById('js-sketch-viewer')); + }); + + it('renders error message', () => { + expect( + document.querySelector('#js-sketch-viewer p'), + ).not.toBeNull(); + + expect( + document.querySelector('#js-sketch-viewer p').textContent.trim(), + ).toContain('Cannot show preview.'); + }); + }); +}); 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/fixtures/sketch_viewer.html.haml b/spec/javascripts/fixtures/sketch_viewer.html.haml new file mode 100644 index 00000000000..f01bd00925a --- /dev/null +++ b/spec/javascripts/fixtures/sketch_viewer.html.haml @@ -0,0 +1,2 @@ +.file-content#js-sketch-viewer{ data: { endpoint: '/test_sketch_file.sketch' } } + .js-loading-icon 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/models/blob_spec.rb b/spec/models/blob_spec.rb index 552229e9b07..ba110383977 100644 --- a/spec/models/blob_spec.rb +++ b/spec/models/blob_spec.rb @@ -67,6 +67,20 @@ describe Blob do end end + describe '#sketch?' do + it 'is falsey with image extension' do + git_blob = Gitlab::Git::Blob.new(name: "design.png") + + expect(described_class.decorate(git_blob)).not_to be_sketch + end + + it 'is truthy with sketch extension' do + git_blob = Gitlab::Git::Blob.new(name: "design.sketch") + + expect(described_class.decorate(git_blob)).to be_sketch + end + end + describe '#video?' do it 'is falsey with image extension' do git_blob = Gitlab::Git::Blob.new(name: 'image.png') @@ -92,7 +106,8 @@ describe Blob do language: nil, lfs_pointer?: false, svg?: false, - text?: false + text?: false, + binary?: false ) described_class.decorate(double).tap do |blob| @@ -135,6 +150,11 @@ describe Blob do blob = stubbed_blob(text?: true, ipython_notebook?: true) expect(blob.to_partial_path(project)).to eq 'notebook' end + + it 'handles Sketch files' do + blob = stubbed_blob(text?: true, sketch?: true, binary?: true) + expect(blob.to_partial_path(project)).to eq 'sketch' + end end describe '#size_within_svg_limits?' do 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/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/yarn.lock b/yarn.lock index f254668646c..48b847b84d0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1245,6 +1245,10 @@ core-js@^2.2.0, core-js@^2.4.0, core-js@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e" +core-js@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.3.0.tgz#fab83fbb0b2d8dc85fa636c4b9d34c75420c6d65" + core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -1581,6 +1585,10 @@ es6-map@^0.1.3: es6-symbol "~3.1.0" event-emitter "~0.3.4" +es6-promise@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.0.2.tgz#010d5858423a5f118979665f46486a95c6ee2bb6" + es6-promise@~4.0.3: version "4.0.5" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.0.5.tgz#7882f30adde5b240ccfa7f7d78c548330951ae42" @@ -2335,6 +2343,10 @@ ignore@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.2.2.tgz#1c51e1ef53bab6ddc15db4d9ac4ec139eceb3410" +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -2764,6 +2776,20 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.3.6" +jszip-utils@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/jszip-utils/-/jszip-utils-0.0.2.tgz#457d5cbca60a1c2e0706e9da2b544e8e7bc50bf8" + +jszip@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.1.3.tgz#8a920403b2b1651c0fc126be90192d9080957c37" + dependencies: + core-js "~2.3.0" + es6-promise "~3.0.2" + lie "~3.1.0" + pako "~1.0.2" + readable-stream "~2.0.6" + karma-coverage-istanbul-reporter@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-0.2.0.tgz#5766263338adeb0026f7e4ac7a89a5f056c5642c" @@ -2868,6 +2894,12 @@ levn@^0.3.0, levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" +lie@~3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e" + dependencies: + immediate "~3.0.5" + load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" @@ -3349,6 +3381,10 @@ pako@~0.2.0: version "0.2.9" resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" +pako@~1.0.2: + version "1.0.5" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.5.tgz#d2205dfe5b9da8af797e7c163db4d1f84e4600bc" + parse-asn1@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.0.0.tgz#35060f6d5015d37628c770f4e091a0b5a278bc23" @@ -3661,7 +3697,7 @@ readable-stream@~1.0.2: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@~2.0.0: +readable-stream@~2.0.0, readable-stream@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" dependencies: |