Commit 5d8e0d80 authored by Stan Hu's avatar Stan Hu

Merge branch 'ce-to-ee-2018-09-25' into 'master'

CE upstream - 2018-09-25 21:21 UTC

See merge request gitlab-org/gitlab-ee!7486
parents a4ed1e16 13e056f7
<script> <script>
import { mapState, mapGetters } from 'vuex'; import { mapState, mapGetters } from 'vuex';
import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue'; import SkeletonLoading from '@gitlab-org/gitlab-ui/dist/components/base/skeleton_loading';
import IdeTree from './ide_tree.vue'; import IdeTree from './ide_tree.vue';
import ResizablePanel from './resizable_panel.vue'; import ResizablePanel from './resizable_panel.vue';
import ActivityBar from './activity_bar.vue'; import ActivityBar from './activity_bar.vue';
...@@ -13,7 +13,7 @@ import { activityBarViews } from '../constants'; ...@@ -13,7 +13,7 @@ import { activityBarViews } from '../constants';
export default { export default {
components: { components: {
SkeletonLoadingContainer, SkeletonLoading,
ResizablePanel, ResizablePanel,
ActivityBar, ActivityBar,
CommitSection, CommitSection,
...@@ -56,7 +56,7 @@ export default { ...@@ -56,7 +56,7 @@ export default {
:key="n" :key="n"
class="multi-file-loading-container" class="multi-file-loading-container"
> >
<skeleton-loading-container /> <skeleton-loading />
</div> </div>
</div> </div>
</template> </template>
......
<script> <script>
import { mapActions, mapGetters, mapState } from 'vuex'; import { mapActions, mapGetters, mapState } from 'vuex';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue'; import SkeletonLoading from '@gitlab-org/gitlab-ui/dist/components/base/skeleton_loading';
import FileRow from '~/vue_shared/components/file_row.vue'; import FileRow from '~/vue_shared/components/file_row.vue';
import NavDropdown from './nav_dropdown.vue'; import NavDropdown from './nav_dropdown.vue';
import FileRowExtra from './file_row_extra.vue'; import FileRowExtra from './file_row_extra.vue';
...@@ -9,7 +9,7 @@ import FileRowExtra from './file_row_extra.vue'; ...@@ -9,7 +9,7 @@ import FileRowExtra from './file_row_extra.vue';
export default { export default {
components: { components: {
Icon, Icon,
SkeletonLoadingContainer, SkeletonLoading,
NavDropdown, NavDropdown,
FileRow, FileRow,
}, },
...@@ -51,7 +51,7 @@ export default { ...@@ -51,7 +51,7 @@ export default {
:key="n" :key="n"
class="multi-file-loading-container" class="multi-file-loading-container"
> >
<skeleton-loading-container /> <skeleton-loading />
</div> </div>
</template> </template>
<template v-else> <template v-else>
......
<script>
import { mapState } from 'vuex';
import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
export default {
components: {
skeletonLoadingContainer,
},
computed: {
...mapState([
'leftPanelCollapsed',
]),
},
};
</script>
<template>
<tr
class="loading-file"
aria-label="Loading files"
>
<td class="multi-file-table-col-name">
<skeleton-loading-container
:small="true"
/>
</td>
<template v-if="!leftPanelCollapsed">
<td class="d-none d-sm-none d-md-block">
<skeleton-loading-container
:small="true"
/>
</td>
<td class="d-none d-sm-block">
<skeleton-loading-container
:small="true"
class="animation-container-right"
/>
</td>
</template>
</tr>
</template>
...@@ -16,7 +16,7 @@ import 'vendor/jquery.atwho'; ...@@ -16,7 +16,7 @@ import 'vendor/jquery.atwho';
import AjaxCache from '~/lib/utils/ajax_cache'; import AjaxCache from '~/lib/utils/ajax_cache';
import Vue from 'vue'; import Vue from 'vue';
import syntaxHighlight from '~/syntax_highlight'; import syntaxHighlight from '~/syntax_highlight';
import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue'; import SkeletonLoading from '@gitlab-org/gitlab-ui/dist/components/base/skeleton_loading';
import axios from './lib/utils/axios_utils'; import axios from './lib/utils/axios_utils';
import { getLocationHash } from './lib/utils/url_utility'; import { getLocationHash } from './lib/utils/url_utility';
import Flash from './flash'; import Flash from './flash';
...@@ -1293,10 +1293,10 @@ export default class Notes { ...@@ -1293,10 +1293,10 @@ export default class Notes {
new Vue({ new Vue({
el, el,
components: { components: {
SkeletonLoadingContainer, SkeletonLoading,
}, },
render(createElement) { render(createElement) {
return createElement('skeleton-loading-container'); return createElement('skeleton-loading');
}, },
}); });
} }
......
...@@ -3,13 +3,13 @@ import { mapState, mapActions } from 'vuex'; ...@@ -3,13 +3,13 @@ import { mapState, mapActions } from 'vuex';
import imageDiffHelper from '~/image_diff/helpers/index'; import imageDiffHelper from '~/image_diff/helpers/index';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import DiffFileHeader from '~/diffs/components/diff_file_header.vue'; import DiffFileHeader from '~/diffs/components/diff_file_header.vue';
import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue'; import SkeletonLoading from '@gitlab-org/gitlab-ui/dist/components/base/skeleton_loading';
import { trimFirstCharOfLineContent } from '~/diffs/store/utils'; import { trimFirstCharOfLineContent } from '~/diffs/store/utils';
export default { export default {
components: { components: {
DiffFileHeader, DiffFileHeader,
SkeletonLoadingContainer, SkeletonLoading,
}, },
props: { props: {
discussion: { discussion: {
...@@ -142,7 +142,7 @@ export default { ...@@ -142,7 +142,7 @@ export default {
class="line_content js-success-lazy-load" class="line_content js-success-lazy-load"
> >
<span></span> <span></span>
<skeleton-loading-container /> <skeleton-loading />
<span></span> <span></span>
</td> </td>
</tr> </tr>
......
...@@ -28,11 +28,17 @@ export default { ...@@ -28,11 +28,17 @@ export default {
return this.mr.divergedCommitsCount > 0; return this.mr.divergedCommitsCount > 0;
}, },
commitsBehindText() { commitsBehindText() {
return sprintf(s__('mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch'), { return sprintf(
commitsBehindLinkStart: `<a href="${_.escape(this.mr.targetBranchPath)}">`, s__(
commitsBehind: n__('%d commit behind', '%d commits behind', this.mr.divergedCommitsCount), 'mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch',
commitsBehindLinkEnd: '</a>', ),
}, false); {
commitsBehindLinkStart: `<a href="${_.escape(this.mr.targetBranchPath)}">`,
commitsBehind: n__('%d commit behind', '%d commits behind', this.mr.divergedCommitsCount),
commitsBehindLinkEnd: '</a>',
},
false,
);
}, },
branchNameClipboardData() { branchNameClipboardData() {
// This supports code in app/assets/javascripts/copy_to_clipboard.js that // This supports code in app/assets/javascripts/copy_to_clipboard.js that
...@@ -45,17 +51,24 @@ export default { ...@@ -45,17 +51,24 @@ export default {
}, },
webIdePath() { webIdePath() {
if (this.mr.canPushToSourceBranch) { if (this.mr.canPushToSourceBranch) {
return mergeUrlParams({ return mergeUrlParams(
target_project: this.mr.sourceProjectFullPath !== this.mr.targetProjectFullPath ? {
this.mr.targetProjectFullPath : '', target_project:
}, webIDEUrl(`/${this.mr.sourceProjectFullPath}/merge_requests/${this.mr.iid}`)); this.mr.sourceProjectFullPath !== this.mr.targetProjectFullPath
? this.mr.targetProjectFullPath
: '',
},
webIDEUrl(`/${this.mr.sourceProjectFullPath}/merge_requests/${this.mr.iid}`),
);
} }
return null; return null;
}, },
ideButtonTitle() { ideButtonTitle() {
return !this.mr.canPushToSourceBranch return !this.mr.canPushToSourceBranch
? s__('mrWidget|You are not allowed to edit this project directly. Please fork to make changes.') ? s__(
'mrWidget|You are not allowed to edit this project directly. Please fork to make changes.',
)
: ''; : '';
}, },
}, },
...@@ -104,37 +117,34 @@ export default { ...@@ -104,37 +117,34 @@ export default {
<div <div
v-if="mr.isOpen" v-if="mr.isOpen"
class="branch-actions" class="branch-actions d-flex"
> >
<span <a
v-if="!mr.sourceBranchRemoved"
v-tooltip v-tooltip
:href="webIdePath"
:title="ideButtonTitle" :title="ideButtonTitle"
:class="{ disabled: !mr.canPushToSourceBranch }"
class="btn btn-default js-web-ide d-none d-md-inline-block append-right-8"
data-placement="bottom" data-placement="bottom"
tabindex="0" tabindex="0"
role="button"
> >
<a {{ s__("mrWidget|Open in Web IDE") }}
v-if="!mr.sourceBranchRemoved" </a>
:href="webIdePath"
:class="{ disabled: !mr.canPushToSourceBranch }"
class="btn btn-default inline js-web-ide d-none d-md-inline-block"
role="button"
>
{{ s__("mrWidget|Open in Web IDE") }}
</a>
</span>
<button <button
:disabled="mr.sourceBranchRemoved" :disabled="mr.sourceBranchRemoved"
data-target="#modal_merge_info" data-target="#modal_merge_info"
data-toggle="modal" data-toggle="modal"
class="btn btn-default inline js-check-out-branch" class="btn btn-default js-check-out-branch append-right-default"
type="button" type="button"
> >
{{ s__("mrWidget|Check out branch") }} {{ s__("mrWidget|Check out branch") }}
</button> </button>
<span class="dropdown prepend-left-10"> <span class="dropdown">
<button <button
type="button" type="button"
class="btn inline dropdown-toggle" class="btn dropdown-toggle"
data-toggle="dropdown" data-toggle="dropdown"
aria-label="Download as" aria-label="Download as"
aria-haspopup="true" aria-haspopup="true"
......
...@@ -2,14 +2,14 @@ ...@@ -2,14 +2,14 @@
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale'; import { __ } from '~/locale';
import $ from 'jquery'; import $ from 'jquery';
import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue'; import SkeletonLoading from '@gitlab-org/gitlab-ui/dist/components/base/skeleton_loading';
const { CancelToken } = axios; const { CancelToken } = axios;
let axiosSource; let axiosSource;
export default { export default {
components: { components: {
SkeletonLoadingContainer, SkeletonLoading,
}, },
props: { props: {
content: { content: {
...@@ -81,7 +81,7 @@ export default { ...@@ -81,7 +81,7 @@ export default {
<div <div
ref="markdown-preview" ref="markdown-preview"
class="md md-previewer"> class="md md-previewer">
<skeleton-loading-container v-if="isLoading" /> <skeleton-loading v-if="isLoading" />
<div <div
v-else v-else
v-html="previewContent"> v-html="previewContent">
......
<script>
import SkeletonLoading from '@gitlab-org/gitlab-ui/dist/components/base/skeleton_loading';
export default {
name: 'SkeletonNote',
components: {
SkeletonLoading,
},
};
</script>
<template> <template>
<li class="timeline-entry note"> <li class="timeline-entry note">
<div class="timeline-entry-inner"> <div class="timeline-entry-inner">
...@@ -6,20 +17,9 @@ ...@@ -6,20 +17,9 @@
<div class="timeline-content"> <div class="timeline-content">
<div class="note-header"></div> <div class="note-header"></div>
<div class="note-body"> <div class="note-body">
<skeleton-loading-container /> <skeleton-loading />
</div> </div>
</div> </div>
</div> </div>
</li> </li>
</template> </template>
<script>
import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
export default {
name: 'SkeletonNote',
components: {
skeletonLoadingContainer,
},
};
</script>
<script>
export default {
props: {
small: {
type: Boolean,
required: false,
default: false,
},
lines: {
type: Number,
required: false,
default: 3,
},
},
computed: {
lineClasses() {
return new Array(this.lines).fill().map((_, i) => `skeleton-line-${i + 1}`);
},
},
};
</script>
<template>
<div
:class="{
'animation-container-small': small,
}"
class="animation-container"
>
<div
v-for="(css, index) in lineClasses"
:key="index"
:class="css"
>
</div>
</div>
</template>
...@@ -12,6 +12,7 @@ class ApplicationController < ActionController::Base ...@@ -12,6 +12,7 @@ class ApplicationController < ActionController::Base
include WorkhorseHelper include WorkhorseHelper
include EnforcesTwoFactorAuthentication include EnforcesTwoFactorAuthentication
include WithPerformanceBar include WithPerformanceBar
include InvalidUTF8ErrorHandler
before_action :authenticate_sessionless_user! before_action :authenticate_sessionless_user!
before_action :authenticate_user! before_action :authenticate_user!
......
module InvalidUTF8ErrorHandler
extend ActiveSupport::Concern
included do
rescue_from ArgumentError, with: :handle_invalid_utf8
end
private
def handle_invalid_utf8(error)
if error.message == "invalid byte sequence in UTF-8"
render_412
else
raise(error)
end
end
def render_412
respond_to do |format|
format.html { render "errors/precondition_failed", layout: "errors", status: 412 }
format.js { render json: { error: 'Invalid UTF-8' }, status: :precondition_failed, content_type: 'application/json' }
format.any { head :precondition_failed }
end
end
end
# frozen_string_literal: true
class Dashboard::ApplicationController < ApplicationController class Dashboard::ApplicationController < ApplicationController
include ControllerWithCrossProjectAccessCheck include ControllerWithCrossProjectAccessCheck
......
# frozen_string_literal: true
class Dashboard::GroupsController < Dashboard::ApplicationController class Dashboard::GroupsController < Dashboard::ApplicationController
include GroupTree include GroupTree
......
# frozen_string_literal: true
class Dashboard::LabelsController < Dashboard::ApplicationController class Dashboard::LabelsController < Dashboard::ApplicationController
def index def index
respond_to do |format| respond_to do |format|
......
# frozen_string_literal: true
class Dashboard::MilestonesController < Dashboard::ApplicationController class Dashboard::MilestonesController < Dashboard::ApplicationController
include MilestoneActions include MilestoneActions
......
# frozen_string_literal: true
class Dashboard::ProjectsController < Dashboard::ApplicationController class Dashboard::ProjectsController < Dashboard::ApplicationController
include ParamsBackwardCompatibility include ParamsBackwardCompatibility
include RendersMemberAccess include RendersMemberAccess
......
# frozen_string_literal: true
class Dashboard::SnippetsController < Dashboard::ApplicationController class Dashboard::SnippetsController < Dashboard::ApplicationController
skip_cross_project_access_check :index skip_cross_project_access_check :index
......
# frozen_string_literal: true
class Dashboard::TodosController < Dashboard::ApplicationController class Dashboard::TodosController < Dashboard::ApplicationController
include ActionView::Helpers::NumberHelper include ActionView::Helpers::NumberHelper
......
# frozen_string_literal: true
class Explore::ApplicationController < ApplicationController class Explore::ApplicationController < ApplicationController
skip_before_action :authenticate_user! skip_before_action :authenticate_user!
......
# frozen_string_literal: true
class Explore::GroupsController < Explore::ApplicationController class Explore::GroupsController < Explore::ApplicationController
include GroupTree include GroupTree
......
# frozen_string_literal: true
class Explore::ProjectsController < Explore::ApplicationController class Explore::ProjectsController < Explore::ApplicationController
include ParamsBackwardCompatibility include ParamsBackwardCompatibility
include RendersMemberAccess include RendersMemberAccess
......
# frozen_string_literal: true
class Explore::SnippetsController < Explore::ApplicationController class Explore::SnippetsController < Explore::ApplicationController
def index def index
@snippets = SnippetsFinder.new(current_user).execute @snippets = SnippetsFinder.new(current_user).execute
......
# frozen_string_literal: true
module GoogleApi module GoogleApi
class AuthorizationsController < ApplicationController class AuthorizationsController < ApplicationController
def callback def callback
......
# frozen_string_literal: true
class Groups::ApplicationController < ApplicationController class Groups::ApplicationController < ApplicationController
prepend EE::Groups::ApplicationController prepend EE::Groups::ApplicationController
include RoutableActions include RoutableActions
......
# frozen_string_literal: true
class Groups::AvatarsController < Groups::ApplicationController class Groups::AvatarsController < Groups::ApplicationController
before_action :authorize_admin_group! before_action :authorize_admin_group!
......
# frozen_string_literal: true
class Groups::BoardsController < Groups::ApplicationController class Groups::BoardsController < Groups::ApplicationController
prepend EE::Boards::BoardsController prepend EE::Boards::BoardsController
include BoardsResponses include BoardsResponses
......
# frozen_string_literal: true
module Groups module Groups
class ChildrenController < Groups::ApplicationController class ChildrenController < Groups::ApplicationController
before_action :group before_action :group
......
# frozen_string_literal: true
class Groups::GroupMembersController < Groups::ApplicationController class Groups::GroupMembersController < Groups::ApplicationController
prepend EE::Groups::GroupMembersController prepend EE::Groups::GroupMembersController
......
# frozen_string_literal: true
class Groups::LabelsController < Groups::ApplicationController class Groups::LabelsController < Groups::ApplicationController
include ToggleSubscriptionAction include ToggleSubscriptionAction
......
# frozen_string_literal: true
class Groups::MilestonesController < Groups::ApplicationController class Groups::MilestonesController < Groups::ApplicationController
prepend EE::Groups::MilestonesController prepend EE::Groups::MilestonesController
......
# frozen_string_literal: true
class Groups::RunnersController < Groups::ApplicationController class Groups::RunnersController < Groups::ApplicationController
# Proper policies should be implemented per # Proper policies should be implemented per
# https://gitlab.com/gitlab-org/gitlab-ce/issues/45894 # https://gitlab.com/gitlab-org/gitlab-ce/issues/45894
......
# frozen_string_literal: true
module Groups module Groups
module Settings module Settings
class CiCdController < Groups::ApplicationController class CiCdController < Groups::ApplicationController
......
# frozen_string_literal: true
module Groups module Groups
class SharedProjectsController < Groups::ApplicationController class SharedProjectsController < Groups::ApplicationController
respond_to :json respond_to :json
......
# frozen_string_literal: true
class Groups::UploadsController < Groups::ApplicationController class Groups::UploadsController < Groups::ApplicationController
include UploadsActions include UploadsActions
include WorkhorseRequest include WorkhorseRequest
......
# frozen_string_literal: true
module Groups module Groups
class VariablesController < Groups::ApplicationController class VariablesController < Groups::ApplicationController
before_action :authorize_admin_build! before_action :authorize_admin_build!
......
# frozen_string_literal: true
class Import::BaseController < ApplicationController class Import::BaseController < ApplicationController
private private
......
# frozen_string_literal: true
class Import::BitbucketController < Import::BaseController class Import::BitbucketController < Import::BaseController
before_action :verify_bitbucket_import_enabled before_action :verify_bitbucket_import_enabled
before_action :bitbucket_auth, except: :callback before_action :bitbucket_auth, except: :callback
......
# frozen_string_literal: true
class Import::FogbugzController < Import::BaseController class Import::FogbugzController < Import::BaseController
before_action :verify_fogbugz_import_enabled before_action :verify_fogbugz_import_enabled
before_action :user_map, only: [:new_user_map, :create_user_map] before_action :user_map, only: [:new_user_map, :create_user_map]
......
# frozen_string_literal: true
class Import::GiteaController < Import::GithubController class Import::GiteaController < Import::GithubController
def new def new
if session[access_token_key].present? && session[host_key].present? if session[access_token_key].present? && session[host_key].present?
......
# frozen_string_literal: true
class Import::GithubController < Import::BaseController class Import::GithubController < Import::BaseController
prepend ::EE::Import::GithubController prepend ::EE::Import::GithubController
......
# frozen_string_literal: true
class Import::GitlabController < Import::BaseController class Import::GitlabController < Import::BaseController
MAX_PROJECT_PAGES = 15 MAX_PROJECT_PAGES = 15
PER_PAGE_PROJECTS = 100 PER_PAGE_PROJECTS = 100
......
# frozen_string_literal: true
class Import::GitlabProjectsController < Import::BaseController class Import::GitlabProjectsController < Import::BaseController
before_action :whitelist_query_limiting, only: [:create] before_action :whitelist_query_limiting, only: [:create]
before_action :verify_gitlab_project_import_enabled before_action :verify_gitlab_project_import_enabled
......
# frozen_string_literal: true
class Import::GoogleCodeController < Import::BaseController class Import::GoogleCodeController < Import::BaseController
before_action :verify_google_code_import_enabled before_action :verify_google_code_import_enabled
before_action :user_map, only: [:new_user_map, :create_user_map] before_action :user_map, only: [:new_user_map, :create_user_map]
......
# frozen_string_literal: true
class Import::ManifestController < Import::BaseController class Import::ManifestController < Import::BaseController
before_action :whitelist_query_limiting, only: [:create] before_action :whitelist_query_limiting, only: [:create]
before_action :verify_import_enabled before_action :verify_import_enabled
......
# frozen_string_literal: true
class Ldap::OmniauthCallbacksController < OmniauthCallbacksController class Ldap::OmniauthCallbacksController < OmniauthCallbacksController
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
prepend EE::OmniauthCallbacksController prepend EE::OmniauthCallbacksController
......
# frozen_string_literal: true
class Oauth::ApplicationsController < Doorkeeper::ApplicationsController class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
include Gitlab::GonHelper include Gitlab::GonHelper
include Gitlab::Allowable include Gitlab::Allowable
......
# frozen_string_literal: true
class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
layout 'profile' layout 'profile'
......
# frozen_string_literal: true
class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicationsController class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicationsController
include PageLayoutHelper include PageLayoutHelper
......
# frozen_string_literal: true
class Profiles::AccountsController < Profiles::ApplicationController class Profiles::AccountsController < Profiles::ApplicationController
include AuthHelper include AuthHelper
......
# frozen_string_literal: true
class Profiles::ActiveSessionsController < Profiles::ApplicationController class Profiles::ActiveSessionsController < Profiles::ApplicationController
def index def index
@sessions = ActiveSession.list(current_user) @sessions = ActiveSession.list(current_user)
......
# frozen_string_literal: true
class Profiles::ApplicationController < ApplicationController class Profiles::ApplicationController < ApplicationController
layout 'profile' layout 'profile'
end end
# frozen_string_literal: true
class Profiles::AvatarsController < Profiles::ApplicationController class Profiles::AvatarsController < Profiles::ApplicationController
def destroy def destroy
@user = current_user @user = current_user
......
# frozen_string_literal: true
class Profiles::ChatNamesController < Profiles::ApplicationController class Profiles::ChatNamesController < Profiles::ApplicationController
before_action :chat_name_token, only: [:new] before_action :chat_name_token, only: [:new]
before_action :chat_name_params, only: [:new, :create, :deny] before_action :chat_name_params, only: [:new, :create, :deny]
......
# frozen_string_literal: true
class Profiles::EmailsController < Profiles::ApplicationController class Profiles::EmailsController < Profiles::ApplicationController
before_action :find_email, only: [:destroy, :resend_confirmation_instructions] before_action :find_email, only: [:destroy, :resend_confirmation_instructions]
......
# frozen_string_literal: true
class Profiles::GpgKeysController < Profiles::ApplicationController class Profiles::GpgKeysController < Profiles::ApplicationController
before_action :set_gpg_key, only: [:destroy, :revoke] before_action :set_gpg_key, only: [:destroy, :revoke]
......
# frozen_string_literal: true
class Profiles::KeysController < Profiles::ApplicationController class Profiles::KeysController < Profiles::ApplicationController
skip_before_action :authenticate_user!, only: [:get_keys] skip_before_action :authenticate_user!, only: [:get_keys]
......
# frozen_string_literal: true
class Profiles::NotificationsController < Profiles::ApplicationController class Profiles::NotificationsController < Profiles::ApplicationController
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def show def show
......
# frozen_string_literal: true
class Profiles::PasswordsController < Profiles::ApplicationController class Profiles::PasswordsController < Profiles::ApplicationController
skip_before_action :check_password_expiration, only: [:new, :create] skip_before_action :check_password_expiration, only: [:new, :create]
skip_before_action :check_two_factor_requirement, only: [:new, :create] skip_before_action :check_two_factor_requirement, only: [:new, :create]
......
# frozen_string_literal: true
class Profiles::PersonalAccessTokensController < Profiles::ApplicationController class Profiles::PersonalAccessTokensController < Profiles::ApplicationController
def index def index
set_index_vars set_index_vars
......
# frozen_string_literal: true
class Profiles::PreferencesController < Profiles::ApplicationController class Profiles::PreferencesController < Profiles::ApplicationController
before_action :user before_action :user
......
# frozen_string_literal: true
class Profiles::TwoFactorAuthsController < Profiles::ApplicationController class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
skip_before_action :check_two_factor_requirement skip_before_action :check_two_factor_requirement
...@@ -30,7 +32,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController ...@@ -30,7 +32,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
unless two_factor_grace_period_expired? unless two_factor_grace_period_expired?
grace_period_deadline = current_user.otp_grace_period_started_at + two_factor_grace_period.hours grace_period_deadline = current_user.otp_grace_period_started_at + two_factor_grace_period.hours
flash.now[:alert] << " You need to do this before #{l(grace_period_deadline)}." flash.now[:alert] = flash.now[:alert] + " You need to do this before #{l(grace_period_deadline)}."
end end
end end
......
# frozen_string_literal: true
class Profiles::U2fRegistrationsController < Profiles::ApplicationController class Profiles::U2fRegistrationsController < Profiles::ApplicationController
def destroy def destroy
u2f_registration = current_user.u2f_registrations.find(params[:id]) u2f_registration = current_user.u2f_registrations.find(params[:id])
......
...@@ -13,4 +13,4 @@ ...@@ -13,4 +13,4 @@
= link_to 'Standard', '#login-pane', class: 'nav-link qa-standard-tab', 'data-toggle' => 'tab' = link_to 'Standard', '#login-pane', class: 'nav-link qa-standard-tab', 'data-toggle' => 'tab'
- if allow_signup? - if allow_signup?
%li.nav-item %li.nav-item
= link_to 'Register', '#register-pane', class: 'nav-link', 'data-toggle' => 'tab' = link_to 'Register', '#register-pane', class: 'nav-link qa-register-tab', 'data-toggle' => 'tab'
- content_for(:title, 'Encoding Error')
%img{ :alt => "GitLab Logo", :src => image_path('logo.svg') }
%h1
412
.container
%h3 Precondition failed
%hr
%p Page can't be loaded because of invalid parameters.
---
title: Render 412 when invalid UTF-8 parameters are passed to controller
merge_request:
author:
type: other
---
title: Enable more frozen string in app/controllers/
merge_request:
author: gfyoung
type: performance
---
title: Fix merge request header margins
merge_request: 21878
author:
type: other
---
title: Add clean-up phase for ScheduleDiffFilesDeletion migration
merge_request: 21734
author:
type: other
# frozen_string_literal: true
class ConsumeRemainingDiffFilesDeletionJobs < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
MIGRATION = 'ScheduleDiffFilesDeletion'.freeze
TMP_INDEX = 'tmp_partial_diff_id_with_files_index'.freeze
def up
# Perform any ongoing background migration that might still be scheduled.
Gitlab::BackgroundMigration.steal(MIGRATION)
remove_concurrent_index_by_name(:merge_request_diffs, TMP_INDEX)
end
def down
add_concurrent_index(:merge_request_diffs, :id, where: "(state NOT IN ('without_files', 'empty'))", name: TMP_INDEX)
end
end
...@@ -158,7 +158,9 @@ Find it under your project's **Repository > Graph**. ...@@ -158,7 +158,9 @@ Find it under your project's **Repository > Graph**.
## Repository Languages ## Repository Languages
For the default branch of each repository, GitLab will determine what programming languages For the default branch of each repository, GitLab will determine what programming languages
were used and display this on the projects pages. were used and display this on the projects pages. If this information is missing, it will
be added after updating the default branch on the project. This process can take up to 5
minutes.
![Repository Languages bar](img/repository_languages.png) ![Repository Languages bar](img/repository_languages.png)
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
}, },
"dependencies": { "dependencies": {
"@gitlab-org/gitlab-svgs": "^1.29.0", "@gitlab-org/gitlab-svgs": "^1.29.0",
"@gitlab-org/gitlab-ui": "^1.5.1", "@gitlab-org/gitlab-ui": "^1.7.0",
"autosize": "^4.0.0", "autosize": "^4.0.0",
"axios": "^0.17.1", "axios": "^0.17.1",
"babel-core": "^6.26.3", "babel-core": "^6.26.3",
......
...@@ -13,8 +13,43 @@ module QA ...@@ -13,8 +13,43 @@ module QA
product(:user) { |factory| factory.user } product(:user) { |factory| factory.user }
def visit_project_with_retry
# The user intermittently fails to stay signed in after visiting the
# project page. The new user is registered and then signs in and a
# screenshot shows that signing in was successful. Then the project
# page is visited but a screenshot shows the user is no longer signed
# in. It's difficult to reproduce locally but GDK logs don't seem to
# show anything unexpected. This method attempts to work around the
# problem and capture data to help troubleshoot.
Capybara::Screenshot.screenshot_and_save_page
start = Time.now
while Time.now - start < 20
push.project.visit!
puts "Visited project page"
Capybara::Screenshot.screenshot_and_save_page
return if Page::Menu::Main.act { has_personal_area?(wait: 0) }
puts "Not signed in. Attempting to sign in again."
Capybara::Screenshot.screenshot_and_save_page
Page::Menu::Main.act { sign_out }
Page::Main::Login.perform do |login|
login.sign_in_using_credentials(user)
end
end
raise "Failed to load project page and stay logged in"
end
def fabricate! def fabricate!
push.project.visit! visit_project_with_retry
Page::Project::Show.act { fork_project } Page::Project::Show.act { fork_project }
Page::Project::Fork::New.perform do |fork_new| Page::Project::Fork::New.perform do |fork_new|
......
...@@ -37,7 +37,10 @@ module QA ...@@ -37,7 +37,10 @@ module QA
product(:password) { |factory| factory.password } product(:password) { |factory| factory.password }
def fabricate! def fabricate!
Page::Menu::Main.perform { |main| main.sign_out } # Don't try to log-out if we're not logged-in
if Page::Menu::Main.act { has_personal_area?(wait: 0) }
Page::Menu::Main.perform { |main| main.sign_out }
end
if credentials_given? if credentials_given?
Page::Main::Login.perform do |login| Page::Main::Login.perform do |login|
......
...@@ -76,6 +76,10 @@ module QA ...@@ -76,6 +76,10 @@ module QA
find_element(name).set(content) find_element(name).set(content)
end end
def has_element?(name)
has_css?(element_selector_css(name))
end
def within_element(name) def within_element(name)
page.within(element_selector_css(name)) do page.within(element_selector_css(name)) do
yield yield
......
...@@ -23,6 +23,7 @@ module QA ...@@ -23,6 +23,7 @@ module QA
view 'app/views/devise/shared/_tabs_ldap.html.haml' do view 'app/views/devise/shared/_tabs_ldap.html.haml' do
element :ldap_tab element :ldap_tab
element :standard_tab element :standard_tab
element :register_tab
end end
view 'app/views/devise/shared/_tabs_normal.html.haml' do view 'app/views/devise/shared/_tabs_normal.html.haml' do
...@@ -35,7 +36,7 @@ module QA ...@@ -35,7 +36,7 @@ module QA
# we need to wait for the instance to start. That said, in some cases # we need to wait for the instance to start. That said, in some cases
# we are already logged-in so we check both cases here. # we are already logged-in so we check both cases here.
wait(max: 500) do wait(max: 500) do
page.has_css?('.login-page') || has_css?('.login-page') ||
Page::Menu::Main.act { has_personal_area?(wait: 0) } Page::Menu::Main.act { has_personal_area?(wait: 0) }
end end
end end
...@@ -78,12 +79,28 @@ module QA ...@@ -78,12 +79,28 @@ module QA
'/users/sign_in' '/users/sign_in'
end end
def has_sign_in_tab?
has_element?(:sign_in_tab)
end
def has_ldap_tab?
has_element?(:ldap_tab)
end
def has_standard_tab?
has_element?(:standard_tab)
end
def sign_in_tab? def sign_in_tab?
page.has_button?('Sign in') has_css?(".active", text: 'Sign in')
end end
def ldap_tab? def ldap_tab?
page.has_link?('LDAP') has_css?(".active", text: 'LDAP')
end
def standard_tab?
has_css?(".active", text: 'Standard')
end end
def switch_to_sign_in_tab def switch_to_sign_in_tab
...@@ -113,8 +130,8 @@ module QA ...@@ -113,8 +130,8 @@ module QA
end end
def sign_in_using_gitlab_credentials(user) def sign_in_using_gitlab_credentials(user)
switch_to_sign_in_tab unless sign_in_tab? switch_to_sign_in_tab if has_sign_in_tab?
switch_to_standard_tab if ldap_tab? switch_to_standard_tab if has_standard_tab?
fill_element :login_field, user.username fill_element :login_field, user.username
fill_element :password_field, user.password fill_element :password_field, user.password
...@@ -122,7 +139,7 @@ module QA ...@@ -122,7 +139,7 @@ module QA
end end
def set_initial_password_if_present def set_initial_password_if_present
return unless page.has_content?('Change your password') return unless has_content?('Change your password')
fill_element :password_field, Runtime::User.password fill_element :password_field, Runtime::User.password
fill_element :password_confirmation, Runtime::User.password fill_element :password_confirmation, Runtime::User.password
......
...@@ -19,7 +19,7 @@ module QA ...@@ -19,7 +19,7 @@ module QA
fill_in :new_user_password, with: user.password fill_in :new_user_password, with: user.password
click_button 'Register' click_button 'Register'
Page::Menu::Main.act { has_personal_area? } Page::Menu::Main.act { assert_has_personal_area }
end end
end end
end end
......
...@@ -61,8 +61,13 @@ module QA ...@@ -61,8 +61,13 @@ module QA
end end
def has_personal_area?(wait: Capybara.default_max_wait_time) def has_personal_area?(wait: Capybara.default_max_wait_time)
# No need to wait, either we're logged-in, or not. using_wait_time(wait) do
using_wait_time(wait) { page.has_selector?('.qa-user-avatar') } page.has_selector?(element_selector_css(:user_avatar))
end
end
def assert_has_personal_area
raise "Failed to sign in" unless has_personal_area?
end end
private private
......
# frozen_string_literal: true
module QA
shared_examples 'registration and login' do
it 'user registers and logs in' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Factory::Resource::User.fabricate!
# TODO, since `Signed in successfully` message was removed
# this is the only way to tell if user is signed in correctly.
#
Page::Menu::Main.perform do |menu|
expect(menu).to have_personal_area
end
end
end
context :manage do
describe 'standard' do
it_behaves_like 'registration and login'
end
end
context :manage, :orchestrated, :ldap do
describe 'while LDAP is enabled' do
it_behaves_like 'registration and login'
end
end
end
...@@ -694,4 +694,38 @@ describe ApplicationController do ...@@ -694,4 +694,38 @@ describe ApplicationController do
expect(response).to have_gitlab_http_status(403) expect(response).to have_gitlab_http_status(403)
end end
end end
context 'when invalid UTF-8 parameters are received' do
controller(described_class) do
def index
params[:text].split(' ')
render json: :ok
end
end
before do
sign_in user
end
context 'html' do
it 'renders 412' do
get :index, text: "hi \255"
expect(response).to have_gitlab_http_status(412)
expect(response).to render_template :precondition_failed
end
end
context 'js' do
it 'renders 412' do
get :index, text: "hi \255", format: :js
json_response = JSON.parse(response.body)
expect(response).to have_gitlab_http_status(412)
expect(json_response['error']).to eq('Invalid UTF-8')
end
end
end
end end
import Vue from 'vue';
import store from '~/ide/stores';
import repoLoadingFile from '~/ide/components/repo_loading_file.vue';
import { resetStore } from '../helpers';
describe('RepoLoadingFile', () => {
let vm;
function createComponent() {
const RepoLoadingFile = Vue.extend(repoLoadingFile);
return new RepoLoadingFile({
store,
}).$mount();
}
function assertLines(lines) {
lines.forEach((line, n) => {
const index = n + 1;
expect(line.classList.contains(`skeleton-line-${index}`)).toBeTruthy();
});
}
function assertColumns(columns) {
columns.forEach(column => {
const container = column.querySelector('.animation-container');
const lines = [...container.querySelectorAll(':scope > div')];
expect(container).toBeTruthy();
expect(lines.length).toEqual(3);
assertLines(lines);
});
}
afterEach(() => {
vm.$destroy();
resetStore(vm.$store);
});
it('renders 3 columns of animated LoC', () => {
vm = createComponent();
const columns = [...vm.$el.querySelectorAll('td')];
expect(columns.length).toEqual(3);
assertColumns(columns);
});
it('renders 1 column of animated LoC if isMini', done => {
vm = createComponent();
vm.$store.state.leftPanelCollapsed = true;
vm.$store.state.openFiles.push('test');
vm.$nextTick(() => {
const columns = [...vm.$el.querySelectorAll('td')];
expect(columns.length).toEqual(1);
assertColumns(columns);
done();
});
});
});
import Vue from 'vue';
import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('Skeleton loading container', () => {
let vm;
beforeEach(() => {
const component = Vue.extend(skeletonLoadingContainer);
vm = mountComponent(component);
});
afterEach(() => {
vm.$destroy();
});
it('renders 3 skeleton lines by default', () => {
expect(vm.$el.querySelector('.skeleton-line-3')).not.toBeNull();
});
it('renders in full mode by default', () => {
expect(vm.$el.classList.contains('animation-container-small')).toBeFalsy();
});
describe('small', () => {
beforeEach((done) => {
vm.small = true;
Vue.nextTick(done);
});
it('renders in small mode', () => {
expect(vm.$el.classList.contains('animation-container-small')).toBeTruthy();
});
});
describe('lines', () => {
beforeEach((done) => {
vm.lines = 5;
Vue.nextTick(done);
});
it('renders 5 lines', () => {
expect(vm.$el.querySelector('.skeleton-line-5')).not.toBeNull();
expect(vm.$el.querySelector('.skeleton-line-6')).toBeNull();
});
});
});
This diff is collapsed.
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