Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
632d073475
commit
c25c2a508b
|
|
@ -19,6 +19,7 @@ export default {
|
|||
scope: __('Scope'),
|
||||
scopeDescription: __('Issues must match this scope to appear in this list.'),
|
||||
selected: __('Selected'),
|
||||
requiredFieldFeedback: __('This field is required.'),
|
||||
},
|
||||
components: {
|
||||
GlButton,
|
||||
|
|
@ -55,12 +56,21 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
searchValue: '',
|
||||
selectedIdValid: true,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
toggleClassList() {
|
||||
return `gl-max-w-full gl-display-flex gl-align-items-center gl-text-trunate ${
|
||||
this.selectedIdValid ? '' : 'gl-inset-border-1-red-400!'
|
||||
}`;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selectedId(val) {
|
||||
if (val) {
|
||||
this.$refs.dropdown.hide(true);
|
||||
this.selectedIdValid = true;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -74,6 +84,13 @@ export default {
|
|||
this.$emit('filter-items', '');
|
||||
this.$emit('hide');
|
||||
},
|
||||
onSubmit() {
|
||||
if (!this.selectedId) {
|
||||
this.selectedIdValid = false;
|
||||
} else {
|
||||
this.$emit('add-list');
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -103,11 +120,16 @@ export default {
|
|||
|
||||
<slot name="select-list-type"></slot>
|
||||
|
||||
<gl-form-group class="gl-px-5 lg-mb-3 gl-max-w-full" :label="searchLabel">
|
||||
<gl-form-group
|
||||
class="gl-px-5 lg-mb-3 gl-max-w-full"
|
||||
:label="searchLabel"
|
||||
:state="selectedIdValid"
|
||||
:invalid-feedback="$options.i18n.requiredFieldFeedback"
|
||||
>
|
||||
<gl-dropdown
|
||||
ref="dropdown"
|
||||
class="gl-mb-3 gl-max-w-full"
|
||||
toggle-class="gl-max-w-full gl-display-flex gl-align-items-center gl-text-trunate"
|
||||
:toggle-class="toggleClassList"
|
||||
boundary="viewport"
|
||||
@shown="setFocus"
|
||||
@hide="onHide"
|
||||
|
|
@ -147,10 +169,9 @@ export default {
|
|||
<div class="gl-display-flex gl-mb-4">
|
||||
<gl-button
|
||||
data-testid="addNewColumnButton"
|
||||
:disabled="!selectedId"
|
||||
variant="confirm"
|
||||
class="gl-mr-3 gl-ml-4"
|
||||
@click="$emit('add-list')"
|
||||
@click="onSubmit"
|
||||
>{{ $options.i18n.add }}</gl-button
|
||||
>
|
||||
<gl-button data-testid="cancelAddNewColumn" @click="setAddColumnFormVisibility(false)">{{
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ export default {
|
|||
<div
|
||||
v-else-if="!isLoading && showTests"
|
||||
ref="container"
|
||||
class="position-relative"
|
||||
class="gl-relative"
|
||||
data-testid="tests-detail"
|
||||
>
|
||||
<transition
|
||||
|
|
@ -82,13 +82,13 @@ export default {
|
|||
@before-enter="beforeEnterTransition"
|
||||
@after-leave="afterLeaveTransition"
|
||||
>
|
||||
<div v-if="showSuite" key="detail" class="w-100 slide-enter-to-element">
|
||||
<div v-if="showSuite" key="detail" class="gl-w-full slide-enter-to-element">
|
||||
<test-summary :report="getSelectedSuite" show-back @on-back-click="summaryBackClick" />
|
||||
|
||||
<test-suite-table />
|
||||
</div>
|
||||
|
||||
<div v-else key="summary" class="w-100 slide-enter-from-element">
|
||||
<div v-else key="summary" class="gl-w-full slide-enter-from-element">
|
||||
<test-summary :report="testReports" />
|
||||
|
||||
<test-summary-table @row-click="summaryTableRowClick" />
|
||||
|
|
|
|||
|
|
@ -80,7 +80,10 @@ export default {
|
|||
<h4>{{ heading }}</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div role="row" class="gl-responsive-table-row table-row-header font-weight-bold fgray">
|
||||
<div
|
||||
role="row"
|
||||
class="gl-responsive-table-row table-row-header gl-font-weight-bold gl-fill-gray-700"
|
||||
>
|
||||
<div role="rowheader" class="table-section section-20">
|
||||
{{ __('Suite') }}
|
||||
</div>
|
||||
|
|
@ -104,7 +107,7 @@ export default {
|
|||
<div
|
||||
v-for="(testCase, index) in getSuiteTests"
|
||||
:key="index"
|
||||
class="gl-responsive-table-row rounded align-items-md-start"
|
||||
class="gl-responsive-table-row gl-rounded-base gl-align-items-flex-start"
|
||||
data-testid="test-case-row"
|
||||
>
|
||||
<div class="table-section section-20 section-wrap">
|
||||
|
|
@ -142,11 +145,8 @@ export default {
|
|||
|
||||
<div class="table-section section-10 section-wrap">
|
||||
<div role="rowheader" class="table-mobile-header">{{ __('Status') }}</div>
|
||||
<div class="table-mobile-content text-center">
|
||||
<div
|
||||
class="ci-status-icon d-flex align-items-center justify-content-end justify-content-md-center"
|
||||
:class="`ci-status-icon-${testCase.status}`"
|
||||
>
|
||||
<div class="table-mobile-content gl-md-display-flex gl-justify-content-center">
|
||||
<div class="ci-status-icon" :class="`ci-status-icon-${testCase.status}`">
|
||||
<gl-icon :size="24" :name="testCase.icon" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -156,7 +156,7 @@ export default {
|
|||
<div role="rowheader" class="table-mobile-header">
|
||||
{{ __('Duration') }}
|
||||
</div>
|
||||
<div class="table-mobile-content pr-sm-1">
|
||||
<div class="table-mobile-content gl-sm-pr-2">
|
||||
{{ testCase.formattedTime }}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -65,58 +65,53 @@ export default {
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<div class="row">
|
||||
<div class="col-12 d-flex gl-mt-3 align-items-center">
|
||||
<gl-button
|
||||
v-if="showBack"
|
||||
size="small"
|
||||
class="gl-mr-3 js-back-button"
|
||||
icon="chevron-lg-left"
|
||||
:aria-label="__('Go back')"
|
||||
@click="onBackClick"
|
||||
/>
|
||||
<div class="gl-w-full gl-display-flex gl-mt-3 gl-align-items-center">
|
||||
<gl-button
|
||||
v-if="showBack"
|
||||
size="small"
|
||||
class="gl-mr-3 js-back-button"
|
||||
icon="chevron-lg-left"
|
||||
:aria-label="__('Go back')"
|
||||
@click="onBackClick"
|
||||
/>
|
||||
|
||||
<h4>{{ heading }}</h4>
|
||||
</div>
|
||||
<h4>{{ heading }}</h4>
|
||||
</div>
|
||||
|
||||
<div class="row mt-2">
|
||||
<div class="col-4 col-md">
|
||||
<span class="js-total-tests">{{
|
||||
<div
|
||||
class="gl-display-flex gl-flex-direction-column gl-md-flex-direction-row gl-w-full gl-mt-3"
|
||||
>
|
||||
<div class="gl-display-flex gl-justify-content-space-between gl-flex-basis-half">
|
||||
<span class="js-total-tests gl-flex-grow-1">{{
|
||||
sprintf(s__('TestReports|%{count} tests'), { count: report.total_count })
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div class="col-4 col-md text-center text-md-center">
|
||||
<span class="js-failed-tests">{{
|
||||
<span class="js-failed-tests gl-flex-grow-1">{{
|
||||
sprintf(s__('TestReports|%{count} failures'), { count: report.failed_count })
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div class="col-4 col-md text-right text-md-center">
|
||||
<span class="js-errored-tests">{{
|
||||
sprintf(s__('TestReports|%{count} errors'), { count: report.error_count })
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div class="col-6 mt-3 col-md mt-md-0 text-md-center">
|
||||
<span class="js-success-rate">{{
|
||||
<div class="gl-display-flex gl-justify-content-space-between gl-flex-grow-1">
|
||||
<div class="gl-display-none gl-md-display-block gl-flex-grow-1"></div>
|
||||
<span class="js-success-rate gl-flex-grow-1">{{
|
||||
sprintf(s__('TestReports|%{rate}%{sign} success rate'), {
|
||||
rate: successPercentage,
|
||||
sign: '%',
|
||||
})
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div class="col-6 mt-3 col-md mt-md-0 text-right">
|
||||
<span class="js-duration">{{ formattedDuration }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<gl-progress-bar :value="successPercentage" :variant="progressBarVariant" height="10px" />
|
||||
</div>
|
||||
</div>
|
||||
<gl-progress-bar
|
||||
class="gl-mt-5"
|
||||
:value="successPercentage"
|
||||
:variant="progressBarVariant"
|
||||
height="10px"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -34,33 +34,31 @@ export default {
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<div class="row gl-mt-3">
|
||||
<div class="col-12">
|
||||
<h4>{{ heading }}</h4>
|
||||
</div>
|
||||
<div class="gl-mt-5">
|
||||
<h4>{{ heading }}</h4>
|
||||
</div>
|
||||
|
||||
<div v-if="hasSuites" class="test-reports-table gl-mb-3 js-test-suites-table">
|
||||
<div role="row" class="gl-responsive-table-row table-row-header font-weight-bold">
|
||||
<div role="rowheader" class="table-section section-25 pl-3">
|
||||
<div v-if="hasSuites" class="js-test-suites-table">
|
||||
<div role="row" class="gl-responsive-table-row table-row-header gl-font-weight-bold">
|
||||
<div role="rowheader" class="table-section section-25 gl-pl-5">
|
||||
{{ __('Job') }}
|
||||
</div>
|
||||
<div role="rowheader" class="table-section section-25">
|
||||
{{ __('Duration') }}
|
||||
</div>
|
||||
<div role="rowheader" class="table-section section-10 text-center">
|
||||
<div role="rowheader" class="table-section section-10 gl-text-center">
|
||||
{{ __('Failed') }}
|
||||
</div>
|
||||
<div role="rowheader" class="table-section section-10 text-center">
|
||||
<div role="rowheader" class="table-section section-10 gl-text-center">
|
||||
{{ __('Errors'), }}
|
||||
</div>
|
||||
<div role="rowheader" class="table-section section-10 text-center">
|
||||
<div role="rowheader" class="table-section section-10 gl-text-center">
|
||||
{{ __('Skipped'), }}
|
||||
</div>
|
||||
<div role="rowheader" class="table-section section-10 text-center">
|
||||
<div role="rowheader" class="table-section section-10 gl-text-center">
|
||||
{{ __('Passed'), }}
|
||||
</div>
|
||||
<div role="rowheader" class="table-section section-10 pr-3 text-right">
|
||||
<div role="rowheader" class="table-section section-10 gl-pr-5 gl-text-right">
|
||||
{{ __('Total') }}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -69,17 +67,17 @@ export default {
|
|||
v-for="(testSuite, index) in getTestSuites"
|
||||
:key="index"
|
||||
role="row"
|
||||
class="gl-responsive-table-row test-reports-summary-row rounded js-suite-row"
|
||||
class="gl-responsive-table-row gl-rounded-base js-suite-row"
|
||||
:class="{
|
||||
'gl-responsive-table-row-clickable cursor-pointer': !testSuite.suite_error,
|
||||
'gl-responsive-table-row-clickable gl-cursor-pointer': !testSuite.suite_error,
|
||||
}"
|
||||
@click="tableRowClick(index)"
|
||||
>
|
||||
<div class="table-section section-25">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
<div role="rowheader" class="table-mobile-header gl-font-weight-bold">
|
||||
{{ __('Suite') }}
|
||||
</div>
|
||||
<div class="table-mobile-content underline cgray pl-3">
|
||||
<div class="table-mobile-content underline gl-text-gray-900 gl-pl-5">
|
||||
{{ testSuite.name }}
|
||||
<gl-icon
|
||||
v-if="testSuite.suite_error"
|
||||
|
|
@ -93,44 +91,44 @@ export default {
|
|||
</div>
|
||||
|
||||
<div class="table-section section-25">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
<div role="rowheader" class="table-mobile-header gl-font-weight-bold">
|
||||
{{ __('Duration') }}
|
||||
</div>
|
||||
<div class="table-mobile-content text-md-left">
|
||||
<div class="table-mobile-content gl-text-left">
|
||||
{{ testSuite.formattedTime }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-10 text-center">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
<div class="table-section section-10 gl-text-center">
|
||||
<div role="rowheader" class="table-mobile-header gl-font-weight-bold">
|
||||
{{ __('Failed') }}
|
||||
</div>
|
||||
<div class="table-mobile-content">{{ testSuite.failed_count }}</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-10 text-center">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
<div class="table-section section-10 gl-text-center">
|
||||
<div role="rowheader" class="table-mobile-header gl-font-weight-bold">
|
||||
{{ __('Errors') }}
|
||||
</div>
|
||||
<div class="table-mobile-content">{{ testSuite.error_count }}</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-10 text-center">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
<div class="table-section section-10 gl-text-center">
|
||||
<div role="rowheader" class="table-mobile-header gl-font-weight-bold">
|
||||
{{ __('Skipped') }}
|
||||
</div>
|
||||
<div class="table-mobile-content">{{ testSuite.skipped_count }}</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-10 text-center">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
<div class="table-section section-10 gl-text-center">
|
||||
<div role="rowheader" class="table-mobile-header gl-font-weight-bold">
|
||||
{{ __('Passed') }}
|
||||
</div>
|
||||
<div class="table-mobile-content">{{ testSuite.success_count }}</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-10 text-right pr-md-3">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
<div class="table-section section-10 gl-text-right pr-md-3">
|
||||
<div role="rowheader" class="table-mobile-header gl-font-weight-bold">
|
||||
{{ __('Total') }}
|
||||
</div>
|
||||
<div class="table-mobile-content">{{ testSuite.total_count }}</div>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ fragment ListItemShared on CiRunner {
|
|||
runnerType
|
||||
shortSha
|
||||
version
|
||||
revision
|
||||
ipAddress
|
||||
active
|
||||
locked
|
||||
|
|
|
|||
|
|
@ -225,12 +225,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.test-reports-table {
|
||||
.build-log {
|
||||
@include build-log();
|
||||
}
|
||||
}
|
||||
|
||||
.progress-bar.bg-primary {
|
||||
background-color: var(--blue-500, $blue-500) !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,14 +57,14 @@ class WebHook < ApplicationRecord
|
|||
!temporarily_disabled? && !permanently_disabled?
|
||||
end
|
||||
|
||||
def temporarily_disabled?(ignore_flag: false)
|
||||
return false unless ignore_flag || web_hooks_disable_failed?
|
||||
def temporarily_disabled?
|
||||
return false unless web_hooks_disable_failed?
|
||||
|
||||
disabled_until.present? && disabled_until >= Time.current
|
||||
end
|
||||
|
||||
def permanently_disabled?(ignore_flag: false)
|
||||
return false unless ignore_flag || web_hooks_disable_failed?
|
||||
def permanently_disabled?
|
||||
return false unless web_hooks_disable_failed?
|
||||
|
||||
recent_failures > FAILURE_THRESHOLD
|
||||
end
|
||||
|
|
@ -126,13 +126,6 @@ class WebHook < ApplicationRecord
|
|||
save(validate: false)
|
||||
end
|
||||
|
||||
def active_state(ignore_flag: false)
|
||||
return :permanently_disabled if permanently_disabled?(ignore_flag: ignore_flag)
|
||||
return :temporarily_disabled if temporarily_disabled?(ignore_flag: ignore_flag)
|
||||
|
||||
:enabled
|
||||
end
|
||||
|
||||
# @return [Boolean] Whether or not the WebHook is currently throttled.
|
||||
def rate_limited?
|
||||
rate_limiter.rate_limited?
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ module WebHooks
|
|||
@hook = hook
|
||||
@log_data = log_data.transform_keys(&:to_sym)
|
||||
@response_category = response_category
|
||||
@prev_state = hook.active_state(ignore_flag: true)
|
||||
end
|
||||
|
||||
def execute
|
||||
|
|
@ -43,36 +42,12 @@ module WebHooks
|
|||
hook.failed!
|
||||
end
|
||||
|
||||
log_state_change
|
||||
hook.update_last_failure
|
||||
end
|
||||
rescue Gitlab::ExclusiveLeaseHelpers::FailedToObtainLockError
|
||||
raise if raise_lock_error?
|
||||
end
|
||||
|
||||
def log_state_change
|
||||
new_state = hook.active_state(ignore_flag: true)
|
||||
|
||||
return if @prev_state == new_state
|
||||
|
||||
Gitlab::AuthLogger.info(
|
||||
message: 'WebHook change active_state',
|
||||
# identification
|
||||
hook_id: hook.id,
|
||||
hook_type: hook.type,
|
||||
project_id: hook.project_id,
|
||||
group_id: hook.group_id,
|
||||
# relevant data
|
||||
prev_state: @prev_state,
|
||||
new_state: new_state,
|
||||
duration: log_data[:execution_duration],
|
||||
response_status: log_data[:response_status],
|
||||
recent_hook_failures: hook.recent_failures,
|
||||
# context
|
||||
**Gitlab::ApplicationContext.current
|
||||
)
|
||||
end
|
||||
|
||||
def lock_name
|
||||
"web_hooks:update_hook_failure_state:#{hook.id}"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -34,9 +34,10 @@
|
|||
= display_issuable_type
|
||||
|
||||
- unless current_controller?('conflicts')
|
||||
- if current_user && moved_mr_sidebar_enabled? && !@merge_request.merged?
|
||||
%li.gl-new-dropdown-divider
|
||||
%hr.dropdown-divider
|
||||
- if current_user && moved_mr_sidebar_enabled?
|
||||
- if !@merge_request.merged?
|
||||
%li.gl-new-dropdown-divider
|
||||
%hr.dropdown-divider
|
||||
%li.gl-new-dropdown-item.js-sidebar-subscriptions-entry-point
|
||||
- unless issuable_author_is_current_user(@merge_request)
|
||||
%li.gl-new-dropdown-item
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@
|
|||
gitlab-com: true
|
||||
packages: [Free, Premium, Ultimate]
|
||||
url: https://docs.gitlab.com/ee/ci/runners/configure_runners.html#artifact-attestation
|
||||
image_url: https://www.youtube.com/embed/MlIdqrDgI8U
|
||||
image_url: https://img.youtube.com/vi/MlIdqrDgI8U/hqdefault.jpg
|
||||
published_at: 2022-06-22
|
||||
release: 15.1
|
||||
- title: "Link to included CI/CD configuration from the pipeline editor"
|
||||
|
|
@ -54,6 +54,6 @@
|
|||
gitlab-com: true
|
||||
packages: [Free, Premium, Ultimate]
|
||||
url: https://docs.gitlab.com/ee/ci/pipeline_editor/
|
||||
image_url: https://www.youtube.com/embed/7BNDUYfY_ok
|
||||
image_url: https://img.youtube.com/vi/7BNDUYfY_ok/hqdefault.jpg
|
||||
published_at: 2022-06-22
|
||||
release: 15.1
|
||||
|
|
|
|||
|
|
@ -253,6 +253,10 @@ then `artifacts:reports:dependency_scanning` must be set to `depscan.json`.
|
|||
Following the POSIX exit code standard, the scanner exits with 0 for success and any number from 1 to 255 for anything else.
|
||||
Success also includes the case when vulnerabilities are found.
|
||||
|
||||
When a CI job fails, security report results are not ingested by GitLab, even if the job
|
||||
[allows failure](../../ci/yaml/#allow_failure). The report artifacts are still uploaded to GitLab and available
|
||||
for [download in the pipeline security tab](../../user/application_security/vulnerability_report/pipeline.md#download-security-scan-outputs).
|
||||
|
||||
When executing a scanning job using the [Docker-in-Docker privileged mode](../../user/application_security/sast/index.md#requirements),
|
||||
we reserve the following standard exit codes.
|
||||
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ RSpec.describe 'Admin Appearance' do
|
|||
|
||||
context 'Profile page with custom profile image guidelines' do
|
||||
before do
|
||||
sign_in(create(:admin))
|
||||
sign_in(admin)
|
||||
gitlab_enable_admin_mode_sign_in(admin)
|
||||
visit admin_application_settings_appearances_path
|
||||
fill_in 'appearance_profile_image_guidelines', with: 'Custom profile image guidelines, please :smile:!'
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ RSpec.describe 'Profile account page', :js do
|
|||
it 'deletes user', :js, :sidekiq_might_not_need_inline do
|
||||
click_button 'Delete account'
|
||||
|
||||
fill_in 'password', with: '12345678'
|
||||
fill_in 'password', with: user.password
|
||||
|
||||
page.within '.modal' do
|
||||
click_button 'Delete account'
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ RSpec.describe 'Profile > Password' do
|
|||
end
|
||||
|
||||
context 'Password authentication enabled' do
|
||||
let(:new_password) { User.random_password }
|
||||
let(:user) { create(:user, password_automatically_set: true) }
|
||||
|
||||
before do
|
||||
|
|
@ -23,7 +24,7 @@ RSpec.describe 'Profile > Password' do
|
|||
context 'User with password automatically set' do
|
||||
describe 'User puts different passwords in the field and in the confirmation' do
|
||||
it 'shows an error message' do
|
||||
fill_passwords('mypassword', 'mypassword2')
|
||||
fill_passwords(new_password, "#{new_password}2")
|
||||
|
||||
page.within('.gl-alert-danger') do
|
||||
expect(page).to have_content("Password confirmation doesn't match Password")
|
||||
|
|
@ -31,7 +32,7 @@ RSpec.describe 'Profile > Password' do
|
|||
end
|
||||
|
||||
it 'does not contain the current password field after an error' do
|
||||
fill_passwords('mypassword', 'mypassword2')
|
||||
fill_passwords(new_password, "#{new_password}2")
|
||||
|
||||
expect(page).to have_no_field('user[current_password]')
|
||||
end
|
||||
|
|
@ -39,7 +40,7 @@ RSpec.describe 'Profile > Password' do
|
|||
|
||||
describe 'User puts the same passwords in the field and in the confirmation' do
|
||||
it 'shows a success message' do
|
||||
fill_passwords('mypassword', 'mypassword')
|
||||
fill_passwords(new_password, new_password)
|
||||
|
||||
page.within('[data-testid="alert-info"]') do
|
||||
expect(page).to have_content('Password was successfully updated. Please sign in again.')
|
||||
|
|
@ -79,7 +80,7 @@ RSpec.describe 'Profile > Password' do
|
|||
end
|
||||
|
||||
context 'Change password' do
|
||||
let(:new_password) { '22233344' }
|
||||
let(:new_password) { User.random_password }
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
|
|
@ -156,6 +157,8 @@ RSpec.describe 'Profile > Password' do
|
|||
end
|
||||
|
||||
context 'when password is expired' do
|
||||
let(:new_password) { User.random_password }
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
|
||||
|
|
@ -170,8 +173,8 @@ RSpec.describe 'Profile > Password' do
|
|||
expect(page).to have_current_path new_profile_password_path, ignore_query: true
|
||||
|
||||
fill_in :user_password, with: user.password
|
||||
fill_in :user_new_password, with: '12345678'
|
||||
fill_in :user_password_confirmation, with: '12345678'
|
||||
fill_in :user_new_password, with: new_password
|
||||
fill_in :user_password_confirmation, with: new_password
|
||||
click_button 'Set new password'
|
||||
|
||||
expect(page).to have_current_path new_user_session_path, ignore_query: true
|
||||
|
|
|
|||
|
|
@ -49,15 +49,15 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do
|
|||
expect(page).to have_current_path edit_user_password_path, ignore_query: true
|
||||
expect(page).to have_content('Please create a password for your new account.')
|
||||
|
||||
fill_in 'user_password', with: 'password'
|
||||
fill_in 'user_password_confirmation', with: 'password'
|
||||
fill_in 'user_password', with: user.password
|
||||
fill_in 'user_password_confirmation', with: user.password
|
||||
click_button 'Change your password'
|
||||
|
||||
expect(page).to have_current_path new_user_session_path, ignore_query: true
|
||||
expect(page).to have_content(I18n.t('devise.passwords.updated_not_active'))
|
||||
|
||||
fill_in 'user_login', with: user.username
|
||||
fill_in 'user_password', with: 'password'
|
||||
fill_in 'user_password', with: user.password
|
||||
click_button 'Sign in'
|
||||
|
||||
expect_single_session_with_authenticated_ttl
|
||||
|
|
@ -231,7 +231,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do
|
|||
end
|
||||
|
||||
it 'does not allow sign-in if the user password is updated before entering a one-time code' do
|
||||
user.update!(password: 'new_password')
|
||||
user.update!(password: User.random_password)
|
||||
|
||||
enter_code(user.current_otp)
|
||||
|
||||
|
|
@ -468,7 +468,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do
|
|||
visit new_user_session_path
|
||||
|
||||
fill_in 'user_login', with: user.email
|
||||
fill_in 'user_password', with: '12345678'
|
||||
fill_in 'user_password', with: user.password
|
||||
click_button 'Sign in'
|
||||
|
||||
expect(page).to have_current_path(new_profile_password_path, ignore_query: true)
|
||||
|
|
@ -788,7 +788,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do
|
|||
visit new_user_session_path
|
||||
|
||||
fill_in 'user_login', with: user.email
|
||||
fill_in 'user_password', with: '12345678'
|
||||
fill_in 'user_password', with: user.password
|
||||
|
||||
click_button 'Sign in'
|
||||
|
||||
|
|
@ -809,7 +809,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do
|
|||
visit new_user_session_path
|
||||
|
||||
fill_in 'user_login', with: user.email
|
||||
fill_in 'user_password', with: '12345678'
|
||||
fill_in 'user_password', with: user.password
|
||||
|
||||
click_button 'Sign in'
|
||||
|
||||
|
|
@ -830,7 +830,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do
|
|||
visit new_user_session_path
|
||||
|
||||
fill_in 'user_login', with: user.email
|
||||
fill_in 'user_password', with: '12345678'
|
||||
fill_in 'user_password', with: user.password
|
||||
|
||||
click_button 'Sign in'
|
||||
|
||||
|
|
@ -873,7 +873,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do
|
|||
visit new_user_session_path
|
||||
|
||||
fill_in 'user_login', with: user.email
|
||||
fill_in 'user_password', with: '12345678'
|
||||
fill_in 'user_password', with: user.password
|
||||
click_button 'Sign in'
|
||||
|
||||
fill_in 'user_otp_attempt', with: user.reload.current_otp
|
||||
|
|
@ -899,7 +899,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do
|
|||
visit new_user_session_path
|
||||
|
||||
fill_in 'user_login', with: user.email
|
||||
fill_in 'user_password', with: '12345678'
|
||||
fill_in 'user_password', with: user.password
|
||||
click_button 'Sign in'
|
||||
|
||||
expect_to_be_on_terms_page
|
||||
|
|
@ -907,9 +907,11 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do
|
|||
|
||||
expect(page).to have_current_path(new_profile_password_path, ignore_query: true)
|
||||
|
||||
fill_in 'user_password', with: '12345678'
|
||||
fill_in 'user_new_password', with: 'new password'
|
||||
fill_in 'user_password_confirmation', with: 'new password'
|
||||
new_password = User.random_password
|
||||
|
||||
fill_in 'user_password', with: user.password
|
||||
fill_in 'user_new_password', with: new_password
|
||||
fill_in 'user_password_confirmation', with: new_password
|
||||
click_button 'Set new password'
|
||||
|
||||
expect(page).to have_content('Password successfully changed')
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ describe('Board card layout', () => {
|
|||
|
||||
const formTitle = () => wrapper.findByTestId('board-add-column-form-title').text();
|
||||
const findSearchInput = () => wrapper.find(GlSearchBoxByType);
|
||||
const findSearchLabel = () => wrapper.find(GlFormGroup);
|
||||
const findSearchLabelFormGroup = () => wrapper.find(GlFormGroup);
|
||||
const cancelButton = () => wrapper.findByTestId('cancelAddNewColumn');
|
||||
const submitButton = () => wrapper.findByTestId('addNewColumnButton');
|
||||
const findDropdown = () => wrapper.findComponent(GlDropdown);
|
||||
|
|
@ -121,10 +121,17 @@ describe('Board card layout', () => {
|
|||
|
||||
mountComponent(props);
|
||||
|
||||
expect(findSearchLabel().attributes('label')).toEqual(props.searchLabel);
|
||||
expect(findSearchLabelFormGroup().attributes('label')).toEqual(props.searchLabel);
|
||||
expect(findSearchInput().attributes('placeholder')).toEqual(props.searchPlaceholder);
|
||||
});
|
||||
|
||||
it('does not show the dropdown as invalid by default', () => {
|
||||
mountComponent();
|
||||
|
||||
expect(findSearchLabelFormGroup().attributes('state')).toBe('true');
|
||||
expect(findDropdown().props('toggleClass')).not.toContain('gl-inset-border-1-red-400!');
|
||||
});
|
||||
|
||||
it('emits filter event on input', () => {
|
||||
mountComponent();
|
||||
|
||||
|
|
@ -137,13 +144,13 @@ describe('Board card layout', () => {
|
|||
});
|
||||
|
||||
describe('Add list button', () => {
|
||||
it('is disabled if no item is selected', () => {
|
||||
it('is enabled by default', () => {
|
||||
mountComponent();
|
||||
|
||||
expect(submitButton().props('disabled')).toBe(true);
|
||||
expect(submitButton().props('disabled')).toBe(false);
|
||||
});
|
||||
|
||||
it('emits add-list event on click', () => {
|
||||
it('emits add-list event on click when an ID is selected', () => {
|
||||
mountComponent({
|
||||
selectedId: mockLabelList.label.id,
|
||||
});
|
||||
|
|
@ -152,5 +159,16 @@ describe('Board card layout', () => {
|
|||
|
||||
expect(wrapper.emitted('add-list')).toEqual([[]]);
|
||||
});
|
||||
|
||||
it('does not emit the add-list event on click and shows the dropdown as invalid when no ID is selected', async () => {
|
||||
mountComponent();
|
||||
|
||||
await submitButton().vm.$emit('click');
|
||||
|
||||
expect(findSearchLabelFormGroup().attributes('state')).toBeUndefined();
|
||||
expect(findDropdown().props('toggleClass')).toContain('gl-inset-border-1-red-400!');
|
||||
|
||||
expect(wrapper.emitted('add-list')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ RSpec.describe 'Runner (JavaScript fixtures)' do
|
|||
let_it_be(:project) { create(:project, :repository, :public) }
|
||||
let_it_be(:project_2) { create(:project, :repository, :public) }
|
||||
|
||||
let_it_be(:instance_runner) { create(:ci_runner, :instance, version: '1.0.0', revision: '123', description: 'Instance runner', ip_address: '127.0.0.1') }
|
||||
let_it_be(:group_runner) { create(:ci_runner, :group, groups: [group], active: false, version: '2.0.0', revision: '456', description: 'Group runner', ip_address: '127.0.0.1') }
|
||||
let_it_be(:group_runner_2) { create(:ci_runner, :group, groups: [group], active: false, version: '2.0.0', revision: '456', description: 'Group runner 2', ip_address: '127.0.0.1') }
|
||||
let_it_be(:project_runner) { create(:ci_runner, :project, projects: [project, project_2], active: false, version: '2.0.0', revision: '456', description: 'Project runner', ip_address: '127.0.0.1') }
|
||||
let_it_be(:instance_runner) { create(:ci_runner, :instance, version: '1.0.0', description: 'Instance runner', ip_address: '127.0.0.1') }
|
||||
let_it_be(:group_runner) { create(:ci_runner, :group, groups: [group], active: false, version: '2.0.0', description: 'Group runner', ip_address: '127.0.0.1') }
|
||||
let_it_be(:group_runner_2) { create(:ci_runner, :group, groups: [group], active: false, version: '2.0.0', description: 'Group runner 2', ip_address: '127.0.0.1') }
|
||||
let_it_be(:project_runner) { create(:ci_runner, :project, projects: [project, project_2], active: false, version: '2.0.0', description: 'Project runner', ip_address: '127.0.0.1') }
|
||||
let_it_be(:build) { create(:ci_build, runner: instance_runner) }
|
||||
|
||||
query_path = 'runner/graphql/'
|
||||
|
|
|
|||
|
|
@ -1,86 +1,82 @@
|
|||
import Vue, { nextTick } from 'vue';
|
||||
import { createComponentWithStore } from 'helpers/vue_mount_component_helper';
|
||||
import { GlBadge } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import ActivityBar from '~/ide/components/activity_bar.vue';
|
||||
import { leftSidebarViews } from '~/ide/constants';
|
||||
import { createStore } from '~/ide/stores';
|
||||
|
||||
describe('IDE activity bar', () => {
|
||||
const Component = Vue.extend(ActivityBar);
|
||||
let vm;
|
||||
describe('IDE ActivityBar component', () => {
|
||||
let wrapper;
|
||||
let store;
|
||||
|
||||
const findChangesBadge = () => vm.$el.querySelector('.badge');
|
||||
const findChangesBadge = () => wrapper.findComponent(GlBadge);
|
||||
|
||||
beforeEach(() => {
|
||||
const mountComponent = (state) => {
|
||||
store = createStore();
|
||||
|
||||
Vue.set(store.state.projects, 'abcproject', {
|
||||
web_url: 'testing',
|
||||
store.replaceState({
|
||||
...store.state,
|
||||
projects: { abcproject: { web_url: 'testing' } },
|
||||
currentProjectId: 'abcproject',
|
||||
...state,
|
||||
});
|
||||
Vue.set(store.state, 'currentProjectId', 'abcproject');
|
||||
|
||||
vm = createComponentWithStore(Component, store);
|
||||
});
|
||||
wrapper = shallowMount(ActivityBar, { store });
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
vm.$destroy();
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe('updateActivityBarView', () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(vm, 'updateActivityBarView').mockImplementation(() => {});
|
||||
|
||||
vm.$mount();
|
||||
mountComponent();
|
||||
jest.spyOn(wrapper.vm, 'updateActivityBarView').mockImplementation(() => {});
|
||||
});
|
||||
|
||||
it('calls updateActivityBarView with edit value on click', () => {
|
||||
vm.$el.querySelector('.js-ide-edit-mode').click();
|
||||
wrapper.find('.js-ide-edit-mode').trigger('click');
|
||||
|
||||
expect(vm.updateActivityBarView).toHaveBeenCalledWith(leftSidebarViews.edit.name);
|
||||
expect(wrapper.vm.updateActivityBarView).toHaveBeenCalledWith(leftSidebarViews.edit.name);
|
||||
});
|
||||
|
||||
it('calls updateActivityBarView with commit value on click', () => {
|
||||
vm.$el.querySelector('.js-ide-commit-mode').click();
|
||||
wrapper.find('.js-ide-commit-mode').trigger('click');
|
||||
|
||||
expect(vm.updateActivityBarView).toHaveBeenCalledWith(leftSidebarViews.commit.name);
|
||||
expect(wrapper.vm.updateActivityBarView).toHaveBeenCalledWith(leftSidebarViews.commit.name);
|
||||
});
|
||||
|
||||
it('calls updateActivityBarView with review value on click', () => {
|
||||
vm.$el.querySelector('.js-ide-review-mode').click();
|
||||
wrapper.find('.js-ide-review-mode').trigger('click');
|
||||
|
||||
expect(vm.updateActivityBarView).toHaveBeenCalledWith(leftSidebarViews.review.name);
|
||||
expect(wrapper.vm.updateActivityBarView).toHaveBeenCalledWith(leftSidebarViews.review.name);
|
||||
});
|
||||
});
|
||||
|
||||
describe('active item', () => {
|
||||
beforeEach(() => {
|
||||
vm.$mount();
|
||||
});
|
||||
|
||||
it('sets edit item active', () => {
|
||||
expect(vm.$el.querySelector('.js-ide-edit-mode').classList).toContain('active');
|
||||
mountComponent();
|
||||
|
||||
expect(wrapper.find('.js-ide-edit-mode').classes()).toContain('active');
|
||||
});
|
||||
|
||||
it('sets commit item active', async () => {
|
||||
vm.$store.state.currentActivityView = leftSidebarViews.commit.name;
|
||||
it('sets commit item active', () => {
|
||||
mountComponent({ currentActivityView: leftSidebarViews.commit.name });
|
||||
|
||||
await nextTick();
|
||||
expect(vm.$el.querySelector('.js-ide-commit-mode').classList).toContain('active');
|
||||
expect(wrapper.find('.js-ide-commit-mode').classes()).toContain('active');
|
||||
});
|
||||
});
|
||||
|
||||
describe('changes badge', () => {
|
||||
it('is rendered when files are staged', () => {
|
||||
store.state.stagedFiles = [{ path: '/path/to/file' }];
|
||||
vm.$mount();
|
||||
mountComponent({ stagedFiles: [{ path: '/path/to/file' }] });
|
||||
|
||||
expect(findChangesBadge()).toBeTruthy();
|
||||
expect(findChangesBadge().textContent.trim()).toBe('1');
|
||||
expect(findChangesBadge().exists()).toBe(true);
|
||||
expect(findChangesBadge().text()).toBe('1');
|
||||
});
|
||||
|
||||
it('is not rendered when no changes are present', () => {
|
||||
vm.$mount();
|
||||
expect(findChangesBadge()).toBeFalsy();
|
||||
mountComponent();
|
||||
|
||||
expect(findChangesBadge().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { mount } from '@vue/test-utils';
|
||||
import _ from 'lodash';
|
||||
import Vue, { nextTick } from 'vue';
|
||||
import { TEST_HOST } from 'helpers/test_constants';
|
||||
import { createComponentWithStore } from 'helpers/vue_mount_component_helper';
|
||||
import IdeStatusBar from '~/ide/components/ide_status_bar.vue';
|
||||
import IdeStatusMR from '~/ide/components/ide_status_mr.vue';
|
||||
import { rightSidebarViews } from '~/ide/constants';
|
||||
import { createStore } from '~/ide/stores';
|
||||
import { projectData } from '../mock_data';
|
||||
|
|
@ -13,42 +13,48 @@ const TEST_MERGE_REQUEST_URL = `${TEST_HOST}merge-requests/${TEST_MERGE_REQUEST_
|
|||
|
||||
jest.mock('~/lib/utils/poll');
|
||||
|
||||
describe('ideStatusBar', () => {
|
||||
let store;
|
||||
let vm;
|
||||
describe('IdeStatusBar component', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = () => {
|
||||
vm = createComponentWithStore(Vue.extend(IdeStatusBar), store).$mount();
|
||||
const findMRStatus = () => wrapper.findComponent(IdeStatusMR);
|
||||
|
||||
const mountComponent = (state = {}) => {
|
||||
const store = createStore();
|
||||
store.replaceState({
|
||||
...store.state,
|
||||
currentBranchId: 'main',
|
||||
currentProjectId: TEST_PROJECT_ID,
|
||||
projects: {
|
||||
...store.state.projects,
|
||||
[TEST_PROJECT_ID]: _.clone(projectData),
|
||||
},
|
||||
...state,
|
||||
});
|
||||
|
||||
wrapper = mount(IdeStatusBar, { store });
|
||||
};
|
||||
const findMRStatus = () => vm.$el.querySelector('.js-ide-status-mr');
|
||||
|
||||
beforeEach(() => {
|
||||
store = createStore();
|
||||
store.state.currentProjectId = TEST_PROJECT_ID;
|
||||
store.state.projects[TEST_PROJECT_ID] = _.clone(projectData);
|
||||
store.state.currentBranchId = 'main';
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vm.$destroy();
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe('default', () => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('triggers a setInterval', () => {
|
||||
expect(vm.intervalId).not.toBe(null);
|
||||
mountComponent();
|
||||
|
||||
expect(wrapper.vm.intervalId).not.toBe(null);
|
||||
});
|
||||
|
||||
it('renders the statusbar', () => {
|
||||
expect(vm.$el.className).toBe('ide-status-bar');
|
||||
mountComponent();
|
||||
|
||||
expect(wrapper.classes()).toEqual(['ide-status-bar']);
|
||||
});
|
||||
|
||||
describe('commitAgeUpdate', () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(vm, 'commitAgeUpdate').mockImplementation(() => {});
|
||||
mountComponent();
|
||||
jest.spyOn(wrapper.vm, 'commitAgeUpdate').mockImplementation(() => {});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
|
@ -56,70 +62,82 @@ describe('ideStatusBar', () => {
|
|||
});
|
||||
|
||||
it('gets called every second', () => {
|
||||
expect(vm.commitAgeUpdate).not.toHaveBeenCalled();
|
||||
expect(wrapper.vm.commitAgeUpdate).not.toHaveBeenCalled();
|
||||
|
||||
jest.advanceTimersByTime(1000);
|
||||
|
||||
expect(vm.commitAgeUpdate.mock.calls.length).toEqual(1);
|
||||
expect(wrapper.vm.commitAgeUpdate.mock.calls).toHaveLength(1);
|
||||
|
||||
jest.advanceTimersByTime(1000);
|
||||
|
||||
expect(vm.commitAgeUpdate.mock.calls.length).toEqual(2);
|
||||
expect(wrapper.vm.commitAgeUpdate.mock.calls).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getCommitPath', () => {
|
||||
it('returns the path to the commit details', () => {
|
||||
expect(vm.getCommitPath('abc123de')).toBe('/commit/abc123de');
|
||||
mountComponent();
|
||||
|
||||
expect(wrapper.vm.getCommitPath('abc123de')).toBe('/commit/abc123de');
|
||||
});
|
||||
});
|
||||
|
||||
describe('pipeline status', () => {
|
||||
it('opens right sidebar on clicking icon', async () => {
|
||||
jest.spyOn(vm, 'openRightPane').mockImplementation(() => {});
|
||||
Vue.set(vm.$store.state.pipelines, 'latestPipeline', {
|
||||
details: {
|
||||
status: {
|
||||
text: 'success',
|
||||
details_path: 'test',
|
||||
icon: 'status_success',
|
||||
it('opens right sidebar on clicking icon', () => {
|
||||
const pipelines = {
|
||||
latestPipeline: {
|
||||
details: {
|
||||
status: {
|
||||
text: 'success',
|
||||
details_path: 'test',
|
||||
icon: 'status_success',
|
||||
},
|
||||
},
|
||||
commit: {
|
||||
author_gravatar_url: 'www',
|
||||
},
|
||||
},
|
||||
commit: {
|
||||
author_gravatar_url: 'www',
|
||||
},
|
||||
});
|
||||
};
|
||||
mountComponent({ pipelines });
|
||||
jest.spyOn(wrapper.vm, 'openRightPane').mockImplementation(() => {});
|
||||
|
||||
await nextTick();
|
||||
vm.$el.querySelector('.ide-status-pipeline button').click();
|
||||
wrapper.find('button').trigger('click');
|
||||
|
||||
expect(vm.openRightPane).toHaveBeenCalledWith(rightSidebarViews.pipelines);
|
||||
expect(wrapper.vm.openRightPane).toHaveBeenCalledWith(rightSidebarViews.pipelines);
|
||||
});
|
||||
});
|
||||
|
||||
it('does not show merge request status', () => {
|
||||
expect(findMRStatus()).toBe(null);
|
||||
mountComponent();
|
||||
|
||||
expect(findMRStatus().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with merge request in store', () => {
|
||||
beforeEach(() => {
|
||||
store.state.projects[TEST_PROJECT_ID].mergeRequests = {
|
||||
[TEST_MERGE_REQUEST_ID]: {
|
||||
web_url: TEST_MERGE_REQUEST_URL,
|
||||
references: {
|
||||
short: `!${TEST_MERGE_REQUEST_ID}`,
|
||||
const state = {
|
||||
currentMergeRequestId: TEST_MERGE_REQUEST_ID,
|
||||
projects: {
|
||||
[TEST_PROJECT_ID]: {
|
||||
..._.clone(projectData),
|
||||
mergeRequests: {
|
||||
[TEST_MERGE_REQUEST_ID]: {
|
||||
web_url: TEST_MERGE_REQUEST_URL,
|
||||
references: {
|
||||
short: `!${TEST_MERGE_REQUEST_ID}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
store.state.currentMergeRequestId = TEST_MERGE_REQUEST_ID;
|
||||
|
||||
createComponent();
|
||||
mountComponent(state);
|
||||
});
|
||||
|
||||
it('shows merge request status', () => {
|
||||
expect(findMRStatus().textContent.trim()).toEqual(`Merge request !${TEST_MERGE_REQUEST_ID}`);
|
||||
expect(findMRStatus().querySelector('a').href).toEqual(TEST_MERGE_REQUEST_URL);
|
||||
expect(findMRStatus().text()).toBe(`Merge request !${TEST_MERGE_REQUEST_ID}`);
|
||||
expect(findMRStatus().find('a').attributes('href')).toBe(TEST_MERGE_REQUEST_URL);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,70 +1,66 @@
|
|||
import Vue, { nextTick } from 'vue';
|
||||
import { createComponentWithStore } from 'helpers/vue_mount_component_helper';
|
||||
import newDropdown from '~/ide/components/new_dropdown/index.vue';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import NewDropdown from '~/ide/components/new_dropdown/index.vue';
|
||||
import Button from '~/ide/components/new_dropdown/button.vue';
|
||||
import { createStore } from '~/ide/stores';
|
||||
|
||||
describe('new dropdown component', () => {
|
||||
let store;
|
||||
let vm;
|
||||
let wrapper;
|
||||
|
||||
const findAllButtons = () => wrapper.findAllComponents(Button);
|
||||
|
||||
const mountComponent = () => {
|
||||
const store = createStore();
|
||||
store.state.currentProjectId = 'abcproject';
|
||||
store.state.path = '';
|
||||
store.state.trees['abcproject/mybranch'] = { tree: [] };
|
||||
|
||||
wrapper = mount(NewDropdown, {
|
||||
store,
|
||||
propsData: {
|
||||
branch: 'main',
|
||||
path: '',
|
||||
mouseOver: false,
|
||||
type: 'tree',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
store = createStore();
|
||||
|
||||
const component = Vue.extend(newDropdown);
|
||||
|
||||
vm = createComponentWithStore(component, store, {
|
||||
branch: 'main',
|
||||
path: '',
|
||||
mouseOver: false,
|
||||
type: 'tree',
|
||||
});
|
||||
|
||||
vm.$store.state.currentProjectId = 'abcproject';
|
||||
vm.$store.state.path = '';
|
||||
vm.$store.state.trees['abcproject/mybranch'] = {
|
||||
tree: [],
|
||||
};
|
||||
|
||||
vm.$mount();
|
||||
|
||||
jest.spyOn(vm.$refs.newModal, 'open').mockImplementation(() => {});
|
||||
mountComponent();
|
||||
jest.spyOn(wrapper.vm.$refs.newModal, 'open').mockImplementation(() => {});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vm.$destroy();
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('renders new file, upload and new directory links', () => {
|
||||
const buttons = vm.$el.querySelectorAll('.dropdown-menu button');
|
||||
|
||||
expect(buttons[0].textContent.trim()).toBe('New file');
|
||||
expect(buttons[1].textContent.trim()).toBe('Upload file');
|
||||
expect(buttons[2].textContent.trim()).toBe('New directory');
|
||||
expect(findAllButtons().at(0).text()).toBe('New file');
|
||||
expect(findAllButtons().at(1).text()).toBe('Upload file');
|
||||
expect(findAllButtons().at(2).text()).toBe('New directory');
|
||||
});
|
||||
|
||||
describe('createNewItem', () => {
|
||||
it('opens modal for a blob when new file is clicked', () => {
|
||||
vm.$el.querySelectorAll('.dropdown-menu button')[0].click();
|
||||
findAllButtons().at(0).trigger('click');
|
||||
|
||||
expect(vm.$refs.newModal.open).toHaveBeenCalledWith('blob', '');
|
||||
expect(wrapper.vm.$refs.newModal.open).toHaveBeenCalledWith('blob', '');
|
||||
});
|
||||
|
||||
it('opens modal for a tree when new directory is clicked', () => {
|
||||
vm.$el.querySelectorAll('.dropdown-menu button')[2].click();
|
||||
findAllButtons().at(2).trigger('click');
|
||||
|
||||
expect(vm.$refs.newModal.open).toHaveBeenCalledWith('tree', '');
|
||||
expect(wrapper.vm.$refs.newModal.open).toHaveBeenCalledWith('tree', '');
|
||||
});
|
||||
});
|
||||
|
||||
describe('isOpen', () => {
|
||||
it('scrolls dropdown into view', async () => {
|
||||
jest.spyOn(vm.$refs.dropdownMenu, 'scrollIntoView').mockImplementation(() => {});
|
||||
jest.spyOn(wrapper.vm.$refs.dropdownMenu, 'scrollIntoView').mockImplementation(() => {});
|
||||
|
||||
vm.isOpen = true;
|
||||
await wrapper.setProps({ isOpen: true });
|
||||
|
||||
await nextTick();
|
||||
|
||||
expect(vm.$refs.dropdownMenu.scrollIntoView).toHaveBeenCalledWith({
|
||||
expect(wrapper.vm.$refs.dropdownMenu.scrollIntoView).toHaveBeenCalledWith({
|
||||
block: 'nearest',
|
||||
});
|
||||
});
|
||||
|
|
@ -72,11 +68,11 @@ describe('new dropdown component', () => {
|
|||
|
||||
describe('delete entry', () => {
|
||||
it('calls delete action', () => {
|
||||
jest.spyOn(vm, 'deleteEntry').mockImplementation(() => {});
|
||||
jest.spyOn(wrapper.vm, 'deleteEntry').mockImplementation(() => {});
|
||||
|
||||
vm.$el.querySelectorAll('.dropdown-menu button')[4].click();
|
||||
findAllButtons().at(4).trigger('click');
|
||||
|
||||
expect(vm.deleteEntry).toHaveBeenCalledWith('');
|
||||
expect(wrapper.vm.deleteEntry).toHaveBeenCalledWith('');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -482,12 +482,6 @@ RSpec.describe WebHook do
|
|||
|
||||
expect(hook).not_to be_temporarily_disabled
|
||||
end
|
||||
|
||||
it 'can ignore the feature flag' do
|
||||
stub_feature_flags(web_hooks_disable_failed: false)
|
||||
|
||||
expect(hook).to be_temporarily_disabled(ignore_flag: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -510,12 +504,6 @@ RSpec.describe WebHook do
|
|||
|
||||
expect(hook).not_to be_permanently_disabled
|
||||
end
|
||||
|
||||
it 'can ignore the feature flag' do
|
||||
stub_feature_flags(web_hooks_disable_failed: false)
|
||||
|
||||
expect(hook).to be_permanently_disabled(ignore_flag: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -101,27 +101,6 @@ RSpec.describe WebHooks::LogExecutionService do
|
|||
it 'resets the failure count' do
|
||||
expect { service.execute }.to change(project_hook, :recent_failures).to(0)
|
||||
end
|
||||
|
||||
it 'sends a message to AuthLogger if the hook as not previously enabled' do
|
||||
project_hook.update!(recent_failures: ::WebHook::FAILURE_THRESHOLD + 1)
|
||||
|
||||
expect(Gitlab::AuthLogger).to receive(:info).with include(
|
||||
message: 'WebHook change active_state',
|
||||
# identification
|
||||
hook_id: project_hook.id,
|
||||
hook_type: project_hook.type,
|
||||
project_id: project_hook.project_id,
|
||||
group_id: nil,
|
||||
# relevant data
|
||||
prev_state: :permanently_disabled,
|
||||
new_state: :enabled,
|
||||
duration: 1.2,
|
||||
response_status: '200',
|
||||
recent_hook_failures: 0
|
||||
)
|
||||
|
||||
service.execute
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -158,27 +137,6 @@ RSpec.describe WebHooks::LogExecutionService do
|
|||
expect { service.execute }.not_to change(project_hook, :recent_failures)
|
||||
end
|
||||
end
|
||||
|
||||
it 'sends a message to AuthLogger if the state would change' do
|
||||
project_hook.update!(recent_failures: ::WebHook::FAILURE_THRESHOLD)
|
||||
|
||||
expect(Gitlab::AuthLogger).to receive(:info).with include(
|
||||
message: 'WebHook change active_state',
|
||||
# identification
|
||||
hook_id: project_hook.id,
|
||||
hook_type: project_hook.type,
|
||||
project_id: project_hook.project_id,
|
||||
group_id: nil,
|
||||
# relevant data
|
||||
prev_state: :enabled,
|
||||
new_state: :permanently_disabled,
|
||||
duration: (be > 0),
|
||||
response_status: data[:response_status],
|
||||
recent_hook_failures: ::WebHook::FAILURE_THRESHOLD + 1
|
||||
)
|
||||
|
||||
service.execute
|
||||
end
|
||||
end
|
||||
|
||||
context 'when response_category is :error' do
|
||||
|
|
@ -200,25 +158,6 @@ RSpec.describe WebHooks::LogExecutionService do
|
|||
expect { service.execute }.to change(project_hook, :backoff_count).by(1)
|
||||
end
|
||||
|
||||
it 'sends a message to AuthLogger if the state would change' do
|
||||
expect(Gitlab::AuthLogger).to receive(:info).with include(
|
||||
message: 'WebHook change active_state',
|
||||
# identification
|
||||
hook_id: project_hook.id,
|
||||
hook_type: project_hook.type,
|
||||
project_id: project_hook.project_id,
|
||||
group_id: nil,
|
||||
# relevant data
|
||||
prev_state: :enabled,
|
||||
new_state: :temporarily_disabled,
|
||||
duration: (be > 0),
|
||||
response_status: data[:response_status],
|
||||
recent_hook_failures: 0
|
||||
)
|
||||
|
||||
service.execute
|
||||
end
|
||||
|
||||
context 'when the previous cool-off was near the maximum' do
|
||||
before do
|
||||
project_hook.update!(disabled_until: 5.minutes.ago, backoff_count: 8)
|
||||
|
|
|
|||
Loading…
Reference in New Issue