Commit 7d26edde authored by Lin Jen-Shin's avatar Lin Jen-Shin

Merge remote-tracking branch 'upstream/master' into qa-secret-variables-scenario

* upstream/master:
  Make Gitaly RepositoryExists opt-out
  Fix .batch_lfs_pointers accepting a lazy enumerator
  Look at notes created just before merge when deciding if an MR can be reverted
  Update missing paths
  Default to HTTPS for all Gravatar URLs
  Add note within ux documentation that further changes should be made within the design.gitlab project
  Moves status icon into a vue file and adds tests Moves merging component into a vue file, adds i18n and better test cases
  Prefer local variables instead
  Add an test for QA::Runtime::RSAKey
  Move initialize method later.
  Also test if the fingerprint is correct
  Generate ssh key on the fly for QA
parents 3d4a7f63 9df130ff
...@@ -53,10 +53,10 @@ ...@@ -53,10 +53,10 @@
</i> </i>
</div> </div>
<div class="deploy-key-content key-list-item-info"> <div class="deploy-key-content key-list-item-info">
<strong class="title"> <strong class="title qa-key-title">
{{ deployKey.title }} {{ deployKey.title }}
</strong> </strong>
<div class="description"> <div class="description qa-key-fingerprint">
{{ deployKey.fingerprint }} {{ deployKey.fingerprint }}
</div> </div>
</div> </div>
......
...@@ -2,7 +2,7 @@ import { getTimeago } from '~/lib/utils/datetime_utility'; ...@@ -2,7 +2,7 @@ import { getTimeago } from '~/lib/utils/datetime_utility';
import { visitUrl } from '../../lib/utils/url_utility'; import { visitUrl } from '../../lib/utils/url_utility';
import Flash from '../../flash'; import Flash from '../../flash';
import MemoryUsage from './mr_widget_memory_usage'; import MemoryUsage from './mr_widget_memory_usage';
import StatusIcon from './mr_widget_status_icon'; import StatusIcon from './mr_widget_status_icon.vue';
import MRWidgetService from '../services/mr_widget_service'; import MRWidgetService from '../services/mr_widget_service';
export default { export default {
......
import ciIcon from '../../vue_shared/components/ci_icon.vue';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
export default {
props: {
status: { type: String, required: true },
showDisabledButton: { type: Boolean, required: false },
},
components: {
ciIcon,
loadingIcon,
},
computed: {
statusObj() {
return {
group: this.status,
icon: `status_${this.status}`,
};
},
},
template: `
<div class="space-children flex-container-block append-right-10">
<div v-if="status === 'loading'" class="mr-widget-icon">
<loading-icon />
</div>
<ci-icon v-else :status="statusObj" />
<button
v-if="showDisabledButton"
type="button"
class="js-disabled-merge-button btn btn-success btn-sm"
disabled="true">
Merge
</button>
</div>
`,
};
<script>
import ciIcon from '../../vue_shared/components/ci_icon.vue';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
export default {
components: {
ciIcon,
loadingIcon,
},
props: {
status: {
type: String,
required: true,
},
showDisabledButton: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
isLoading() {
return this.status === 'loading';
},
statusObj() {
return {
group: this.status,
icon: `status_${this.status}`,
};
},
},
};
</script>
<template>
<div class="space-children flex-container-block append-right-10">
<div
v-if="isLoading"
class="mr-widget-icon"
>
<loading-icon />
</div>
<ci-icon
v-else
:status="statusObj"
/>
<button
v-if="showDisabledButton"
type="button"
class="js-disabled-merge-button btn btn-success btn-sm"
disabled="true"
>
{{ s__("mrWidget|Merge") }}
</button>
</div>
</template>
<script> <script>
import statusIcon from '../mr_widget_status_icon'; import statusIcon from '../mr_widget_status_icon.vue';
export default { export default {
name: 'MRWidgetArchived', name: 'MRWidgetArchived',
......
<script> <script>
import loadingIcon from '~/vue_shared/components/loading_icon.vue'; import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
import statusIcon from '../mr_widget_status_icon'; import statusIcon from '../mr_widget_status_icon.vue';
export default { export default {
name: 'MRWidgetAutoMergeFailed', name: 'MRWidgetAutoMergeFailed',
......
<script> <script>
import statusIcon from '../mr_widget_status_icon'; import statusIcon from '../mr_widget_status_icon.vue';
export default { export default {
name: 'MRWidgetChecking', name: 'MRWidgetChecking',
......
<script> <script>
import mrWidgetAuthorTime from '../../components/mr_widget_author_time'; import mrWidgetAuthorTime from '../../components/mr_widget_author_time';
import statusIcon from '../mr_widget_status_icon'; import statusIcon from '../mr_widget_status_icon.vue';
export default { export default {
name: 'MRWidgetClosed', name: 'MRWidgetClosed',
......
<script> <script>
import statusIcon from '../mr_widget_status_icon'; import statusIcon from '../mr_widget_status_icon.vue';
export default { export default {
name: 'MRWidgetConflicts', name: 'MRWidgetConflicts',
......
import statusIcon from '../mr_widget_status_icon'; import statusIcon from '../mr_widget_status_icon.vue';
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
export default { export default {
......
import Flash from '../../../flash'; import Flash from '../../../flash';
import statusIcon from '../mr_widget_status_icon'; import statusIcon from '../mr_widget_status_icon.vue';
import MRWidgetAuthor from '../../components/mr_widget_author'; import MRWidgetAuthor from '../../components/mr_widget_author';
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
......
...@@ -2,7 +2,7 @@ import Flash from '../../../flash'; ...@@ -2,7 +2,7 @@ import Flash from '../../../flash';
import mrWidgetAuthorTime from '../../components/mr_widget_author_time'; import mrWidgetAuthorTime from '../../components/mr_widget_author_time';
import tooltip from '../../../vue_shared/directives/tooltip'; import tooltip from '../../../vue_shared/directives/tooltip';
import loadingIcon from '../../../vue_shared/components/loading_icon.vue'; import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
import statusIcon from '../mr_widget_status_icon'; import statusIcon from '../mr_widget_status_icon.vue';
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
export default { export default {
......
import statusIcon from '../mr_widget_status_icon';
export default {
name: 'MRWidgetMerging',
props: {
mr: { type: Object, required: true },
},
components: {
statusIcon,
},
template: `
<div class="mr-widget-body mr-state-locked media">
<status-icon status="loading" />
<div class="media-body">
<h4>
This merge request is in the process of being merged
</h4>
<section class="mr-info-list">
<p>
The changes will be merged into
<span class="label-branch">
<a :href="mr.targetBranchPath">{{mr.targetBranch}}</a>
</span>
</p>
</section>
</div>
</div>
`,
};
<script>
import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetMerging',
components: {
statusIcon,
},
props: {
mr: {
type: Object,
required: true,
default: () => ({}),
},
},
};
</script>
<template>
<div class="mr-widget-body mr-state-locked media">
<status-icon status="loading" />
<div class="media-body">
<h4>
{{ s__("mrWidget|This merge request is in the process of being merged") }}
</h4>
<section class="mr-info-list">
<p>
{{ s__("mrWidget|The changes will be merged into") }}
<span class="label-branch">
<a :href="mr.targetBranchPath">{{ mr.targetBranch }}</a>
</span>
</p>
</section>
</div>
</div>
</template>
import statusIcon from '../mr_widget_status_icon'; import statusIcon from '../mr_widget_status_icon.vue';
import tooltip from '../../../vue_shared/directives/tooltip'; import tooltip from '../../../vue_shared/directives/tooltip';
import mrWidgetMergeHelp from '../../components/mr_widget_merge_help'; import mrWidgetMergeHelp from '../../components/mr_widget_merge_help';
......
import statusIcon from '../mr_widget_status_icon'; import statusIcon from '../mr_widget_status_icon.vue';
export default { export default {
name: 'MRWidgetNotAllowed', name: 'MRWidgetNotAllowed',
......
import statusIcon from '../mr_widget_status_icon'; import statusIcon from '../mr_widget_status_icon.vue';
export default { export default {
name: 'MRWidgetPipelineBlocked', name: 'MRWidgetPipelineBlocked',
......
import statusIcon from '../mr_widget_status_icon'; import statusIcon from '../mr_widget_status_icon.vue';
export default { export default {
name: 'MRWidgetPipelineBlocked', name: 'MRWidgetPipelineBlocked',
......
...@@ -3,7 +3,7 @@ import warningSvg from 'icons/_icon_status_warning.svg'; ...@@ -3,7 +3,7 @@ import warningSvg from 'icons/_icon_status_warning.svg';
import simplePoll from '~/lib/utils/simple_poll'; import simplePoll from '~/lib/utils/simple_poll';
import MergeRequest from '../../../merge_request'; import MergeRequest from '../../../merge_request';
import Flash from '../../../flash'; import Flash from '../../../flash';
import statusIcon from '../mr_widget_status_icon'; import statusIcon from '../mr_widget_status_icon.vue';
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
export default { export default {
......
<script> <script>
import simplePoll from '../../../lib/utils/simple_poll'; import simplePoll from '../../../lib/utils/simple_poll';
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
import statusIcon from '../mr_widget_status_icon'; import statusIcon from '../mr_widget_status_icon.vue';
import loadingIcon from '../../../vue_shared/components/loading_icon.vue'; import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
import Flash from '../../../flash'; import Flash from '../../../flash';
......
import statusIcon from '../mr_widget_status_icon'; import statusIcon from '../mr_widget_status_icon.vue';
export default { export default {
name: 'MRWidgetSHAMismatch', name: 'MRWidgetSHAMismatch',
......
import statusIcon from '../mr_widget_status_icon'; import statusIcon from '../mr_widget_status_icon.vue';
export default { export default {
name: 'MRWidgetUnresolvedDiscussions', name: 'MRWidgetUnresolvedDiscussions',
......
import statusIcon from '../mr_widget_status_icon'; import statusIcon from '../mr_widget_status_icon.vue';
import tooltip from '../../../vue_shared/directives/tooltip'; import tooltip from '../../../vue_shared/directives/tooltip';
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
......
...@@ -19,7 +19,7 @@ export { default as WidgetRelatedLinks } from './components/mr_widget_related_li ...@@ -19,7 +19,7 @@ export { default as WidgetRelatedLinks } from './components/mr_widget_related_li
export { default as MergedState } from './components/states/mr_widget_merged'; export { default as MergedState } from './components/states/mr_widget_merged';
export { default as FailedToMerge } from './components/states/mr_widget_failed_to_merge'; export { default as FailedToMerge } from './components/states/mr_widget_failed_to_merge';
export { default as ClosedState } from './components/states/mr_widget_closed.vue'; export { default as ClosedState } from './components/states/mr_widget_closed.vue';
export { default as MergingState } from './components/states/mr_widget_merging'; export { default as MergingState } from './components/states/mr_widget_merging.vue';
export { default as WipState } from './components/states/mr_widget_wip'; export { default as WipState } from './components/states/mr_widget_wip';
export { default as ArchivedState } from './components/states/mr_widget_archived.vue'; export { default as ArchivedState } from './components/states/mr_widget_archived.vue';
export { default as ConflictsState } from './components/states/mr_widget_conflicts.vue'; export { default as ConflictsState } from './components/states/mr_widget_conflicts.vue';
......
...@@ -989,8 +989,14 @@ class MergeRequest < ActiveRecord::Base ...@@ -989,8 +989,14 @@ class MergeRequest < ActiveRecord::Base
merged_at = metrics&.merged_at merged_at = metrics&.merged_at
notes_association = notes_with_associations notes_association = notes_with_associations
# It is not guaranteed that Note#created_at will be strictly later than
# MergeRequestMetric#merged_at. Nanoseconds on MySQL may break this
# comparison, as will a HA environment if clocks are not *precisely*
# synchronized. Add a minute's leeway to compensate for both possibilities
cutoff = merged_at - 1.minute
if merged_at if merged_at
notes_association = notes_association.where('created_at > ?', merged_at) notes_association = notes_association.where('created_at >= ?', cutoff)
end end
!merge_commit.has_been_reverted?(current_user, notes_association) !merge_commit.has_been_reverted?(current_user, notes_association)
......
...@@ -13,11 +13,11 @@ ...@@ -13,11 +13,11 @@
- if @user.avatar? - if @user.avatar?
You can change your avatar here You can change your avatar here
- if gravatar_enabled? - if gravatar_enabled?
or remove the current avatar to revert to #{link_to Gitlab.config.gravatar.host, 'http://' + Gitlab.config.gravatar.host} or remove the current avatar to revert to #{link_to Gitlab.config.gravatar.host, 'https://' + Gitlab.config.gravatar.host}
- else - else
You can upload an avatar here You can upload an avatar here
- if gravatar_enabled? - if gravatar_enabled?
or change it at #{link_to Gitlab.config.gravatar.host, 'http://' + Gitlab.config.gravatar.host} or change it at #{link_to Gitlab.config.gravatar.host, 'https://' + Gitlab.config.gravatar.host}
.col-lg-8 .col-lg-8
.clearfix.avatar-image.append-bottom-default .clearfix.avatar-image.append-bottom-default
= link_to avatar_icon(@user, 400), target: '_blank', rel: 'noopener noreferrer' do = link_to avatar_icon(@user, 400), target: '_blank', rel: 'noopener noreferrer' do
......
---
title: Default to HTTPS for all Gravatar URLs
merge_request: 16666
author:
type: fixed
---
title: Make Gitaly RepositoryExists opt-out
merge_request: 16680
author:
type: other
---
title: Add note within ux documentation that further changes should be made within
the design.gitlab project
merge_request:
author:
type: deprecated
...@@ -175,10 +175,12 @@ production: &base ...@@ -175,10 +175,12 @@ production: &base
host: 'https://mattermost.example.com' host: 'https://mattermost.example.com'
## Gravatar ## Gravatar
## For Libravatar see: http://doc.gitlab.com/ce/customization/libravatar.html ## If using gravatar.com, there's nothing to change here. For Libravatar
## you'll need to provide the custom URLs. For more information,
## see: https://docs.gitlab.com/ee/customization/libravatar.html
gravatar: gravatar:
# gravatar urls: possible placeholders: %{hash} %{size} %{email} %{username} # Gravatar/Libravatar URLs: possible placeholders: %{hash} %{size} %{email} %{username}
# plain_url: "http://..." # default: http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon # plain_url: "http://..." # default: https://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon
# ssl_url: "https://..." # default: https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon # ssl_url: "https://..." # default: https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon
## Auxiliary jobs ## Auxiliary jobs
......
...@@ -350,7 +350,7 @@ Settings.mattermost['host'] = nil unless Settings.mattermost.enabled ...@@ -350,7 +350,7 @@ Settings.mattermost['host'] = nil unless Settings.mattermost.enabled
# #
Settings['gravatar'] ||= Settingslogic.new({}) Settings['gravatar'] ||= Settingslogic.new({})
Settings.gravatar['enabled'] = true if Settings.gravatar['enabled'].nil? Settings.gravatar['enabled'] = true if Settings.gravatar['enabled'].nil?
Settings.gravatar['plain_url'] ||= 'http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon' Settings.gravatar['plain_url'] ||= 'https://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon'
Settings.gravatar['ssl_url'] ||= 'https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon' Settings.gravatar['ssl_url'] ||= 'https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon'
Settings.gravatar['host'] = Settings.host_without_www(Settings.gravatar['plain_url']) Settings.gravatar['host'] = Settings.host_without_www(Settings.gravatar['plain_url'])
......
> We are in the process of transferring UX documentation to the [design.gitlab.com](https://gitlab.com/gitlab-org/design.gitlab.com) project. Any updates to these docs should be made in that project. If documentation does not yet exist within [design.gitlab.com](https://gitlab.com/gitlab-org/design.gitlab.com), [create an issue](https://gitlab.com/gitlab-org/design.gitlab.com/issues) and merge request to add your new changes.
# GitLab UX Guide # GitLab UX Guide
The goal of this guide is to provide standards, principles and in-depth information to design beautiful and effective GitLab features. This will be a living document, and we welcome contributions, feedback and suggestions. The goal of this guide is to provide standards, principles and in-depth information to design beautiful and effective GitLab features. This will be a living document, and we welcome contributions, feedback and suggestions.
......
...@@ -70,11 +70,9 @@ module Gitlab ...@@ -70,11 +70,9 @@ module Gitlab
# Returns array of Gitlab::Git::Blob # Returns array of Gitlab::Git::Blob
# Does not guarantee blob data will be set # Does not guarantee blob data will be set
def batch_lfs_pointers(repository, blob_ids) def batch_lfs_pointers(repository, blob_ids)
return [] if blob_ids.empty?
repository.gitaly_migrate(:batch_lfs_pointers) do |is_enabled| repository.gitaly_migrate(:batch_lfs_pointers) do |is_enabled|
if is_enabled if is_enabled
repository.gitaly_blob_client.batch_lfs_pointers(blob_ids) repository.gitaly_blob_client.batch_lfs_pointers(blob_ids.to_a)
else else
blob_ids.lazy blob_ids.lazy
.select { |sha| possible_lfs_blob?(repository, sha) } .select { |sha| possible_lfs_blob?(repository, sha) }
......
...@@ -133,7 +133,7 @@ module Gitlab ...@@ -133,7 +133,7 @@ module Gitlab
end end
def exists? def exists?
Gitlab::GitalyClient.migrate(:repository_exists) do |enabled| Gitlab::GitalyClient.migrate(:repository_exists, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |enabled|
if enabled if enabled
gitaly_repository_client.exists? gitaly_repository_client.exists?
else else
......
...@@ -34,6 +34,8 @@ module Gitlab ...@@ -34,6 +34,8 @@ module Gitlab
end end
def batch_lfs_pointers(blob_ids) def batch_lfs_pointers(blob_ids)
return [] if blob_ids.empty?
request = Gitaly::GetLFSPointersRequest.new( request = Gitaly::GetLFSPointersRequest.new(
repository: @gitaly_repo, repository: @gitaly_repo,
blob_ids: blob_ids blob_ids: blob_ids
......
...@@ -6,4 +6,5 @@ gem 'capybara-screenshot', '~> 1.0.18' ...@@ -6,4 +6,5 @@ gem 'capybara-screenshot', '~> 1.0.18'
gem 'rake', '~> 12.3.0' gem 'rake', '~> 12.3.0'
gem 'rspec', '~> 3.7' gem 'rspec', '~> 3.7'
gem 'selenium-webdriver', '~> 3.8.0' gem 'selenium-webdriver', '~> 3.8.0'
gem 'net-ssh', require: false
gem 'airborne', '~> 0.2.13' gem 'airborne', '~> 0.2.13'
...@@ -46,6 +46,7 @@ GEM ...@@ -46,6 +46,7 @@ GEM
mini_mime (1.0.0) mini_mime (1.0.0)
mini_portile2 (2.3.0) mini_portile2 (2.3.0)
minitest (5.11.1) minitest (5.11.1)
net-ssh (4.1.0)
netrc (0.11.0) netrc (0.11.0)
nokogiri (1.8.1) nokogiri (1.8.1)
mini_portile2 (~> 2.3.0) mini_portile2 (~> 2.3.0)
...@@ -97,6 +98,7 @@ DEPENDENCIES ...@@ -97,6 +98,7 @@ DEPENDENCIES
airborne (~> 0.2.13) airborne (~> 0.2.13)
capybara (~> 2.16.1) capybara (~> 2.16.1)
capybara-screenshot (~> 1.0.18) capybara-screenshot (~> 1.0.18)
net-ssh
pry-byebug (~> 3.5.1) pry-byebug (~> 3.5.1)
rake (~> 12.3.0) rake (~> 12.3.0)
rspec (~> 3.7) rspec (~> 3.7)
......
...@@ -11,6 +11,7 @@ module QA ...@@ -11,6 +11,7 @@ module QA
autoload :Scenario, 'qa/runtime/scenario' autoload :Scenario, 'qa/runtime/scenario'
autoload :Browser, 'qa/runtime/browser' autoload :Browser, 'qa/runtime/browser'
autoload :Env, 'qa/runtime/env' autoload :Env, 'qa/runtime/env'
autoload :RSAKey, 'qa/runtime/rsa_key'
autoload :Address, 'qa/runtime/address' autoload :Address, 'qa/runtime/address'
autoload :API, 'qa/runtime/api' autoload :API, 'qa/runtime/api'
end end
......
...@@ -10,6 +10,12 @@ module QA ...@@ -10,6 +10,12 @@ module QA
end end
end end
product :fingerprint do
Page::Project::Settings::Repository.act do
expand_deploy_keys(&:key_fingerprint)
end
end
dependency Factory::Resource::Project, as: :project do |project| dependency Factory::Resource::Project, as: :project do |project|
project.name = 'project-to-deploy' project.name = 'project-to-deploy'
project.description = 'project for adding deploy key test' project.description = 'project for adding deploy key test'
......
...@@ -41,7 +41,21 @@ module QA ...@@ -41,7 +41,21 @@ module QA
end end
def click_element(name) def click_element(name)
find(Page::Element.new(name).selector_css).click find_element(name).click
end
def find_element(name)
find(element_selector_css(name))
end
def within_element(name)
page.within(element_selector_css(name)) do
yield
end
end
def element_selector_css(name)
Page::Element.new(name).selector_css
end end
def self.path def self.path
......
...@@ -14,8 +14,8 @@ module QA ...@@ -14,8 +14,8 @@ module QA
end end
view 'app/assets/javascripts/deploy_keys/components/key.vue' do view 'app/assets/javascripts/deploy_keys/components/key.vue' do
element :key_title, /class=".*title.*"/ element :key_title, /class=".*qa-key-title.*"/
element :key_title_field, '{{ deployKey.title }}' element :key_fingerprint, /class=".*qa-key-fingerprint.*"/
end end
def fill_key_title(title) def fill_key_title(title)
...@@ -31,8 +31,22 @@ module QA ...@@ -31,8 +31,22 @@ module QA
end end
def key_title def key_title
page.within('.qa-project-deploy-keys') do within_project_deploy_keys do
page.find('.title').text find_element(:key_title).text
end
end
def key_fingerprint
within_project_deploy_keys do
find_element(:key_fingerprint).text
end
end
private
def within_project_deploy_keys
within_element(:project_deploy_keys) do
yield
end end
end end
end end
......
require 'net/ssh'
require 'forwardable'
module QA
module Runtime
class RSAKey
extend Forwardable
attr_reader :key
def_delegators :@key, :fingerprint
def initialize(bits = 4096)
@key = OpenSSL::PKey::RSA.new(bits)
end
def public_key
@public_key ||= "#{key.ssh_type} #{[key.to_blob].pack('m0')}"
end
end
end
end
...@@ -10,17 +10,6 @@ module QA ...@@ -10,17 +10,6 @@ module QA
def password def password
ENV['GITLAB_PASSWORD'] || '5iveL!fe' ENV['GITLAB_PASSWORD'] || '5iveL!fe'
end end
def ssh_key
<<~KEY.delete("\n")
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFf6RYK3qu/RKF/3ndJmL5xgMLp3O9
6x8lTay+QGZ0+9FnnAXMdUqBq/ZU6d/gyMB4IaW3nHzM1w049++yAB6UPCzMB8Uo27K5
/jyZCtj7Vm9PFNjF/8am1kp46c/SeYicQgQaSBdzIW3UDEa1Ef68qroOlvpi9PYZ/tA7
M0YP0K5PXX+E36zaIRnJVMPT3f2k+GnrxtjafZrwFdpOP/Fol5BQLBgcsyiU+LM1SuaC
rzd8c9vyaTA1CxrkxaZh+buAi0PmdDtaDrHd42gqZkXCKavyvgM5o2CkQ5LJHCgzpXy0
5qNFzmThBSkb+XtoxbyagBiGbVZtSVow6Xa7qewz= dummy@gitlab.com
KEY
end
end end
end end
end end
module QA module QA
feature 'deploy keys support', :core do feature 'deploy keys support', :core do
given(:deploy_key_title) { 'deploy key title' }
given(:deploy_key_value) { Runtime::User.ssh_key }
scenario 'user adds a deploy key' do scenario 'user adds a deploy key' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials } Page::Main::Login.act { sign_in_using_credentials }
key = Runtime::RSAKey.new
deploy_key_title = 'deploy key title'
deploy_key_value = key.public_key
deploy_key = Factory::Resource::DeployKey.fabricate! do |resource| deploy_key = Factory::Resource::DeployKey.fabricate! do |resource|
resource.title = deploy_key_title resource.title = deploy_key_title
resource.key = deploy_key_value resource.key = deploy_key_value
end end
expect(deploy_key.title).to eq(deploy_key_title) expect(deploy_key.title).to eq(deploy_key_title)
expect(deploy_key.fingerprint).to eq(key.fingerprint)
end end
end end
end end
describe QA::Runtime::RSAKey do
describe '#public_key' do
subject { described_class.new.public_key }
it 'generates a public RSA key' do
expect(subject).to match(/\Assh\-rsa AAAA[0-9A-Za-z+\/]+={0,3}\z/)
end
end
end
...@@ -91,7 +91,7 @@ x #ccc solid;padding-left:1ex"><div> ...@@ -91,7 +91,7 @@ x #ccc solid;padding-left:1ex"><div>
adding=3D"0" border=3D"0"><tbody> adding=3D"0" border=3D"0"><tbody>
<tr> <tr>
<td style=3D"vertical-align:top;width:55px"> <td style=3D"vertical-align:top;width:55px">
<img src=3D"http://www.gravatar.com/avatar/42776c4982dff1fa45ee8248= <img src=3D"https://www.gravatar.com/avatar/42776c4982dff1fa45ee8248=
532f8ad0.png?s=3D45&amp;r=3Dpg&amp;d=3Didenticon" title=3D"Neil" style=3D"m= 532f8ad0.png?s=3D45&amp;r=3Dpg&amp;d=3Didenticon" title=3D"Neil" style=3D"m=
ax-width:694px" width=3D"45" height=3D"45"> ax-width:694px" width=3D"45" height=3D"45">
</td> </td>
...@@ -121,7 +121,7 @@ nk">@eviltrout</a> Any idea why it showed up in suggested topics? </p> ...@@ -121,7 +121,7 @@ nk">@eviltrout</a> Any idea why it showed up in suggested topics? </p>
<div style=3D"color:#666"> <div style=3D"color:#666">
<p>To respond, reply to this email or visit <a href=3D"http://meta.disc= <p>To respond, reply to this email or visit <a href=3D"http://meta.disc=
ourse.org/t/spam-post-pops-back-up-in-suggested-topics/11005/5" style=3D"co= ourse.org/t/spam-post-pops-back-up-in-suggested-topics/11005/5" style=3D"co=
lor:#666" target=3D"_blank">http://meta.discourse.org/t/spam-post-pops-back= lor:#666" target=3D"_blank">https://meta.discourse.org/t/spam-post-pops-back=
-up-in-suggested-topics/11005/5</a> in your browser.</p> -up-in-suggested-topics/11005/5</a> in your browser.</p>
</div> </div>
...@@ -132,12 +132,12 @@ lor:#666" target=3D"_blank">http://meta.discourse.org/t/spam-post-pops-back= ...@@ -132,12 +132,12 @@ lor:#666" target=3D"_blank">http://meta.discourse.org/t/spam-post-pops-back=
lpadding=3D"0" border=3D"0"><tbody> lpadding=3D"0" border=3D"0"><tbody>
<tr> <tr>
<td style=3D"vertical-align:top;width:55px"> <td style=3D"vertical-align:top;width:55px">
<img src=3D"http://www.gravatar.com/avatar/42776c4982dff1fa45ee8248= <img src=3D"https://www.gravatar.com/avatar/42776c4982dff1fa45ee8248=
532f8ad0.png?s=3D45&amp;r=3Dpg&amp;d=3Didenticon" title=3D"Neil" style=3D"m= 532f8ad0.png?s=3D45&amp;r=3Dpg&amp;d=3Didenticon" title=3D"Neil" style=3D"m=
ax-width:694px" width=3D"45" height=3D"45"> ax-width:694px" width=3D"45" height=3D"45">
</td> </td>
<td> <td>
<a href=3D"http://meta.discourse.org/users/neil" style=3D"font-size= <a href=3D"https://meta.discourse.org/users/neil" style=3D"font-size=
:13px;font-family:&#39;lucida grande&#39;,tahoma,verdana,arial,sans-serif;c= :13px;font-family:&#39;lucida grande&#39;,tahoma,verdana,arial,sans-serif;c=
olor:#3b5998;text-decoration:none;font-weight:bold" target=3D"_blank">Neil<= olor:#3b5998;text-decoration:none;font-weight:bold" target=3D"_blank">Neil<=
/a><br> /a><br>
...@@ -155,12 +155,12 @@ vember 19</span> ...@@ -155,12 +155,12 @@ vember 19</span>
adding=3D"0" border=3D"0"><tbody> adding=3D"0" border=3D"0"><tbody>
<tr> <tr>
<td style=3D"vertical-align:top;width:55px"> <td style=3D"vertical-align:top;width:55px">
<img src=3D"http://www.gravatar.com/avatar/5120fc4e345db0d1a9648882= <img src=3D"https://www.gravatar.com/avatar/5120fc4e345db0d1a9648882=
72073819.png?s=3D45&amp;r=3Dpg&amp;d=3Didenticon" title=3D"riking" style=3D= 72073819.png?s=3D45&amp;r=3Dpg&amp;d=3Didenticon" title=3D"riking" style=3D=
"max-width:694px" width=3D"45" height=3D"45"> "max-width:694px" width=3D"45" height=3D"45">
</td> </td>
<td> <td>
<a href=3D"http://meta.discourse.org/users/riking" style=3D"font-si= <a href=3D"https://meta.discourse.org/users/riking" style=3D"font-si=
ze:13px;font-family:&#39;lucida grande&#39;,tahoma,verdana,arial,sans-serif= ze:13px;font-family:&#39;lucida grande&#39;,tahoma,verdana,arial,sans-serif=
;color:#3b5998;text-decoration:none;font-weight:bold" target=3D"_blank">rik= ;color:#3b5998;text-decoration:none;font-weight:bold" target=3D"_blank">rik=
ing</a><br> ing</a><br>
...@@ -173,7 +173,7 @@ vember 19</span> ...@@ -173,7 +173,7 @@ vember 19</span>
<td style=3D"padding-top:5px" colspan=3D"2"> <td style=3D"padding-top:5px" colspan=3D"2">
<p style=3D"margin-top:0"><u></u></p><div> <p style=3D"margin-top:0"><u></u></p><div>
<div></div> <div></div>
<img width=3D"20" height=3D"20" src=3D"http://www.gravatar.com/avatar/51d62= <img width=3D"20" height=3D"20" src=3D"https://www.gravatar.com/avatar/51d62=
3f33f8b83095db84ff35e15dbe8.png?s=3D40&amp;r=3Dpg&amp;d=3Didenticon" style= 3f33f8b83095db84ff35e15dbe8.png?s=3D40&amp;r=3Dpg&amp;d=3Didenticon" style=
=3D"max-width:694px">codinghorror:</div> =3D"max-width:694px">codinghorror:</div>
<blockquote><p style=3D"margin-top:0">I can&#39;t even find that topic by n= <blockquote><p style=3D"margin-top:0">I can&#39;t even find that topic by n=
...@@ -193,12 +193,12 @@ uld be invisible to me, and not showing up in Suggested Topics.</p> ...@@ -193,12 +193,12 @@ uld be invisible to me, and not showing up in Suggested Topics.</p>
adding=3D"0" border=3D"0"><tbody> adding=3D"0" border=3D"0"><tbody>
<tr> <tr>
<td style=3D"vertical-align:top;width:55px"> <td style=3D"vertical-align:top;width:55px">
<img src=3D"http://www.gravatar.com/avatar/51d623f33f8b83095db84ff3= <img src=3D"https://www.gravatar.com/avatar/51d623f33f8b83095db84ff3=
5e15dbe8.png?s=3D45&amp;r=3Dpg&amp;d=3Didenticon" title=3D"codinghorror" st= 5e15dbe8.png?s=3D45&amp;r=3Dpg&amp;d=3Didenticon" title=3D"codinghorror" st=
yle=3D"max-width:694px" width=3D"45" height=3D"45"> yle=3D"max-width:694px" width=3D"45" height=3D"45">
</td> </td>
<td> <td>
<a href=3D"http://meta.discourse.org/users/codinghorror" style=3D"f= <a href=3D"https://meta.discourse.org/users/codinghorror" style=3D"f=
ont-size:13px;font-family:&#39;lucida grande&#39;,tahoma,verdana,arial,sans= ont-size:13px;font-family:&#39;lucida grande&#39;,tahoma,verdana,arial,sans=
-serif;color:#3b5998;text-decoration:none;font-weight:bold" target=3D"_blan= -serif;color:#3b5998;text-decoration:none;font-weight:bold" target=3D"_blan=
k">codinghorror</a><br> k">codinghorror</a><br>
...@@ -219,12 +219,12 @@ rout" target=3D"_blank">@eviltrout</a>? I can&#39;t even find that topic by= ...@@ -219,12 +219,12 @@ rout" target=3D"_blank">@eviltrout</a>? I can&#39;t even find that topic by=
adding=3D"0" border=3D"0"><tbody> adding=3D"0" border=3D"0"><tbody>
<tr> <tr>
<td style=3D"vertical-align:top;width:55px"> <td style=3D"vertical-align:top;width:55px">
<img src=3D"http://www.gravatar.com/avatar/5120fc4e345db0d1a9648882= <img src=3D"https://www.gravatar.com/avatar/5120fc4e345db0d1a9648882=
72073819.png?s=3D45&amp;r=3Dpg&amp;d=3Didenticon" title=3D"riking" style=3D= 72073819.png?s=3D45&amp;r=3Dpg&amp;d=3Didenticon" title=3D"riking" style=3D=
"max-width:694px" width=3D"45" height=3D"45"> "max-width:694px" width=3D"45" height=3D"45">
</td> </td>
<td> <td>
<a href=3D"http://meta.discourse.org/users/riking" style=3D"font-si= <a href=3D"https://meta.discourse.org/users/riking" style=3D"font-si=
ze:13px;font-family:&#39;lucida grande&#39;,tahoma,verdana,arial,sans-serif= ze:13px;font-family:&#39;lucida grande&#39;,tahoma,verdana,arial,sans-serif=
;color:#3b5998;text-decoration:none;font-weight:bold" target=3D"_blank">rik= ;color:#3b5998;text-decoration:none;font-weight:bold" target=3D"_blank">rik=
ing</a><br> ing</a><br>
...@@ -241,7 +241,7 @@ lar spam post, and it was promptly deleted/hidden, but it just popped up in= ...@@ -241,7 +241,7 @@ lar spam post, and it was promptly deleted/hidden, but it just popped up in=
<p style=3D"margin-top:0"></p> <p style=3D"margin-top:0"></p>
<div><a href=3D"//cdn.discourse.org/uploads/meta_discourse/2158/50b8b49557c= <div><a href=3D"//cdn.discourse.org/uploads/meta_discourse/2158/50b8b49557c=
b249e.png" target=3D"_blank"><img src=3D"http://cdn.discourse.org/uploads/m= b249e.png" target=3D"_blank"><img src=3D"https://cdn.discourse.org/uploads/m=
eta_discourse/_optimized/ab1/c92/acd2c33402_584x134.png" width=3D"584" heig= eta_discourse/_optimized/ab1/c92/acd2c33402_584x134.png" width=3D"584" heig=
ht=3D"134" style=3D"max-width:694px"><div> ht=3D"134" style=3D"max-width:694px"><div>
...@@ -257,12 +257,12 @@ ht=3D"134" style=3D"max-width:694px"><div> ...@@ -257,12 +257,12 @@ ht=3D"134" style=3D"max-width:694px"><div>
<div style=3D"color:#666"> <div style=3D"color:#666">
<p>To respond, reply to this email or visit <a href=3D"http://meta.discours= <p>To respond, reply to this email or visit <a href=3D"http://meta.discours=
e.org/t/spam-post-pops-back-up-in-suggested-topics/11005/5" style=3D"color:= e.org/t/spam-post-pops-back-up-in-suggested-topics/11005/5" style=3D"color:=
#666" target=3D"_blank">http://meta.discourse.org/t/spam-post-pops-back-up-= #666" target=3D"_blank">https://meta.discourse.org/t/spam-post-pops-back-up-=
in-suggested-topics/11005/5</a> in your browser.</p> in-suggested-topics/11005/5</a> in your browser.</p>
</div> </div>
<div style=3D"color:#666"> <div style=3D"color:#666">
<p>To unsubscribe from these emails, visit your <a href=3D"http://meta.disc= <p>To unsubscribe from these emails, visit your <a href=3D"https://meta.disc=
ourse.org/user_preferences" style=3D"color:#666" target=3D"_blank">user pre= ourse.org/user_preferences" style=3D"color:#666" target=3D"_blank">user pre=
ferences</a>.</p> ferences</a>.</p>
</div> </div>
......
...@@ -117,7 +117,7 @@ describe ApplicationHelper do ...@@ -117,7 +117,7 @@ describe ApplicationHelper do
stub_config_setting(https: false) stub_config_setting(https: false)
expect(helper.gravatar_icon(user_email)) expect(helper.gravatar_icon(user_email))
.to match('http://www.gravatar.com/avatar/b58c6f14d292556214bd64909bcdb118') .to match('https://www.gravatar.com/avatar/b58c6f14d292556214bd64909bcdb118')
end end
it 'uses HTTPs when configured' do it 'uses HTTPs when configured' do
......
...@@ -24,7 +24,7 @@ describe Settings do ...@@ -24,7 +24,7 @@ describe Settings do
expect(described_class.host_without_www('http://foo.com')).to eq 'foo.com' expect(described_class.host_without_www('http://foo.com')).to eq 'foo.com'
expect(described_class.host_without_www('http://www.foo.com')).to eq 'foo.com' expect(described_class.host_without_www('http://www.foo.com')).to eq 'foo.com'
expect(described_class.host_without_www('http://secure.foo.com')).to eq 'secure.foo.com' expect(described_class.host_without_www('http://secure.foo.com')).to eq 'secure.foo.com'
expect(described_class.host_without_www('http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon')).to eq 'gravatar.com' expect(described_class.host_without_www('https://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon')).to eq 'gravatar.com'
expect(described_class.host_without_www('https://foo.com')).to eq 'foo.com' expect(described_class.host_without_www('https://foo.com')).to eq 'foo.com'
expect(described_class.host_without_www('https://www.foo.com')).to eq 'foo.com' expect(described_class.host_without_www('https://www.foo.com')).to eq 'foo.com'
......
...@@ -68,7 +68,7 @@ describe('Environment item', () => { ...@@ -68,7 +68,7 @@ describe('Environment item', () => {
username: 'root', username: 'root',
id: 1, id: 1,
state: 'active', state: 'active',
avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root', web_url: 'http://localhost:3000/root',
}, },
commit: { commit: {
...@@ -84,7 +84,7 @@ describe('Environment item', () => { ...@@ -84,7 +84,7 @@ describe('Environment item', () => {
username: 'root', username: 'root',
id: 1, id: 1,
state: 'active', state: 'active',
avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root', web_url: 'http://localhost:3000/root',
}, },
commit_path: '/root/ci-folders/tree/500aabcb17c97bdcf2d0c410b70cb8556f0362dd', commit_path: '/root/ci-folders/tree/500aabcb17c97bdcf2d0c410b70cb8556f0362dd',
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
"username": "root", "username": "root",
"id": 1, "id": 1,
"state": "active", "state": "active",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon",
"web_url": "http://localhost:3000/u/root" "web_url": "http://localhost:3000/u/root"
}, },
"name": "test", "name": "test",
......
...@@ -4,7 +4,7 @@ export default { ...@@ -4,7 +4,7 @@ export default {
for (let i = 0; i < numberUsers; i = i += 1) { for (let i = 0; i < numberUsers; i = i += 1) {
users.push( users.push(
{ {
avatar: 'http://gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', avatar: 'https://gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
id: (i + 1), id: (i + 1),
name: `GitLab User ${i}`, name: `GitLab User ${i}`,
username: `gitlab${i}`, username: `gitlab${i}`,
......
...@@ -37,7 +37,7 @@ export default { ...@@ -37,7 +37,7 @@ export default {
username: 'root', username: 'root',
id: 1, id: 1,
state: 'active', state: 'active',
avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root', web_url: 'http://localhost:3000/root',
}, },
erase_path: '/root/ci-mock/-/jobs/4757/erase', erase_path: '/root/ci-mock/-/jobs/4757/erase',
...@@ -54,7 +54,7 @@ export default { ...@@ -54,7 +54,7 @@ export default {
username: 'root', username: 'root',
id: 1, id: 1,
state: 'active', state: 'active',
avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root', web_url: 'http://localhost:3000/root',
}, },
active: false, active: false,
...@@ -107,10 +107,10 @@ export default { ...@@ -107,10 +107,10 @@ export default {
username: 'root', username: 'root',
id: 1, id: 1,
state: 'active', state: 'active',
avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root', web_url: 'http://localhost:3000/root',
}, },
author_gravatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', author_gravatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
commit_url: 'http://localhost:3000/root/ci-mock/commit/c58647773a6b5faf066d4ad6ff2c9fbba5f180f6', commit_url: 'http://localhost:3000/root/ci-mock/commit/c58647773a6b5faf066d4ad6ff2c9fbba5f180f6',
commit_path: '/root/ci-mock/commit/c58647773a6b5faf066d4ad6ff2c9fbba5f180f6', commit_path: '/root/ci-mock/commit/c58647773a6b5faf066d4ad6ff2c9fbba5f180f6',
}, },
......
...@@ -107,7 +107,7 @@ export const note = { ...@@ -107,7 +107,7 @@ export const note = {
"name": "Administrator", "name": "Administrator",
"username": "root", "username": "root",
"state": "active", "state": "active",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"path": "/root" "path": "/root"
}, },
"created_at": "2017-08-10T15:24:03.087Z", "created_at": "2017-08-10T15:24:03.087Z",
......
...@@ -27,7 +27,7 @@ const RESPONSE_MAP = { ...@@ -27,7 +27,7 @@ const RESPONSE_MAP = {
username: 'user0', username: 'user0',
id: 22, id: 22,
state: 'active', state: 'active',
avatar_url: 'http: //www.gravatar.com/avatar/52e4ce24a915fb7e51e1ad3b57f4b00a?s=80\u0026d=identicon', avatar_url: 'https://www.gravatar.com/avatar/52e4ce24a915fb7e51e1ad3b57f4b00a?s=80\u0026d=identicon',
web_url: 'http: //localhost:3001/user0', web_url: 'http: //localhost:3001/user0',
}, },
{ {
...@@ -35,7 +35,7 @@ const RESPONSE_MAP = { ...@@ -35,7 +35,7 @@ const RESPONSE_MAP = {
username: 'tajuana', username: 'tajuana',
id: 18, id: 18,
state: 'active', state: 'active',
avatar_url: 'http: //www.gravatar.com/avatar/4852a41fb41616bf8f140d3701673f53?s=80\u0026d=identicon', avatar_url: 'https://www.gravatar.com/avatar/4852a41fb41616bf8f140d3701673f53?s=80\u0026d=identicon',
web_url: 'http: //localhost:3001/tajuana', web_url: 'http: //localhost:3001/tajuana',
}, },
{ {
...@@ -43,7 +43,7 @@ const RESPONSE_MAP = { ...@@ -43,7 +43,7 @@ const RESPONSE_MAP = {
username: 'michaele.will', username: 'michaele.will',
id: 16, id: 16,
state: 'active', state: 'active',
avatar_url: 'http: //www.gravatar.com/avatar/e301827eb03be955c9c172cb9a8e4e8a?s=80\u0026d=identicon', avatar_url: 'https://www.gravatar.com/avatar/e301827eb03be955c9c172cb9a8e4e8a?s=80\u0026d=identicon',
web_url: 'http: //localhost:3001/michaele.will', web_url: 'http: //localhost:3001/michaele.will',
}, },
], ],
...@@ -72,24 +72,24 @@ const RESPONSE_MAP = { ...@@ -72,24 +72,24 @@ const RESPONSE_MAP = {
username: 'user0', username: 'user0',
id: 22, id: 22,
state: 'active', state: 'active',
avatar_url: 'http: //www.gravatar.com/avatar/52e4ce24a915fb7e51e1ad3b57f4b00a?s=80\u0026d=identicon', avatar_url: 'https://www.gravatar.com/avatar/52e4ce24a915fb7e51e1ad3b57f4b00a?s=80\u0026d=identicon',
web_url: 'http: //localhost:3001/user0', web_url: 'http://localhost:3001/user0',
}, },
{ {
name: 'Marguerite Bartell', name: 'Marguerite Bartell',
username: 'tajuana', username: 'tajuana',
id: 18, id: 18,
state: 'active', state: 'active',
avatar_url: 'http: //www.gravatar.com/avatar/4852a41fb41616bf8f140d3701673f53?s=80\u0026d=identicon', avatar_url: 'https://www.gravatar.com/avatar/4852a41fb41616bf8f140d3701673f53?s=80\u0026d=identicon',
web_url: 'http: //localhost:3001/tajuana', web_url: 'http://localhost:3001/tajuana',
}, },
{ {
name: 'Laureen Ritchie', name: 'Laureen Ritchie',
username: 'michaele.will', username: 'michaele.will',
id: 16, id: 16,
state: 'active', state: 'active',
avatar_url: 'http: //www.gravatar.com/avatar/e301827eb03be955c9c172cb9a8e4e8a?s=80\u0026d=identicon', avatar_url: 'https://www.gravatar.com/avatar/e301827eb03be955c9c172cb9a8e4e8a?s=80\u0026d=identicon',
web_url: 'http: //localhost:3001/michaele.will', web_url: 'http://localhost:3001/michaele.will',
}, },
], ],
human_time_estimate: null, human_time_estimate: null,
...@@ -100,24 +100,24 @@ const RESPONSE_MAP = { ...@@ -100,24 +100,24 @@ const RESPONSE_MAP = {
username: 'user0', username: 'user0',
id: 22, id: 22,
state: 'active', state: 'active',
avatar_url: 'http: //www.gravatar.com/avatar/52e4ce24a915fb7e51e1ad3b57f4b00a?s=80\u0026d=identicon', avatar_url: 'https://www.gravatar.com/avatar/52e4ce24a915fb7e51e1ad3b57f4b00a?s=80\u0026d=identicon',
web_url: 'http: //localhost:3001/user0', web_url: 'http://localhost:3001/user0',
}, },
{ {
name: 'Marguerite Bartell', name: 'Marguerite Bartell',
username: 'tajuana', username: 'tajuana',
id: 18, id: 18,
state: 'active', state: 'active',
avatar_url: 'http: //www.gravatar.com/avatar/4852a41fb41616bf8f140d3701673f53?s=80\u0026d=identicon', avatar_url: 'https://www.gravatar.com/avatar/4852a41fb41616bf8f140d3701673f53?s=80\u0026d=identicon',
web_url: 'http: //localhost:3001/tajuana', web_url: 'http://localhost:3001/tajuana',
}, },
{ {
name: 'Laureen Ritchie', name: 'Laureen Ritchie',
username: 'michaele.will', username: 'michaele.will',
id: 16, id: 16,
state: 'active', state: 'active',
avatar_url: 'http: //www.gravatar.com/avatar/e301827eb03be955c9c172cb9a8e4e8a?s=80\u0026d=identicon', avatar_url: 'https://www.gravatar.com/avatar/e301827eb03be955c9c172cb9a8e4e8a?s=80\u0026d=identicon',
web_url: 'http: //localhost:3001/michaele.will', web_url: 'http://localhost:3001/michaele.will',
}, },
], ],
subscribed: true, subscribed: true,
...@@ -182,7 +182,7 @@ const mockData = { ...@@ -182,7 +182,7 @@ const mockData = {
id: 1, id: 1,
name: 'Administrator', name: 'Administrator',
username: 'root', username: 'root',
avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
}, },
rootPath: '/', rootPath: '/',
fullPath: '/gitlab-org/gitlab-shell', fullPath: '/gitlab-org/gitlab-shell',
...@@ -194,7 +194,7 @@ const mockData = { ...@@ -194,7 +194,7 @@ const mockData = {
human_total_time_spent: null, human_total_time_spent: null,
}, },
user: { user: {
avatar: 'http://gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', avatar: 'https://gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
id: 1, id: 1,
name: 'Administrator', name: 'Administrator',
username: 'root', username: 'root',
......
...@@ -6,14 +6,14 @@ const ASSIGNEE = { ...@@ -6,14 +6,14 @@ const ASSIGNEE = {
id: 2, id: 2,
name: 'gitlab user 2', name: 'gitlab user 2',
username: 'gitlab2', username: 'gitlab2',
avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
}; };
const ANOTHER_ASSINEE = { const ANOTHER_ASSINEE = {
id: 3, id: 3,
name: 'gitlab user 3', name: 'gitlab user 3',
username: 'gitlab3', username: 'gitlab3',
avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
}; };
const PARTICIPANT = { const PARTICIPANT = {
...@@ -38,7 +38,7 @@ describe('Sidebar store', () => { ...@@ -38,7 +38,7 @@ describe('Sidebar store', () => {
id: 1, id: 1,
name: 'Administrator', name: 'Administrator',
username: 'root', username: 'root',
avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
}, },
editable: true, editable: true,
rootPath: '/', rootPath: '/',
......
import Vue from 'vue';
import mrStatusIcon from '~/vue_merge_request_widget/components/mr_widget_status_icon.vue';
import mountComponent from '../../helpers/vue_mount_component_helper';
describe('MR widget status icon component', () => {
let vm;
let Component;
beforeEach(() => {
Component = Vue.extend(mrStatusIcon);
});
afterEach(() => {
vm.$destroy();
});
describe('while loading', () => {
it('renders loading icon', () => {
vm = mountComponent(Component, { status: 'loading' });
expect(vm.$el.querySelector('.mr-widget-icon i').classList).toContain('fa-spinner');
});
});
describe('with status icon', () => {
it('renders ci status icon', () => {
vm = mountComponent(Component, { status: 'failed' });
expect(vm.$el.querySelector('.js-ci-status-icon-failed')).not.toBeNull();
});
});
describe('with disabled button', () => {
it('renders a disabled button', () => {
vm = mountComponent(Component, { status: 'failed', showDisabledButton: true });
expect(vm.$el.querySelector('.js-disabled-merge-button').textContent.trim()).toEqual('Merge');
});
});
describe('without disabled button', () => {
it('does not render a disabled button', () => {
vm = mountComponent(Component, { status: 'failed' });
expect(vm.$el.querySelector('.js-disabled-merge-button')).toBeNull();
});
});
});
import Vue from 'vue';
import mergingComponent from '~/vue_merge_request_widget/components/states/mr_widget_merging';
describe('MRWidgetMerging', () => {
describe('props', () => {
it('should have props', () => {
const { mr } = mergingComponent.props;
expect(mr.type instanceof Object).toBeTruthy();
expect(mr.required).toBeTruthy();
});
});
describe('template', () => {
it('should have correct elements', () => {
const Component = Vue.extend(mergingComponent);
const mr = {
targetBranchPath: '/branch-path',
targetBranch: 'branch',
};
const el = new Component({
el: document.createElement('div'),
propsData: { mr },
}).$el;
expect(el.classList.contains('mr-widget-body')).toBeTruthy();
expect(el.innerText).toContain('This merge request is in the process of being merged');
expect(el.innerText).toContain('changes will be merged into');
expect(el.querySelector('.label-branch a').getAttribute('href')).toEqual(mr.targetBranchPath);
expect(el.querySelector('.label-branch a').textContent).toContain(mr.targetBranch);
});
});
});
import Vue from 'vue';
import mergingComponent from '~/vue_merge_request_widget/components/states/mr_widget_merging.vue';
import mountComponent from '../../../helpers/vue_mount_component_helper';
describe('MRWidgetMerging', () => {
let vm;
beforeEach(() => {
const Component = Vue.extend(mergingComponent);
vm = mountComponent(Component, { mr: {
targetBranchPath: '/branch-path',
targetBranch: 'branch',
} });
});
afterEach(() => {
vm.$destroy();
});
it('renders information about merge request being merged', () => {
expect(
vm.$el.querySelector('.media-body').textContent.trim().replace(/\s\s+/g, ' ').replace(/[\r\n]+/g, ' '),
).toContain('This merge request is in the process of being merged');
});
it('renders branch information', () => {
expect(
vm.$el.querySelector('.mr-info-list').textContent.trim().replace(/\s\s+/g, ' ').replace(/[\r\n]+/g, ' '),
).toEqual('The changes will be merged into branch');
expect(
vm.$el.querySelector('a').getAttribute('href'),
).toEqual('/branch-path');
});
});
...@@ -38,7 +38,7 @@ export default { ...@@ -38,7 +38,7 @@ export default {
"username": "root", "username": "root",
"id": 1, "id": 1,
"state": "active", "state": "active",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"web_url": "http://localhost:3000/root" "web_url": "http://localhost:3000/root"
}, },
"merged_at": "2017-04-07T15:39:25.696Z", "merged_at": "2017-04-07T15:39:25.696Z",
...@@ -50,7 +50,7 @@ export default { ...@@ -50,7 +50,7 @@ export default {
"username": "root", "username": "root",
"id": 1, "id": 1,
"state": "active", "state": "active",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"web_url": "http://localhost:3000/root" "web_url": "http://localhost:3000/root"
}, },
"merge_user": null, "merge_user": null,
...@@ -64,7 +64,7 @@ export default { ...@@ -64,7 +64,7 @@ export default {
"username": "root", "username": "root",
"id": 1, "id": 1,
"state": "active", "state": "active",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"web_url": "http://localhost:3000/root" "web_url": "http://localhost:3000/root"
}, },
"active": false, "active": false,
...@@ -159,10 +159,10 @@ export default { ...@@ -159,10 +159,10 @@ export default {
"username": "root", "username": "root",
"id": 1, "id": 1,
"state": "active", "state": "active",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"web_url": "http://localhost:3000/root" "web_url": "http://localhost:3000/root"
}, },
"author_gravatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "author_gravatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"commit_url": "http://localhost:3000/root/acets-app/commit/104096c51715e12e7ae41f9333e9fa35b73f385d", "commit_url": "http://localhost:3000/root/acets-app/commit/104096c51715e12e7ae41f9333e9fa35b73f385d",
"commit_path": "/root/acets-app/commit/104096c51715e12e7ae41f9333e9fa35b73f385d" "commit_path": "/root/acets-app/commit/104096c51715e12e7ae41f9333e9fa35b73f385d"
}, },
......
...@@ -268,6 +268,21 @@ describe Gitlab::Git::Blob, seed_helper: true do ...@@ -268,6 +268,21 @@ describe Gitlab::Git::Blob, seed_helper: true do
expect(blobs).to all( be_a(Gitlab::Git::Blob) ) expect(blobs).to all( be_a(Gitlab::Git::Blob) )
end end
it 'accepts blob IDs as a lazy enumerator' do
blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id].lazy)
expect(blobs.count).to eq(1)
expect(blobs).to all( be_a(Gitlab::Git::Blob) )
end
it 'handles empty list of IDs gracefully' do
blobs_1 = described_class.batch_lfs_pointers(repository, [].lazy)
blobs_2 = described_class.batch_lfs_pointers(repository, [])
expect(blobs_1).to eq([])
expect(blobs_2).to eq([])
end
it 'silently ignores tree objects' do it 'silently ignores tree objects' do
blobs = described_class.batch_lfs_pointers(repository, [tree_object.oid]) blobs = described_class.batch_lfs_pointers(repository, [tree_object.oid])
......
...@@ -1127,9 +1127,19 @@ describe MergeRequest do ...@@ -1127,9 +1127,19 @@ describe MergeRequest do
end end
end end
context 'when the revert commit is mentioned in a note before the MR was merged' do context 'when the revert commit is mentioned in a note just before the MR was merged' do
before do before do
subject.notes.last.update!(created_at: subject.metrics.merged_at - 1.second) subject.notes.last.update!(created_at: subject.metrics.merged_at - 30.seconds)
end
it 'returns false' do
expect(subject.can_be_reverted?(current_user)).to be_falsey
end
end
context 'when the revert commit is mentioned in a note long before the MR was merged' do
before do
subject.notes.last.update!(created_at: subject.metrics.merged_at - 2.minutes)
end end
it 'returns true' do it 'returns true' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment