Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
6c7d90ede4
commit
63fc59f6fd
|
|
@ -1 +1 @@
|
|||
2.14.0
|
||||
2.15.0
|
||||
|
|
|
|||
|
|
@ -1,66 +0,0 @@
|
|||
<script>
|
||||
import { GlSprintf, GlTooltipDirective } from '@gitlab/ui';
|
||||
import {
|
||||
approximateDuration,
|
||||
differenceInSeconds,
|
||||
formatDate,
|
||||
getDayDifference,
|
||||
} from '~/lib/utils/datetime_utility';
|
||||
import { DAYS_TO_EXPIRE_SOON } from '../../constants';
|
||||
|
||||
export default {
|
||||
name: 'ExpiresAt',
|
||||
components: { GlSprintf },
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
props: {
|
||||
date: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
noExpirationSet() {
|
||||
return this.date === null;
|
||||
},
|
||||
parsed() {
|
||||
return new Date(this.date);
|
||||
},
|
||||
differenceInSeconds() {
|
||||
return differenceInSeconds(new Date(), this.parsed);
|
||||
},
|
||||
isExpired() {
|
||||
return this.differenceInSeconds <= 0;
|
||||
},
|
||||
inWords() {
|
||||
return approximateDuration(this.differenceInSeconds);
|
||||
},
|
||||
formatted() {
|
||||
return formatDate(this.parsed);
|
||||
},
|
||||
expiresSoon() {
|
||||
return getDayDifference(new Date(), this.parsed) < DAYS_TO_EXPIRE_SOON;
|
||||
},
|
||||
cssClass() {
|
||||
return {
|
||||
'gl-text-red-500': this.isExpired,
|
||||
'gl-text-orange-500': this.expiresSoon,
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span v-if="noExpirationSet">{{ s__('Members|No expiration set') }}</span>
|
||||
<span v-else v-gl-tooltip.hover :title="formatted" :class="cssClass">
|
||||
<template v-if="isExpired">{{ s__('Members|Expired') }}</template>
|
||||
<gl-sprintf v-else :message="s__('Members|in %{time}')">
|
||||
<template #time>
|
||||
{{ inWords }}
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</span>
|
||||
</template>
|
||||
|
|
@ -10,7 +10,6 @@ import RemoveGroupLinkModal from '../modals/remove_group_link_modal.vue';
|
|||
import RemoveMemberModal from '../modals/remove_member_modal.vue';
|
||||
import CreatedAt from './created_at.vue';
|
||||
import ExpirationDatepicker from './expiration_datepicker.vue';
|
||||
import ExpiresAt from './expires_at.vue';
|
||||
import MemberActionButtons from './member_action_buttons.vue';
|
||||
import MemberAvatar from './member_avatar.vue';
|
||||
import MemberSource from './member_source.vue';
|
||||
|
|
@ -24,7 +23,6 @@ export default {
|
|||
GlPagination,
|
||||
MemberAvatar,
|
||||
CreatedAt,
|
||||
ExpiresAt,
|
||||
MembersTableCell,
|
||||
MemberSource,
|
||||
MemberActionButtons,
|
||||
|
|
@ -182,10 +180,6 @@ export default {
|
|||
<created-at :date="createdAt" />
|
||||
</template>
|
||||
|
||||
<template #cell(expires)="{ item: { expiresAt } }">
|
||||
<expires-at :date="expiresAt" />
|
||||
</template>
|
||||
|
||||
<template #cell(maxRole)="{ item: member }">
|
||||
<members-table-cell #default="{ permissions }" :member="member">
|
||||
<role-dropdown v-if="permissions.canUpdate" :permissions="permissions" :member="member" />
|
||||
|
|
|
|||
|
|
@ -37,12 +37,6 @@ export const FIELDS = [
|
|||
thClass: 'col-meta',
|
||||
tdClass: 'col-meta',
|
||||
},
|
||||
{
|
||||
key: 'expires',
|
||||
label: __('Access expires'),
|
||||
thClass: 'col-meta',
|
||||
tdClass: 'col-meta',
|
||||
},
|
||||
{
|
||||
key: 'maxRole',
|
||||
label: __('Max role'),
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { MEMBER_TYPES } from '~/members/constants';
|
|||
import { groupLinkRequestFormatter } from '~/members/utils';
|
||||
import UsersSelect from '~/users_select';
|
||||
|
||||
const SHARED_FIELDS = ['account', 'expires', 'maxRole', 'expiration', 'actions'];
|
||||
const SHARED_FIELDS = ['account', 'maxRole', 'expiration', 'actions'];
|
||||
|
||||
initMembersApp(document.querySelector('.js-group-members-list-app'), {
|
||||
[MEMBER_TYPES.user]: {
|
||||
|
|
|
|||
|
|
@ -27,19 +27,22 @@ export const findTimezoneByIdentifier = (tzList = [], identifier = null) => {
|
|||
};
|
||||
|
||||
export default class TimezoneDropdown {
|
||||
constructor({ $dropdownEl, $inputEl, onSelectTimezone, displayFormat } = defaults) {
|
||||
constructor({
|
||||
$dropdownEl,
|
||||
$inputEl,
|
||||
onSelectTimezone,
|
||||
displayFormat,
|
||||
allowEmpty = false,
|
||||
} = defaults) {
|
||||
this.$dropdown = $dropdownEl;
|
||||
this.$dropdownToggle = this.$dropdown.find('.dropdown-toggle-text');
|
||||
this.$input = $inputEl;
|
||||
this.timezoneData = this.$dropdown.data('data');
|
||||
this.timezoneData = this.$dropdown.data('data') || [];
|
||||
|
||||
this.onSelectTimezone = onSelectTimezone;
|
||||
this.displayFormat = displayFormat || defaults.displayFormat;
|
||||
this.allowEmpty = allowEmpty;
|
||||
|
||||
this.initialTimezone =
|
||||
findTimezoneByIdentifier(this.timezoneData, this.$input.val()) || defaultTimezone;
|
||||
|
||||
this.initDefaultTimezone();
|
||||
this.initDropdown();
|
||||
}
|
||||
|
||||
|
|
@ -52,24 +55,25 @@ export default class TimezoneDropdown {
|
|||
search: {
|
||||
fields: ['name'],
|
||||
},
|
||||
clicked: (cfg) => this.updateInputValue(cfg),
|
||||
clicked: (cfg) => this.handleDropdownChange(cfg),
|
||||
text: (item) => formatTimezone(item),
|
||||
});
|
||||
|
||||
this.setDropdownToggle(this.displayFormat(this.initialTimezone));
|
||||
}
|
||||
const initialTimezone = findTimezoneByIdentifier(this.timezoneData, this.$input.val());
|
||||
|
||||
initDefaultTimezone() {
|
||||
if (!this.$input.val()) {
|
||||
this.$input.val(defaultTimezone.name);
|
||||
if (initialTimezone !== null) {
|
||||
this.setDropdownValue(initialTimezone);
|
||||
} else if (!this.allowEmpty) {
|
||||
this.setDropdownValue(defaultTimezone);
|
||||
}
|
||||
}
|
||||
|
||||
setDropdownToggle(dropdownText) {
|
||||
this.$dropdownToggle.text(dropdownText);
|
||||
setDropdownValue(timezone) {
|
||||
this.$dropdownToggle.text(this.displayFormat(timezone));
|
||||
this.$input.val(timezone.name);
|
||||
}
|
||||
|
||||
updateInputValue({ selectedObj, e }) {
|
||||
handleDropdownChange({ selectedObj, e }) {
|
||||
e.preventDefault();
|
||||
this.$input.val(selectedObj.identifier);
|
||||
if (this.onSelectTimezone) {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ initInviteMembersForm();
|
|||
|
||||
new UsersSelect(); // eslint-disable-line no-new
|
||||
|
||||
const SHARED_FIELDS = ['account', 'expires', 'maxRole', 'expiration', 'actions'];
|
||||
const SHARED_FIELDS = ['account', 'maxRole', 'expiration', 'actions'];
|
||||
initMembersApp(document.querySelector('.js-project-members-list-app'), {
|
||||
[MEMBER_TYPES.user]: {
|
||||
tableFields: SHARED_FIELDS.concat(['source', 'granted']),
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ export default class Profile {
|
|||
$inputEl: this.$inputEl,
|
||||
$dropdownEl: $('.js-timezone-dropdown'),
|
||||
displayFormat: (selectedItem) => formatTimezone(selectedItem),
|
||||
allowEmpty: true,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class Import::BulkImportsController < ApplicationController
|
|||
end
|
||||
|
||||
def create
|
||||
response = BulkImportService.new(current_user, create_params, credentials).execute
|
||||
response = ::BulkImports::CreateService.new(current_user, create_params, credentials).execute
|
||||
|
||||
if response.success?
|
||||
render json: response.payload.to_json(only: [:id])
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ module TimeZoneHelper
|
|||
end
|
||||
|
||||
def local_time(timezone)
|
||||
return if timezone.blank?
|
||||
|
||||
time_zone_instance = ActiveSupport::TimeZone.new(timezone) || Time.zone
|
||||
time_zone_instance.now.strftime("%-l:%M %p")
|
||||
end
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ class UserPreference < ApplicationRecord
|
|||
ignore_columns :experience_level, remove_with: '14.10', remove_after: '2021-03-22'
|
||||
|
||||
default_value_for :tab_width, value: Gitlab::TabWidth::DEFAULT, allows_nil: false
|
||||
default_value_for :timezone, value: Time.zone.tzinfo.name, allows_nil: false
|
||||
default_value_for :time_display_relative, value: true, allows_nil: false
|
||||
default_value_for :time_format_in_24h, value: false, allows_nil: false
|
||||
default_value_for :render_whitespace_in_code, value: false, allows_nil: false
|
||||
|
|
|
|||
|
|
@ -1,70 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Entry point of the BulkImport feature.
|
||||
# This service receives a Gitlab Instance connection params
|
||||
# and a list of groups to be imported.
|
||||
#
|
||||
# Process topography:
|
||||
#
|
||||
# sync | async
|
||||
# |
|
||||
# User +--> P1 +----> Pn +---+
|
||||
# | ^ | Enqueue new job
|
||||
# | +-----+
|
||||
#
|
||||
# P1 (sync)
|
||||
#
|
||||
# - Create a BulkImport record
|
||||
# - Create a BulkImport::Entity for each group to be imported
|
||||
# - Enqueue a BulkImportWorker job (P2) to import the given groups (entities)
|
||||
#
|
||||
# Pn (async)
|
||||
#
|
||||
# - For each group to be imported (BulkImport::Entity.with_status(:created))
|
||||
# - Import the group data
|
||||
# - Create entities for each subgroup of the imported group
|
||||
# - Enqueue a BulkImportService job (Pn) to import the new entities (subgroups)
|
||||
#
|
||||
class BulkImportService
|
||||
attr_reader :current_user, :params, :credentials
|
||||
|
||||
def initialize(current_user, params, credentials)
|
||||
@current_user = current_user
|
||||
@params = params
|
||||
@credentials = credentials
|
||||
end
|
||||
|
||||
def execute
|
||||
bulk_import = create_bulk_import
|
||||
|
||||
BulkImportWorker.perform_async(bulk_import.id)
|
||||
|
||||
ServiceResponse.success(payload: bulk_import)
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
ServiceResponse.error(
|
||||
message: e.message,
|
||||
http_status: :unprocessable_entity
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_bulk_import
|
||||
BulkImport.transaction do
|
||||
bulk_import = BulkImport.create!(user: current_user, source_type: 'gitlab')
|
||||
bulk_import.create_configuration!(credentials.slice(:url, :access_token))
|
||||
|
||||
params.each do |entity|
|
||||
BulkImports::Entity.create!(
|
||||
bulk_import: bulk_import,
|
||||
source_type: entity[:source_type],
|
||||
source_full_path: entity[:source_full_path],
|
||||
destination_name: entity[:destination_name],
|
||||
destination_namespace: entity[:destination_namespace]
|
||||
)
|
||||
end
|
||||
|
||||
bulk_import
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Entry point of the BulkImport feature.
|
||||
# This service receives a Gitlab Instance connection params
|
||||
# and a list of groups to be imported.
|
||||
#
|
||||
# Process topography:
|
||||
#
|
||||
# sync | async
|
||||
# |
|
||||
# User +--> P1 +----> Pn +---+
|
||||
# | ^ | Enqueue new job
|
||||
# | +-----+
|
||||
#
|
||||
# P1 (sync)
|
||||
#
|
||||
# - Create a BulkImport record
|
||||
# - Create a BulkImport::Entity for each group to be imported
|
||||
# - Enqueue a BulkImportWorker job (P2) to import the given groups (entities)
|
||||
#
|
||||
# Pn (async)
|
||||
#
|
||||
# - For each group to be imported (BulkImport::Entity.with_status(:created))
|
||||
# - Import the group data
|
||||
# - Create entities for each subgroup of the imported group
|
||||
# - Enqueue a BulkImports::CreateService job (Pn) to import the new entities (subgroups)
|
||||
#
|
||||
module BulkImports
|
||||
class CreateService
|
||||
attr_reader :current_user, :params, :credentials
|
||||
|
||||
def initialize(current_user, params, credentials)
|
||||
@current_user = current_user
|
||||
@params = params
|
||||
@credentials = credentials
|
||||
end
|
||||
|
||||
def execute
|
||||
bulk_import = create_bulk_import
|
||||
|
||||
BulkImportWorker.perform_async(bulk_import.id)
|
||||
|
||||
ServiceResponse.success(payload: bulk_import)
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
ServiceResponse.error(
|
||||
message: e.message,
|
||||
http_status: :unprocessable_entity
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_bulk_import
|
||||
BulkImport.transaction do
|
||||
bulk_import = BulkImport.create!(user: current_user, source_type: 'gitlab')
|
||||
bulk_import.create_configuration!(credentials.slice(:url, :access_token))
|
||||
|
||||
params.each do |entity|
|
||||
BulkImports::Entity.create!(
|
||||
bulk_import: bulk_import,
|
||||
source_type: entity[:source_type],
|
||||
source_full_path: entity[:source_full_path],
|
||||
destination_name: entity[:destination_name],
|
||||
destination_namespace: entity[:destination_namespace]
|
||||
)
|
||||
end
|
||||
|
||||
bulk_import
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -80,7 +80,7 @@
|
|||
%p= s_("Profiles|Set your local time zone")
|
||||
.col-lg-8
|
||||
%h5= _("Time zone")
|
||||
= dropdown_tag(_("Select a time zone"), options: { toggle_class: 'gl-button btn js-timezone-dropdown input-lg', title: _("Select a time zone"), filter: true, placeholder: s_("OfSearchInADropdown|Filter"), data: { data: timezone_data } } )
|
||||
= dropdown_tag(_("Select a time zone"), options: { toggle_class: 'gl-button btn js-timezone-dropdown input-lg gl-w-full!', title: _("Select a time zone"), filter: true, placeholder: s_("OfSearchInADropdown|Filter"), data: { data: timezone_data } } )
|
||||
%input.hidden{ :type => 'hidden', :id => 'user_timezone', :name => 'user[timezone]', value: @user.timezone }
|
||||
.col-lg-12
|
||||
%hr
|
||||
|
|
|
|||
|
|
@ -84,10 +84,12 @@
|
|||
= sprite_icon('location', css_class: 'fgray')
|
||||
%span{ itemprop: 'addressLocality' }
|
||||
= @user.location
|
||||
= render 'middle_dot_divider', stacking: true do
|
||||
= sprite_icon('clock', css_class: 'fgray')
|
||||
%span
|
||||
= local_time(@user.timezone)
|
||||
- user_local_time = local_time(@user.timezone)
|
||||
- unless user_local_time.nil?
|
||||
= render 'middle_dot_divider', stacking: true, data: { testid: 'user-local-time' } do
|
||||
= sprite_icon('clock', css_class: 'fgray')
|
||||
%span
|
||||
= user_local_time
|
||||
- unless work_information(@user).blank?
|
||||
= render 'middle_dot_divider', stacking: true do
|
||||
= sprite_icon('work', css_class: 'fgray')
|
||||
|
|
|
|||
|
|
@ -48,8 +48,12 @@ gitlab-ctl restart
|
|||
> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/29669) in GitLab 13.9.
|
||||
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/29669) in GitLab 14.1.
|
||||
|
||||
A user can set their time zone in their profile. If a user has not set their time zone, it defaults
|
||||
to the time zone [configured at the instance level](#changing-your-time-zone). On GitLab.com, the
|
||||
default time zone is UTC.
|
||||
Users can set their time zone in their profile. On GitLab.com, the default time zone is UTC.
|
||||
|
||||
New users do not have a default time zone in [GitLab 14.4 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/340795). New users must
|
||||
explicitly set their time zone before it displays on their profile.
|
||||
|
||||
In GitLab 14.3 and earlier, users with no configured time zone default to the time zone
|
||||
[configured at the instance level](#changing-your-time-zone).
|
||||
|
||||
For more information, see [Set your time zone](../user/profile/index.md#set-your-time-zone).
|
||||
|
|
|
|||
|
|
@ -726,8 +726,6 @@ GET /projects/:id/integrations/github
|
|||
|
||||
## Hangouts Chat
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/20290) in GitLab 11.2.
|
||||
|
||||
Google Workspace team collaboration tool.
|
||||
|
||||
### Create/Edit Hangouts Chat integration
|
||||
|
|
@ -738,9 +736,6 @@ Set Hangouts Chat integration for a project.
|
|||
PUT /projects/:id/integrations/hangouts-chat
|
||||
```
|
||||
|
||||
NOTE:
|
||||
Specific event parameters (for example, `push_events` flag) were [introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/11435) in GitLab 10.4.
|
||||
|
||||
Parameters:
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|
|
@ -832,10 +827,6 @@ GET /projects/:id/integrations/jira
|
|||
|
||||
Set Jira integration for a project.
|
||||
|
||||
> Starting with GitLab 8.14, `api_url`, `issues_url`, `new_issue_url` and
|
||||
> `project_url` are replaced by `url`. If you are using an
|
||||
> older version, [follow this documentation](https://gitlab.com/gitlab-org/gitlab/-/blob/8-13-stable-ee/doc/api/services.md#jira).
|
||||
|
||||
```plaintext
|
||||
PUT /projects/:id/integrations/jira
|
||||
```
|
||||
|
|
@ -1198,9 +1189,6 @@ Set Slack integration for a project.
|
|||
PUT /projects/:id/integrations/slack
|
||||
```
|
||||
|
||||
NOTE:
|
||||
Specific event parameters (for example, `push_events` flag and `push_channel`) were [introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/11435) in GitLab 10.4.
|
||||
|
||||
Parameters:
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|
|
@ -1308,9 +1296,6 @@ Set Mattermost notifications integration for a project.
|
|||
PUT /projects/:id/integrations/mattermost
|
||||
```
|
||||
|
||||
NOTE:
|
||||
Specific event parameters (for example, `push_events` flag and `push_channel`) were [introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/11435) in GitLab 10.4.
|
||||
|
||||
Parameters:
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
> - The pre-configured `KUBECONFIG` was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/324275) in GitLab 14.2.
|
||||
> - The ability to authorize groups was [introduced](https://gitlab.com/groups/gitlab-org/-/epics/5784) in GitLab 14.3.
|
||||
|
||||
WARNING:
|
||||
The CI/CD Tunnel is not supported for GitLab self-managed instances installed via Omnibus. We
|
||||
plan to [add support for Omnibus](https://gitlab.com/gitlab-org/gitlab/-/issues/324272) in the future.
|
||||
|
||||
The CI/CD Tunnel enables users to access Kubernetes clusters from GitLab CI/CD jobs even if there is no network
|
||||
connectivity between GitLab Runner and a cluster. GitLab Runner does not have to be running in the same cluster.
|
||||
|
||||
|
|
|
|||
|
|
@ -171,8 +171,51 @@ from the GitLab server.
|
|||
Blobs are kept forever on the GitLab server, and there is no hard limit on how much data can be
|
||||
stored.
|
||||
|
||||
### Using the API to clear the cache
|
||||
|
||||
To reclaim disk space used by image blobs that are no longer needed, use
|
||||
the [Dependency Proxy API](../../../api/dependency_proxy.md).
|
||||
the [Dependency Proxy API](../../../api/dependency_proxy.md) to clear the entire
|
||||
cache.
|
||||
|
||||
### Cleanup policies
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/294187) in GitLab Free 14.4.
|
||||
|
||||
The cleanup policy is a scheduled job you can use to clear cached images that are no longer used,
|
||||
freeing up additional storage space. The policies use time-to-live (TTL) logic:
|
||||
|
||||
- The number of days is configured.
|
||||
- All cached dependency proxy files that have not been pulled in that many days are deleted.
|
||||
|
||||
Use the [GraphQL API](../../../api/graphql/reference/index.md#mutationupdatedependencyproxyimagettlgrouppolicy)
|
||||
to enable and configure cleanup policies:
|
||||
|
||||
```graphql
|
||||
mutation {
|
||||
updateDependencyProxyImageTtlGroupPolicy(input:
|
||||
{
|
||||
groupPath: "<your-full-group-path>",
|
||||
enabled: true,
|
||||
ttl: 90
|
||||
}
|
||||
) {
|
||||
dependencyProxyImageTtlPolicy {
|
||||
enabled
|
||||
ttl
|
||||
}
|
||||
errors
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
See the [Getting started with GraphQL](../../../api/graphql/getting_started.md)
|
||||
guide to learn how to make GraphQL queries. Support for enabling and configuring cleanup policies in
|
||||
the UI is tracked in [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/340777).
|
||||
|
||||
When the policy is initially enabled, the default TTL setting is 90 days. Once enabled, stale
|
||||
dependency proxy files are queued for deletion each day. Deletion may not occur right away due to
|
||||
processing time. If the image is pulled after the cached files are marked as expired, the expired
|
||||
files are ignored and new files are downloaded and cached from the external registry.
|
||||
|
||||
## Docker Hub rate limits and the Dependency Proxy
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ module API
|
|||
end
|
||||
end
|
||||
post do
|
||||
response = BulkImportService.new(
|
||||
response = ::BulkImports::CreateService.new(
|
||||
current_user,
|
||||
params[:entities],
|
||||
url: params[:configuration][:url],
|
||||
|
|
|
|||
|
|
@ -21212,9 +21212,6 @@ msgstr ""
|
|||
msgid "Members|Expiration date updated successfully."
|
||||
msgstr ""
|
||||
|
||||
msgid "Members|Expired"
|
||||
msgstr ""
|
||||
|
||||
msgid "Members|Filter members"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -21230,9 +21227,6 @@ msgstr ""
|
|||
msgid "Members|Membership"
|
||||
msgstr ""
|
||||
|
||||
msgid "Members|No expiration set"
|
||||
msgstr ""
|
||||
|
||||
msgid "Members|Remove \"%{groupName}\""
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -21254,9 +21248,6 @@ msgstr ""
|
|||
msgid "Members|Search invited"
|
||||
msgstr ""
|
||||
|
||||
msgid "Members|in %{time}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Member|Deny access"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -199,9 +199,9 @@ RSpec.describe Import::BulkImportsController do
|
|||
session[:bulk_import_gitlab_url] = instance_url
|
||||
end
|
||||
|
||||
it 'executes BulkImportService' do
|
||||
it 'executes BulkImpors::CreatetService' do
|
||||
expect_next_instance_of(
|
||||
BulkImportService, user, bulk_import_params, { url: instance_url, access_token: pat }) do |service|
|
||||
::BulkImports::CreateService, user, bulk_import_params, { url: instance_url, access_token: pat }) do |service|
|
||||
allow(service).to receive(:execute).and_return(ServiceResponse.success(payload: bulk_import))
|
||||
end
|
||||
|
||||
|
|
@ -214,7 +214,7 @@ RSpec.describe Import::BulkImportsController do
|
|||
it 'returns error when validation fails' do
|
||||
error_response = ServiceResponse.error(message: 'Record invalid', http_status: :unprocessable_entity)
|
||||
expect_next_instance_of(
|
||||
BulkImportService, user, bulk_import_params, { url: instance_url, access_token: pat }) do |service|
|
||||
::BulkImports::CreateService, user, bulk_import_params, { url: instance_url, access_token: pat }) do |service|
|
||||
allow(service).to receive(:execute).and_return(error_response)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ RSpec.describe 'Groups > Members > Manage groups', :js do
|
|||
context 'when group link exists' do
|
||||
let_it_be(:shared_with_group) { create(:group) }
|
||||
let_it_be(:shared_group) { create(:group) }
|
||||
let_it_be(:expiration_date) { 5.days.from_now.to_date }
|
||||
|
||||
let(:additional_link_attrs) { {} }
|
||||
|
||||
|
|
@ -115,29 +116,29 @@ RSpec.describe 'Groups > Members > Manage groups', :js do
|
|||
click_groups_tab
|
||||
|
||||
page.within first_row do
|
||||
fill_in 'Expiration date', with: 5.days.from_now.to_date
|
||||
fill_in 'Expiration date', with: expiration_date
|
||||
find_field('Expiration date').native.send_keys :enter
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_content(/in \d days/)
|
||||
expect(page).to have_field('Expiration date', with: expiration_date)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when expiry date is set' do
|
||||
let(:additional_link_attrs) { { expires_at: 5.days.from_now.to_date } }
|
||||
let(:additional_link_attrs) { { expires_at: expiration_date } }
|
||||
|
||||
it 'clears expiry date' do
|
||||
click_groups_tab
|
||||
|
||||
page.within first_row do
|
||||
expect(page).to have_content(/in \d days/)
|
||||
expect(page).to have_field('Expiration date', with: expiration_date)
|
||||
|
||||
find('[data-testid="clear-button"]').click
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_content('No expiration set')
|
||||
expect(page).to have_field('Expiration date', with: '')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ RSpec.describe 'Groups > Members > Owner adds member with expiration date', :js
|
|||
|
||||
let_it_be(:user1) { create(:user, name: 'John Doe') }
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:expiration_date) { 5.days.from_now.to_date }
|
||||
|
||||
let(:new_member) { create(:user, name: 'Mary Jane') }
|
||||
|
||||
|
|
@ -19,10 +20,10 @@ RSpec.describe 'Groups > Members > Owner adds member with expiration date', :js
|
|||
it 'expiration date is displayed in the members list' do
|
||||
visit group_group_members_path(group)
|
||||
|
||||
invite_member(new_member.name, role: 'Guest', expires_at: 5.days.from_now.to_date)
|
||||
invite_member(new_member.name, role: 'Guest', expires_at: expiration_date)
|
||||
|
||||
page.within second_row do
|
||||
expect(page).to have_content(/in \d days/)
|
||||
expect(page).to have_field('Expiration date', with: expiration_date)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -31,27 +32,27 @@ RSpec.describe 'Groups > Members > Owner adds member with expiration date', :js
|
|||
visit group_group_members_path(group)
|
||||
|
||||
page.within second_row do
|
||||
fill_in 'Expiration date', with: 5.days.from_now.to_date
|
||||
fill_in 'Expiration date', with: expiration_date
|
||||
find_field('Expiration date').native.send_keys :enter
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_content(/in \d days/)
|
||||
expect(page).to have_field('Expiration date', with: expiration_date)
|
||||
end
|
||||
end
|
||||
|
||||
it 'clears expiration date' do
|
||||
create(:group_member, :developer, user: new_member, group: group, expires_at: 5.days.from_now.to_date)
|
||||
create(:group_member, :developer, user: new_member, group: group, expires_at: expiration_date)
|
||||
visit group_group_members_path(group)
|
||||
|
||||
page.within second_row do
|
||||
expect(page).to have_content(/in \d days/)
|
||||
expect(page).to have_field('Expiration date', with: expiration_date)
|
||||
|
||||
find('[data-testid="clear-button"]').click
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_content('No expiration set')
|
||||
expect(page).to have_field('Expiration date', with: '')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -561,14 +561,11 @@ RSpec.describe 'User edit profile' do
|
|||
|
||||
page.find("a", text: "Nuku'alofa").click
|
||||
|
||||
tz = page.find('.user-time-preferences #user_timezone', visible: false)
|
||||
|
||||
expect(tz.value).to eq('Pacific/Tongatapu')
|
||||
expect(page).to have_field(:user_timezone, with: 'Pacific/Tongatapu', type: :hidden)
|
||||
end
|
||||
|
||||
it 'timezone defaults to servers default' do
|
||||
timezone_name = Time.zone.tzinfo.name
|
||||
expect(page.find('.user-time-preferences #user_timezone', visible: false).value).to eq(timezone_name)
|
||||
it 'timezone defaults to empty' do
|
||||
expect(page).to have_field(:user_timezone, with: '', type: :hidden)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ RSpec.describe 'Projects > Members > Groups with access list', :js do
|
|||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:group) { create(:group, :public) }
|
||||
let_it_be(:project) { create(:project, :public) }
|
||||
let_it_be(:expiration_date) { 5.days.from_now.to_date }
|
||||
|
||||
let(:additional_link_attrs) { {} }
|
||||
let!(:group_link) { create(:project_group_link, project: project, group: group, **additional_link_attrs) }
|
||||
|
|
@ -37,27 +38,27 @@ RSpec.describe 'Projects > Members > Groups with access list', :js do
|
|||
|
||||
it 'updates expiry date' do
|
||||
page.within find_group_row(group) do
|
||||
fill_in 'Expiration date', with: 5.days.from_now.to_date
|
||||
fill_in 'Expiration date', with: expiration_date
|
||||
find_field('Expiration date').native.send_keys :enter
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_content(/in \d days/)
|
||||
expect(page).to have_field('Expiration date', with: expiration_date)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when link has expiry date set' do
|
||||
let(:additional_link_attrs) { { expires_at: 5.days.from_now.to_date } }
|
||||
let(:additional_link_attrs) { { expires_at: expiration_date } }
|
||||
|
||||
it 'clears expiry date' do
|
||||
page.within find_group_row(group) do
|
||||
expect(page).to have_content(/in \d days/)
|
||||
expect(page).to have_field('Expiration date', with: expiration_date)
|
||||
|
||||
find('[data-testid="clear-button"]').click
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_content('No expiration set')
|
||||
expect(page).to have_field('Expiration date', with: '')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -165,6 +165,8 @@ RSpec.describe 'Project > Members > Invite group', :js do
|
|||
let(:project) { create(:project) }
|
||||
let!(:group) { create(:group) }
|
||||
|
||||
let_it_be(:expiration_date) { 5.days.from_now.to_date }
|
||||
|
||||
around do |example|
|
||||
freeze_time { example.run }
|
||||
end
|
||||
|
|
@ -176,15 +178,14 @@ RSpec.describe 'Project > Members > Invite group', :js do
|
|||
|
||||
visit project_project_members_path(project)
|
||||
|
||||
invite_group(group.name, role: 'Guest', expires_at: 5.days.from_now)
|
||||
invite_group(group.name, role: 'Guest', expires_at: expiration_date)
|
||||
end
|
||||
|
||||
it 'the group link shows the expiration time with a warning class' do
|
||||
setup
|
||||
click_link 'Groups'
|
||||
|
||||
expect(find_group_row(group)).to have_content(/in \d days/)
|
||||
expect(find_group_row(group)).to have_selector('.gl-text-orange-500')
|
||||
expect(page).to have_field('Expiration date', with: expiration_date)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ RSpec.describe 'Projects > Members > Maintainer adds member with expiration date
|
|||
|
||||
let_it_be(:maintainer) { create(:user) }
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:three_days_from_now) { 3.days.from_now.to_date }
|
||||
let_it_be(:five_days_from_now) { 5.days.from_now.to_date }
|
||||
|
||||
let(:new_member) { create(:user) }
|
||||
|
||||
|
|
@ -22,39 +24,39 @@ RSpec.describe 'Projects > Members > Maintainer adds member with expiration date
|
|||
it 'expiration date is displayed in the members list' do
|
||||
visit project_project_members_path(project)
|
||||
|
||||
invite_member(new_member.name, role: 'Guest', expires_at: 5.days.from_now.to_date)
|
||||
invite_member(new_member.name, role: 'Guest', expires_at: five_days_from_now)
|
||||
|
||||
page.within find_member_row(new_member) do
|
||||
expect(page).to have_content(/in \d days/)
|
||||
expect(page).to have_field('Expiration date', with: five_days_from_now)
|
||||
end
|
||||
end
|
||||
|
||||
it 'changes expiration date' do
|
||||
project.team.add_users([new_member.id], :developer, expires_at: 3.days.from_now.to_date)
|
||||
project.team.add_users([new_member.id], :developer, expires_at: three_days_from_now)
|
||||
visit project_project_members_path(project)
|
||||
|
||||
page.within find_member_row(new_member) do
|
||||
fill_in 'Expiration date', with: 5.days.from_now.to_date
|
||||
fill_in 'Expiration date', with: five_days_from_now
|
||||
find_field('Expiration date').native.send_keys :enter
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_content(/in \d days/)
|
||||
expect(page).to have_field('Expiration date', with: five_days_from_now)
|
||||
end
|
||||
end
|
||||
|
||||
it 'clears expiration date' do
|
||||
project.team.add_users([new_member.id], :developer, expires_at: 5.days.from_now.to_date)
|
||||
project.team.add_users([new_member.id], :developer, expires_at: five_days_from_now)
|
||||
visit project_project_members_path(project)
|
||||
|
||||
page.within find_member_row(new_member) do
|
||||
expect(page).to have_content(/in \d days/)
|
||||
expect(page).to have_field('Expiration date', with: five_days_from_now)
|
||||
|
||||
find('[data-testid="clear-button"]').click
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_content('No expiration set')
|
||||
expect(page).to have_field('Expiration date', with: '')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ RSpec.describe 'User page' do
|
|||
|
||||
context 'timezone' do
|
||||
let_it_be(:timezone) { 'America/Los_Angeles' }
|
||||
let_it_be(:local_time_selector) { '[data-testid="user-local-time"]' }
|
||||
|
||||
before do
|
||||
travel_to Time.find_zone(timezone).local(2021, 7, 20, 15, 30, 45)
|
||||
|
|
@ -92,7 +93,19 @@ RSpec.describe 'User page' do
|
|||
it 'shows local time' do
|
||||
subject
|
||||
|
||||
expect(page).to have_content('3:30 PM')
|
||||
within local_time_selector do
|
||||
expect(page).to have_content('3:30 PM')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when timezone is not set' do
|
||||
let_it_be(:user) { create(:user, timezone: nil) }
|
||||
|
||||
it 'does not show local time' do
|
||||
subject
|
||||
|
||||
expect(page).not_to have_selector(local_time_selector)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -102,7 +115,9 @@ RSpec.describe 'User page' do
|
|||
it 'shows local time using the configured default timezone (UTC in this case)' do
|
||||
subject
|
||||
|
||||
expect(page).to have_content('10:30 PM')
|
||||
within local_time_selector do
|
||||
expect(page).to have_content('10:30 PM')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,86 +0,0 @@
|
|||
import { within } from '@testing-library/dom';
|
||||
import { mount, createWrapper } from '@vue/test-utils';
|
||||
import { useFakeDate } from 'helpers/fake_date';
|
||||
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
|
||||
import ExpiresAt from '~/members/components/table/expires_at.vue';
|
||||
|
||||
describe('ExpiresAt', () => {
|
||||
// March 15th, 2020
|
||||
useFakeDate(2020, 2, 15);
|
||||
|
||||
let wrapper;
|
||||
|
||||
const createComponent = (propsData) => {
|
||||
wrapper = mount(ExpiresAt, {
|
||||
propsData,
|
||||
directives: {
|
||||
GlTooltip: createMockDirective(),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const getByText = (text, options) =>
|
||||
createWrapper(within(wrapper.element).getByText(text, options));
|
||||
|
||||
const getTooltipDirective = (elementWrapper) => getBinding(elementWrapper.element, 'gl-tooltip');
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe('when no expiration date is set', () => {
|
||||
it('displays "No expiration set"', () => {
|
||||
createComponent({ date: null });
|
||||
|
||||
expect(getByText('No expiration set').exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when expiration date is in the past', () => {
|
||||
let expiredText;
|
||||
|
||||
beforeEach(() => {
|
||||
createComponent({ date: '2019-03-15T00:00:00.000' });
|
||||
|
||||
expiredText = getByText('Expired');
|
||||
});
|
||||
|
||||
it('displays "Expired"', () => {
|
||||
expect(expiredText.exists()).toBe(true);
|
||||
expect(expiredText.classes()).toContain('gl-text-red-500');
|
||||
});
|
||||
|
||||
it('displays tooltip with formatted date', () => {
|
||||
const tooltipDirective = getTooltipDirective(expiredText);
|
||||
|
||||
expect(tooltipDirective).not.toBeUndefined();
|
||||
expect(expiredText.attributes('title')).toBe('Mar 15, 2019 12:00am UTC');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when expiration date is in the future', () => {
|
||||
it.each`
|
||||
date | expected | warningColor
|
||||
${'2020-03-23T00:00:00.000'} | ${'in 8 days'} | ${false}
|
||||
${'2020-03-20T00:00:00.000'} | ${'in 5 days'} | ${true}
|
||||
${'2020-03-16T00:00:00.000'} | ${'in 1 day'} | ${true}
|
||||
${'2020-03-15T05:00:00.000'} | ${'in about 5 hours'} | ${true}
|
||||
${'2020-03-15T01:00:00.000'} | ${'in about 1 hour'} | ${true}
|
||||
${'2020-03-15T00:30:00.000'} | ${'in 30 minutes'} | ${true}
|
||||
${'2020-03-15T00:01:15.000'} | ${'in 1 minute'} | ${true}
|
||||
${'2020-03-15T00:00:15.000'} | ${'in less than a minute'} | ${true}
|
||||
`('displays "$expected"', ({ date, expected, warningColor }) => {
|
||||
createComponent({ date });
|
||||
|
||||
const expiredText = getByText(expected);
|
||||
|
||||
expect(expiredText.exists()).toBe(true);
|
||||
|
||||
if (warningColor) {
|
||||
expect(expiredText.classes()).toContain('gl-text-orange-500');
|
||||
} else {
|
||||
expect(expiredText.classes()).not.toContain('gl-text-orange-500');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -10,7 +10,6 @@ import setWindowLocation from 'helpers/set_window_location_helper';
|
|||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||
import CreatedAt from '~/members/components/table/created_at.vue';
|
||||
import ExpirationDatepicker from '~/members/components/table/expiration_datepicker.vue';
|
||||
import ExpiresAt from '~/members/components/table/expires_at.vue';
|
||||
import MemberActionButtons from '~/members/components/table/member_action_buttons.vue';
|
||||
import MemberAvatar from '~/members/components/table/member_avatar.vue';
|
||||
import MemberSource from '~/members/components/table/member_source.vue';
|
||||
|
|
@ -68,7 +67,6 @@ describe('MembersTable', () => {
|
|||
stubs: [
|
||||
'member-avatar',
|
||||
'member-source',
|
||||
'expires-at',
|
||||
'created-at',
|
||||
'member-action-buttons',
|
||||
'role-dropdown',
|
||||
|
|
@ -119,7 +117,6 @@ describe('MembersTable', () => {
|
|||
${'granted'} | ${'Access granted'} | ${memberMock} | ${CreatedAt}
|
||||
${'invited'} | ${'Invited'} | ${invite} | ${CreatedAt}
|
||||
${'requested'} | ${'Requested'} | ${accessRequest} | ${CreatedAt}
|
||||
${'expires'} | ${'Access expires'} | ${memberMock} | ${ExpiresAt}
|
||||
${'maxRole'} | ${'Max role'} | ${memberCanUpdate} | ${RoleDropdown}
|
||||
${'expiration'} | ${'Expiration'} | ${memberMock} | ${ExpirationDatepicker}
|
||||
`('renders the $label field', ({ field, label, member, expectedComponent }) => {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,17 @@ describe('Timezone Dropdown', () => {
|
|||
let $dropdownEl = null;
|
||||
let $wrapper = null;
|
||||
const tzListSel = '.dropdown-content ul li a.is-active';
|
||||
const tzDropdownToggleText = '.dropdown-toggle-text';
|
||||
|
||||
const initTimezoneDropdown = (options = {}) => {
|
||||
// eslint-disable-next-line no-new
|
||||
new TimezoneDropdown({
|
||||
$inputEl,
|
||||
$dropdownEl,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
const findDropdownToggleText = () => $wrapper.find('.dropdown-toggle-text');
|
||||
|
||||
describe('Initialize', () => {
|
||||
describe('with dropdown already loaded', () => {
|
||||
|
|
@ -18,16 +28,13 @@ describe('Timezone Dropdown', () => {
|
|||
loadFixtures('pipeline_schedules/edit.html');
|
||||
$wrapper = $('.dropdown');
|
||||
$inputEl = $('#schedule_cron_timezone');
|
||||
$inputEl.val('');
|
||||
$dropdownEl = $('.js-timezone-dropdown');
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
new TimezoneDropdown({
|
||||
$inputEl,
|
||||
$dropdownEl,
|
||||
});
|
||||
});
|
||||
|
||||
it('can take an $inputEl in the constructor', () => {
|
||||
initTimezoneDropdown();
|
||||
|
||||
const tzStr = '[UTC + 5.5] Sri Jayawardenepura';
|
||||
const tzValue = 'Asia/Colombo';
|
||||
|
||||
|
|
@ -42,6 +49,8 @@ describe('Timezone Dropdown', () => {
|
|||
});
|
||||
|
||||
it('will format data array of timezones into a list of offsets', () => {
|
||||
initTimezoneDropdown();
|
||||
|
||||
const data = $dropdownEl.data('data');
|
||||
const formatted = $wrapper.find(tzListSel).text();
|
||||
|
||||
|
|
@ -50,10 +59,28 @@ describe('Timezone Dropdown', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('will default the timezone to UTC', () => {
|
||||
const tz = $inputEl.val();
|
||||
describe('when `allowEmpty` property is `false`', () => {
|
||||
beforeEach(() => {
|
||||
initTimezoneDropdown();
|
||||
});
|
||||
|
||||
expect(tz).toBe('UTC');
|
||||
it('will default the timezone to UTC', () => {
|
||||
const tz = $inputEl.val();
|
||||
|
||||
expect(tz).toBe('UTC');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when `allowEmpty` property is `true`', () => {
|
||||
beforeEach(() => {
|
||||
initTimezoneDropdown({
|
||||
allowEmpty: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('will default the value of the input to an empty string', () => {
|
||||
expect($inputEl.val()).toBe('');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -68,23 +95,15 @@ describe('Timezone Dropdown', () => {
|
|||
it('will populate the list of UTC offsets after the dropdown is loaded', () => {
|
||||
expect($wrapper.find(tzListSel).length).toEqual(0);
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
new TimezoneDropdown({
|
||||
$inputEl,
|
||||
$dropdownEl,
|
||||
});
|
||||
initTimezoneDropdown();
|
||||
|
||||
expect($wrapper.find(tzListSel).length).toEqual($($dropdownEl).data('data').length);
|
||||
});
|
||||
|
||||
it('will call a provided handler when a new timezone is selected', () => {
|
||||
const onSelectTimezone = jest.fn();
|
||||
// eslint-disable-next-line no-new
|
||||
new TimezoneDropdown({
|
||||
$inputEl,
|
||||
$dropdownEl,
|
||||
onSelectTimezone,
|
||||
});
|
||||
|
||||
initTimezoneDropdown({ onSelectTimezone });
|
||||
|
||||
$wrapper.find(tzListSel).first().trigger('click');
|
||||
|
||||
|
|
@ -94,24 +113,15 @@ describe('Timezone Dropdown', () => {
|
|||
it('will correctly set the dropdown label if a timezone identifier is set on the inputEl', () => {
|
||||
$inputEl.val('America/St_Johns');
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
new TimezoneDropdown({
|
||||
$inputEl,
|
||||
$dropdownEl,
|
||||
displayFormat: (selectedItem) => formatTimezone(selectedItem),
|
||||
});
|
||||
initTimezoneDropdown({ displayFormat: (selectedItem) => formatTimezone(selectedItem) });
|
||||
|
||||
expect($wrapper.find(tzDropdownToggleText).html()).toEqual('[UTC - 2.5] Newfoundland');
|
||||
expect(findDropdownToggleText().html()).toEqual('[UTC - 2.5] Newfoundland');
|
||||
});
|
||||
|
||||
it('will call a provided `displayFormat` handler to format the dropdown value', () => {
|
||||
const displayFormat = jest.fn();
|
||||
// eslint-disable-next-line no-new
|
||||
new TimezoneDropdown({
|
||||
$inputEl,
|
||||
$dropdownEl,
|
||||
displayFormat,
|
||||
});
|
||||
|
||||
initTimezoneDropdown({ displayFormat });
|
||||
|
||||
$wrapper.find(tzListSel).first().trigger('click');
|
||||
|
||||
|
|
|
|||
|
|
@ -76,6 +76,18 @@ RSpec.describe TimeZoneHelper, :aggregate_failures do
|
|||
travel_to Time.find_zone(timezone).local(2021, 7, 20, 15, 30, 45)
|
||||
end
|
||||
|
||||
context 'when timezone is `nil`' do
|
||||
it 'returns `nil`' do
|
||||
expect(helper.local_time(nil)).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when timezone is blank' do
|
||||
it 'returns `nil`' do
|
||||
expect(helper.local_time('')).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a valid timezone is passed' do
|
||||
it 'returns local time' do
|
||||
expect(helper.local_time(timezone)).to eq('3:30 PM')
|
||||
|
|
|
|||
|
|
@ -80,12 +80,6 @@ RSpec.describe UserPreference do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#timezone' do
|
||||
it 'returns server time as default' do
|
||||
expect(user_preference.timezone).to eq(Time.zone.tzinfo.name)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#tab_width' do
|
||||
it 'is set to 8 by default' do
|
||||
# Intentionally not using factory here to test the constructor.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe BulkImportService do
|
||||
RSpec.describe BulkImports::CreateService do
|
||||
let(:user) { create(:user) }
|
||||
let(:credentials) { { url: 'http://gitlab.example', access_token: 'token' } }
|
||||
let(:params) do
|
||||
Loading…
Reference in New Issue