Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-10-31 03:10:18 +00:00
parent 453bb35fc8
commit b6cb3bc944
24 changed files with 288 additions and 376 deletions

View File

@ -161,6 +161,8 @@ export default {
},
onKeyDown({ event }) {
if (!this.items.length) return false;
if (event.key === 'ArrowUp') {
this.upHandler();
return true;

View File

@ -57,6 +57,7 @@ function createSuggestionPlugin({
render: () => {
let component;
let popup;
let isHidden = false;
const onUpdate = (props) => {
component?.updateProps({ ...props, loading: false });
@ -88,6 +89,12 @@ function createSuggestionPlugin({
popup = tippy('body', {
getReferenceClientRect: props.clientRect,
appendTo: () => document.body,
onHide: () => {
isHidden = true;
},
onShow: () => {
isHidden = false;
},
content: component.element,
showOnCreate: true,
interactive: true,
@ -100,6 +107,8 @@ function createSuggestionPlugin({
onUpdate,
onKeyDown(props) {
if (isHidden) return false;
if (props.event.key === 'Escape') {
popup?.[0].hide();

View File

@ -195,7 +195,6 @@ export default {
:autocomplete-data-sources="autocompleteDataSources"
:form-field-props="formFieldProps"
:add-spacing-classes="false"
data-testid="work-item-add-comment"
use-bottom-toolbar
supports-quick-actions
:autofocus="autofocus"

View File

@ -207,7 +207,6 @@ export default {
<gl-button
v-if="showEdit"
v-gl-tooltip
data-testid="edit-work-item-note"
data-track-action="click_button"
data-track-label="edit_button"
category="tertiary"
@ -219,7 +218,6 @@ export default {
<gl-disclosure-dropdown
ref="dropdown"
v-gl-tooltip
data-testid="work-item-note-actions"
icon="ellipsis_v"
text-sr-only
placement="right"

View File

@ -20,7 +20,6 @@ import {
I18N_WORK_ITEM_DELETE,
I18N_WORK_ITEM_ARE_YOU_SURE_DELETE,
TEST_ID_CONFIDENTIALITY_TOGGLE_ACTION,
TEST_ID_NOTIFICATIONS_TOGGLE_ACTION,
TEST_ID_NOTIFICATIONS_TOGGLE_FORM,
TEST_ID_DELETE_ACTION,
TEST_ID_PROMOTE_ACTION,
@ -39,8 +38,8 @@ import projectWorkItemTypesQuery from '../graphql/project_work_item_types.query.
export default {
i18n: {
enableTaskConfidentiality: s__('WorkItem|Turn on confidentiality'),
disableTaskConfidentiality: s__('WorkItem|Turn off confidentiality'),
enableConfidentiality: s__('WorkItem|Turn on confidentiality'),
disableConfidentiality: s__('WorkItem|Turn off confidentiality'),
notifications: s__('WorkItem|Notifications'),
notificationOn: s__('WorkItem|Notifications turned on.'),
notificationOff: s__('WorkItem|Notifications turned off.'),
@ -60,7 +59,6 @@ export default {
},
mixins: [Tracking.mixin({ label: 'actions_menu' })],
isLoggedIn: isLoggedIn(),
notificationsToggleTestId: TEST_ID_NOTIFICATIONS_TOGGLE_ACTION,
notificationsToggleFormTestId: TEST_ID_NOTIFICATIONS_TOGGLE_FORM,
confidentialityTestId: TEST_ID_CONFIDENTIALITY_TOGGLE_ACTION,
copyReferenceTestId: TEST_ID_COPY_REFERENCE_ACTION,
@ -165,6 +163,11 @@ export default {
canPromoteToObjective() {
return this.canUpdate && this.workItemType === WORK_ITEM_TYPE_VALUE_KEY_RESULT;
},
confidentialItemText() {
return this.isConfidential
? this.$options.i18n.disableConfidentiality
: this.$options.i18n.enableConfidentiality;
},
objectiveWorkItemTypeId() {
return this.workItemTypes.find((type) => type.name === WORK_ITEM_TYPE_VALUE_OBJECTIVE).id;
},
@ -267,7 +270,7 @@ export default {
icon="ellipsis_v"
data-testid="work-item-actions-dropdown"
text-sr-only
:text="__('More actions')"
:toggle-text="__('More actions')"
category="tertiary"
:auto-close="false"
no-caret
@ -282,7 +285,6 @@ export default {
<gl-toggle
:value="subscribedToNotifications"
:label="$options.i18n.notifications"
:data-testid="$options.notificationsToggleTestId"
class="work-item-notification-toggle"
label-position="left"
@change="toggleNotifications($event)"
@ -299,49 +301,46 @@ export default {
>
<template #list-item>{{ __('Promote to objective') }}</template>
</gl-disclosure-dropdown-item>
<template v-if="canUpdate && !isParentConfidential">
<gl-disclosure-dropdown-item
:data-testid="$options.confidentialityTestId"
@action="handleToggleWorkItemConfidentiality"
><template #list-item>{{
isConfidential
? $options.i18n.disableTaskConfidentiality
: $options.i18n.enableTaskConfidentiality
}}</template></gl-disclosure-dropdown-item
>
</template>
<gl-disclosure-dropdown-item
v-if="canUpdate && !isParentConfidential"
:data-testid="$options.confidentialityTestId"
@action="handleToggleWorkItemConfidentiality"
>
<template #list-item>{{ confidentialItemText }}</template>
</gl-disclosure-dropdown-item>
<gl-disclosure-dropdown-item
ref="workItemReference"
:data-testid="$options.copyReferenceTestId"
:data-clipboard-text="workItemReference"
@action="copyToClipboard(workItemReference, $options.i18n.referenceCopied)"
><template #list-item>{{
$options.i18n.copyReference
}}</template></gl-disclosure-dropdown-item
>
<template v-if="$options.isLoggedIn && workItemCreateNoteEmail">
<gl-disclosure-dropdown-item
ref="workItemCreateNoteEmail"
:data-testid="$options.copyCreateNoteEmailTestId"
:data-clipboard-text="workItemCreateNoteEmail"
@action="copyToClipboard(workItemCreateNoteEmail, $options.i18n.emailAddressCopied)"
><template #list-item>{{
i18n.copyCreateNoteEmail
}}</template></gl-disclosure-dropdown-item
>
</template>
<gl-dropdown-divider v-if="canDelete" />
<gl-disclosure-dropdown-item
v-if="canDelete"
:data-testid="$options.deleteActionTestId"
variant="danger"
@action="handleDelete"
>
<template #list-item
><span class="text-danger">{{ i18n.deleteWorkItem }}</span></template
>
<template #list-item>{{ $options.i18n.copyReference }}</template>
</gl-disclosure-dropdown-item>
<gl-disclosure-dropdown-item
v-if="$options.isLoggedIn && workItemCreateNoteEmail"
:data-testid="$options.copyCreateNoteEmailTestId"
:data-clipboard-text="workItemCreateNoteEmail"
@action="copyToClipboard(workItemCreateNoteEmail, $options.i18n.emailAddressCopied)"
>
<template #list-item>{{ i18n.copyCreateNoteEmail }}</template>
</gl-disclosure-dropdown-item>
<template v-if="canDelete">
<gl-dropdown-divider />
<gl-disclosure-dropdown-item
:data-testid="$options.deleteActionTestId"
variant="danger"
@action="handleDelete"
>
<template #list-item>
<span class="text-danger">{{ i18n.deleteWorkItem }}</span>
</template>
</gl-disclosure-dropdown-item>
</template>
</gl-disclosure-dropdown>
<gl-modal
ref="modal"
modal-id="work-item-confirm-delete"

View File

@ -238,7 +238,6 @@ export default {
<template>
<div v-if="!isLoading" class="gl-mt-3">
<awards-list
data-testid="work-item-award-list"
:awards="awards"
:can-award-emoji="$options.isLoggedIn"
:current-user-id="currentUserId"

View File

@ -244,13 +244,7 @@ export default {
@keydown.ctrl.enter="updateWorkItem"
/>
<div class="gl-display-flex">
<gl-alert
v-if="hasConflicts"
:dismissible="false"
variant="danger"
class="gl-w-full"
data-testid="work-item-description-conflicts"
>
<gl-alert v-if="hasConflicts" :dismissible="false" variant="danger" class="gl-w-full">
<p>
{{
s__(

View File

@ -409,7 +409,7 @@ export default {
</gl-skeleton-loader>
</div>
<template v-else>
<div class="gl-display-flex gl-align-items-center" data-testid="work-item-body">
<div class="gl-display-flex gl-align-items-center">
<ul
v-if="parentWorkItem"
class="list-unstyled gl-display-flex gl-min-w-0 gl-mr-auto gl-mb-0 gl-z-index-0"

View File

@ -225,7 +225,6 @@ export default {
<gl-dropdown
v-else
id="milestone-value"
data-testid="work-item-milestone-dropdown"
class="gl-pl-0 gl-max-w-full work-item-field-value"
:toggle-class="dropdownClasses"
:text="dropdownText"

View File

@ -104,10 +104,7 @@ export default {
</script>
<template>
<gl-button
:loading="updateInProgress"
data-testid="work-item-state-toggle"
@click="updateWorkItem"
>{{ toggleWorkItemStateText }}</gl-button
>
<gl-button :loading="updateInProgress" @click="updateWorkItem">{{
toggleWorkItemStateText
}}</gl-button>
</template>

View File

@ -175,17 +175,12 @@ export default {
<template>
<gl-button
v-gl-tooltip.hover
data-testid="work-item-todos-action"
:loading="isLoading"
:title="buttonLabel"
category="tertiary"
:aria-label="buttonLabel"
@click="onToggle"
>
<gl-icon
data-testid="work-item-todos-icon"
:class="{ 'gl-fill-blue-500': pendingTodo }"
:name="buttonIcon"
/>
<gl-icon :class="{ 'gl-fill-blue-500': pendingTodo }" :name="buttonIcon" />
</gl-button>
</template>

View File

@ -246,7 +246,6 @@ export const WORK_ITEM_ACTIVITY_SORT_OPTIONS = [
];
export const TEST_ID_CONFIDENTIALITY_TOGGLE_ACTION = 'confidentiality-toggle-action';
export const TEST_ID_NOTIFICATIONS_TOGGLE_ACTION = 'notifications-toggle-action';
export const TEST_ID_NOTIFICATIONS_TOGGLE_FORM = 'notifications-toggle-form';
export const TEST_ID_DELETE_ACTION = 'delete-action';
export const TEST_ID_PROMOTE_ACTION = 'promote-action';

View File

@ -6,22 +6,130 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Audit events **(PREMIUM ALL)**
Use audit events to track important events, including who performed the related action and when.
You can use audit events to track, for example:
A security audit is a in-depth analysis and review of your infrastructure, which is used to display
areas of concern and potentially hazardous practices. To assist with the audit process, GitLab provides
audit events which allow you to track a variety of different actions within GitLab.
For example, you can use audit events to track:
- Who changed the permission level of a particular user for a GitLab project, and when.
- Who added a new user or removed a user, and when.
Audit events are similar to the [log system](logs/index.md).
These events can be used to in an audit to assess risk, strengthen security measures, respond to incidents, and adhere to compliance. For a complete list the audit events GitLab provides, see [Audit event types](../administration/audit_event_streaming/audit_event_types.md).
The GitLab API, database, and `audit_json.log` record many audit events. Some audit events are only available through
[streaming audit events](audit_event_streaming.md).
## Prerequisites
You can also generate an [audit report](audit_reports.md) of audit events.
To view specific types of audit events, you need a minimum role.
NOTE:
You can't configure a retention policy for audit events, but epic
[7917](https://gitlab.com/groups/gitlab-org/-/epics/7917) proposes to change this.
- To view the group audit events of all users in a group, you must have the [Owner role](../user/permissions.md#roles) for the group.
- To view the project audit events of all users in a project, you must have at least the [Maintainer role](../user/permissions.md#roles) for the project.
- To view the group and project audit events based on your own actions in a group or project, you must have at least the [Developer role](../user/permissions.md#roles)
for the group or project.
Users with the [Auditor access level](auditor_users.md) can see group and project events for all users.
## Viewing audit events
Audit events can be viewed at the group, project, instance, and sign-in level. Each level has different audit events which it logs.
### Group audit events
To view a group's audit events:
1. On the left sidebar, select **Search or go to** and find your group.
1. On the left sidebar, select **Secure > Audit events**.
1. Filter the audit events by the member of the project (user) who performed the action and date range.
Group audit events can also be accessed using the [Group Audit Events API](../api/audit_events.md#group-audit-events). Group audit event queries are limited to a maximum of 30 days.
### Project audit events
1. On the left sidebar, select **Search or go to** and find your project.
1. On the left sidebar, select **Secure > Audit events**.
1. Filter the audit events by the member of the project (user) who performed the action and date range.
Project audit events can also be accessed using the [Project Audit Events API](../api/audit_events.md#project-audit-events). Project audit event queries are limited to a maximum of 30 days.
### Instance audit events **(PREMIUM SAAS)**
You can view audit events from user actions across an entire GitLab instance.
To view instance audit events:
1. On the left sidebar, select **Search or go to**.
1. Select **Admin Area**.
1. On the left sidebar, select **Monitoring > Audit Events**.
1. Filter by the following:
- Member of the project (user) who performed the action
- Group
- Project
- Date Range
### Sign-in audit events **(FREE ALL)**
Successful sign-in events are the only audit events available at all tiers. To see successful sign-in events:
1. On the left sidebar, select your avatar.
1. Select **Edit profile > Authentication log**.
After upgrading to a paid tier, you can also see successful sign-in events on audit event pages.
## Exporting audit events
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/1449) in GitLab 13.4.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/285441) in GitLab 13.7.
> - Entity type `Gitlab::Audit::InstanceScope` for instance audit events [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/418185) in GitLab 16.2.
You can export the current view (including filters) of your instance audit events as a
CSV(comma-separated values) file. To export the instance audit events to CSV:
1. On the left sidebar, select **Search or go to**.
1. Select **Admin Area**.
1. On the left sidebar, select **Monitoring > Audit Events**.
1. Select the available search filters.
1. Select **Export as CSV**.
A download confirmation dialog then appears for you to download the CSV file. The exported CSV is limited
to a maximum of 100000 events. The remaining records are truncated when this limit is reached.
### Audit event CSV encoding
The exported CSV file is encoded as follows:
- `,` is used as the column delimiter
- `"` is used to quote fields if necessary.
- `\n` is used to separate rows.
The first row contains the headers, which are listed in the following table along
with a description of the values:
| Column | Description |
| --------------------- | ---------------------------------------------------------------------------------- |
| **ID** | Audit event `id`. |
| **Author ID** | ID of the author. |
| **Author Name** | Full name of the author. |
| **Entity ID** | ID of the scope. |
| **Entity Type** | Type of the scope (`Project`, `Group`, `User`, or `Gitlab::Audit::InstanceScope`). |
| **Entity Path** | Path of the scope. |
| **Target ID** | ID of the target. |
| **Target Type** | Type of the target. |
| **Target Details** | Details of the target. |
| **Action** | Description of the action. |
| **IP Address** | IP address of the author who performed the action. |
| **Created At (UTC)** | Formatted as `YYYY-MM-DD HH:MM:SS`. |
All items are sorted by `created_at` in ascending order.
## User impersonation
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/536) in GitLab 13.0.
> - Impersonation session events included in group audit events in GitLab 14.8.
When a user is [impersonated](../administration/admin_area.md#user-impersonation), their actions are logged as audit events with the following additional details:
- Audit events include information about the impersonating administrator.
- Extra audit events are recorded for the start and end of the administrator's impersonation session.
![Audit event with impersonated user](img/impersonated_audit_events_v15_7.png)
## Time zones
@ -32,145 +140,12 @@ The time zone used for audit events depends on where you view them:
- In GitLab UI, your local time zone (GitLab 15.7 and later) or UTC (GitLab 15.6 and earlier) is used.
- The [Audit Events API](../api/audit_events.md) returns dates and times in UTC by default, or the
[configured time zone](timezone.md) on a self-managed GitLab instance.
- In `audit_json.log`, UTC is used.
- In CSV exports, UTC is used.
## View audit events
Depending on the events you want to view, at a minimum you must have:
- For group audit events of all users in the group, the Owner role for the group.
- For project audit events of all users in the project, the Maintainer role for the project.
- For group and project audit events based on your own actions, the Developer role for the group or project.
- [Auditor users](auditor_users.md) can see group and project events for all users.
You can view audit events scoped to a group or project.
To view a group's audit events:
1. Go to the group.
1. On the left sidebar, select **Secure > Audit events**.
Group events do not include project audit events. Group events can also be accessed using the
[Group Audit Events API](../api/audit_events.md#group-audit-events). Group event queries are limited to a maximum of 30
days.
To view a project's audit events:
1. Go to the project.
1. On the left sidebar, select **Secure > Audit events**.
Project events can also be accessed using the [Project Audit Events API](../api/audit_events.md#project-audit-events).
Project event queries are limited to a maximum of 30 days.
## View instance audit events **(PREMIUM SELF)**
You can view audit events from user actions across an entire GitLab instance.
To view instance audit events:
1. On the left sidebar, select **Search or go to**.
1. Select **Admin Area**.
1. On the left sidebar, select **Monitoring > Audit Events**.
### Export to CSV
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/1449) in GitLab 13.4.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/285441) in GitLab 13.7.
> - Entity type `Gitlab::Audit::InstanceScope` for instance audit events [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/418185) in GitLab 16.2.
You can export the current view (including filters) of your instance audit events as a CSV file. To export the instance
audit events to CSV:
1. On the left sidebar, select **Search or go to**.
1. Select **Admin Area**.
1. On the left sidebar, select **Monitoring > Audit Events**.
1. Select the available search [filters](#filter-audit-events).
1. Select **Export as CSV**.
The exported file:
- Is sorted by `created_at` in ascending order.
- Is limited to a maximum of 100 000 events. The remaining records are truncated when this limit is reached.
Data is encoded with:
- Comma as the column delimiter.
- `"` to quote fields if necessary.
- New lines separate rows.
The first row contains the headers, which are listed in the following table along with a description of the values:
| Column | Description |
|:---------------------|:-------------------------------------------------------------------|
| **ID** | Audit event `id`. |
| **Author ID** | ID of the author. |
| **Author Name** | Full name of the author. |
| **Entity ID** | ID of the scope. |
| **Entity Type** | Type of the scope (`Project`, `Group`, `User`, or `Gitlab::Audit::InstanceScope`). |
| **Entity Path** | Path of the scope. |
| **Target ID** | ID of the target. |
| **Target Type** | Type of the target. |
| **Target Details** | Details of the target. |
| **Action** | Description of the action. |
| **IP Address** | IP address of the author who performed the action. |
| **Created At (UTC)** | Formatted as `YYYY-MM-DD HH:MM:SS`. |
## View sign-in events **(FREE ALL)**
Successful sign-in events are the only audit events available at all tiers. To see successful sign-in events:
1. On the left sidebar, select your avatar.
1. Select **Edit profile > Authentication log**.
After upgrading to a paid tier, you can also see successful sign-in events on audit event pages.
## Filter audit events
From audit events pages, different filters are available depending on the page you're on.
| Audit event page | Available filter |
|:-----------------|:-----------------------------------------------------------------------------------------------------------------------|
| Project | User (member of the project) who performed the action. |
| Group | User (member of the group) who performed the action. |
| Instance | Group, project, or user. |
| All | Date range buttons and pickers (maximum range of 31 days). Default is from the first day of the month to today's date. |
## User impersonation
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/536) in GitLab 13.0.
> - Impersonation session events included in group audit events in GitLab 14.8.
When a user is [impersonated](../administration/admin_area.md#user-impersonation), their actions are logged as audit events
with additional details:
- Audit events include information about the impersonating administrator. These audit events are visible in audit event
pages depending on the audit event type (group, project, or user).
- Extra audit events are recorded for the start and end of the administrator's impersonation session. These audit events
are visible as:
- Instance audit events.
- Group audit events for all groups the user belongs to. For performance reasons, group audit events are limited to
the oldest 20 groups you belong to.
![Audit event with impersonated user](img/impersonated_audit_events_v15_7.png)
## Available audit events
For a list of available audit events, see [Audit event types](../administration/audit_event_streaming/audit_event_types.md).
## Unsupported events
Some events are not tracked in audit events. The following epics and issues propose support for more events:
- [Project settings and activity](https://gitlab.com/groups/gitlab-org/-/epics/474).
- [Group settings and activity](https://gitlab.com/groups/gitlab-org/-/epics/475).
- [Instance-level settings and activity](https://gitlab.com/groups/gitlab-org/-/epics/476).
- [Deployment Approval activity](https://gitlab.com/gitlab-org/gitlab/-/issues/354782).
- [Approval rules processing by a non GitLab user](https://gitlab.com/gitlab-org/gitlab/-/issues/407384).
## Contribute to audit events
If you don't see the event you want in any of the epics, you can either:
- Use the **Audit Event Proposal** issue template to
[create an issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new?issuable_template=Audit%20Event%20Proposal) to
request it.
[create an issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new?issuable_template=Audit%20Event%20Proposal) to request it.
- [Add it yourself](../development/audit_event_guide/index.md).

View File

@ -10,7 +10,7 @@ This document lists the different implementations of CSV export in GitLab codeba
| Export type | How it works | Advantages | Disadvantages | Existing examples |
|---|---|---|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Streaming | - Query and yield data in batches to a response stream.<br>- Download starts immediately. | - Report available immediately. | - No progress indicator.<br>- Requires a reliable connection. | [Export Audit Event Log](../administration/audit_events.md#export-to-csv) |
| Streaming | - Query and yield data in batches to a response stream.<br>- Download starts immediately. | - Report available immediately. | - No progress indicator.<br>- Requires a reliable connection. | [Export Audit Event Log](../administration/audit_events.md#exporting-audit-events) |
| Downloading | - Query and write data in batches to a temporary file.<br>- Loads the file into memory.<br>- Sends the file to the client. | - Report available immediately. | - Large amount of data might cause request timeout.<br>- Memory intensive.<br>- Request expires when user navigates to a different page. | - [Export Chain of Custody Report](../user/compliance/compliance_center/index.md#chain-of-custody-report)<br>- [Export License Usage File](../subscriptions/self_managed/index.md#export-your-license-usage) |
| As email attachment | - Asynchronously process the query with background job.<br>- Email uses the export as an attachment. | - Asynchronous processing. | - Requires users use a different app (email) to download the CSV.<br>- Email providers may limit attachment size. | - [Export issues](../user/project/issues/csv_export.md)<br>- [Export merge requests](../user/project/merge_requests/csv_export.md) |
| As downloadable link in email (*) | - Asynchronously process the query with background job.<br>- Email uses an export link. | - Asynchronous processing.<br>- Bypasses email provider attachment size limit. | - Requires users use a different app (email).<br>- Requires additional storage and cleanup. | [Export User Permissions](https://gitlab.com/gitlab-org/gitlab/-/issues/1772) |

View File

@ -787,7 +787,7 @@ and primarily the restore prerequisites. Then, follow the steps under the
## Updating GitLab
GitLab releases a new version every month on the 22nd. Whenever a new version is
GitLab releases a new version every month on the [release date](https://about.gitlab.com/releases/). Whenever a new version is
released, you can update your GitLab instance:
1. SSH into your instance

View File

@ -513,7 +513,7 @@ To disable third-party AI features for a group:
1. Select **Save changes**.
When Code Suggestions are enabled and disabled, an
[audit event](../../administration/audit_events.md#view-audit-events) is created.
[audit event](../../administration/audit_events.md) is created.
## Group activity analytics **(PREMIUM ALL)**

View File

@ -41983,6 +41983,9 @@ msgstr ""
msgid "ScanResultPolicy|Prevent branch protection modification"
msgstr ""
msgid "ScanResultPolicy|Prevent pushing and force pushing"
msgstr ""
msgid "ScanResultPolicy|Protected branch settings"
msgstr ""

View File

@ -49,8 +49,8 @@
"@apollo/client": "^3.5.10",
"@babel/core": "^7.18.5",
"@babel/preset-env": "^7.18.2",
"@cubejs-client/core": "^0.34.2",
"@cubejs-client/vue": "^0.34.2",
"@cubejs-client/core": "^0.34.9",
"@cubejs-client/vue": "^0.34.9",
"@floating-ui/dom": "^1.2.9",
"@gitlab/application-sdk-browser": "^0.2.8",
"@gitlab/at.js": "1.5.7",

View File

@ -19,9 +19,7 @@ RSpec.describe 'Work item', :js, feature_category: :team_planning do
context 'for signed in user' do
before do
project.add_developer(user)
sign_in(user)
visit work_items_path
end
@ -38,7 +36,7 @@ RSpec.describe 'Work item', :js, feature_category: :team_planning do
end
it 'actions dropdown is displayed' do
expect(page).to have_selector('[data-testid="work-item-actions-dropdown"]')
expect(page).to have_button _('More actions')
end
it 'reassigns to another user',
@ -75,9 +73,7 @@ RSpec.describe 'Work item', :js, feature_category: :team_planning do
context 'for signed in owner' do
before do
project.add_owner(user)
sign_in(user)
visit work_items_path
end
@ -87,9 +83,7 @@ RSpec.describe 'Work item', :js, feature_category: :team_planning do
context 'for guest users' do
before do
project.add_guest(user)
sign_in(user)
visit work_items_path
end
@ -114,14 +108,12 @@ RSpec.describe 'Work item', :js, feature_category: :team_planning do
end
it 'todos action is not displayed' do
expect(page).not_to have_selector('[data-testid="work-item-todos-action"]')
expect(page).not_to have_button s_('WorkItem|Add a to do')
end
it 'award button is disabled and add reaction is not displayed' do
within('[data-testid="work-item-award-list"]') do
expect(page).not_to have_selector('[data-testid="emoji-picker"]')
expect(page).to have_selector('[data-testid="award-button"].disabled')
end
expect(page).not_to have_button _('Add reaction')
expect(page).to have_selector('[data-testid="award-button"].disabled')
end
it 'assignees input field is disabled' do

View File

@ -1,4 +1,4 @@
import { GlDisclosureDropdown } from '@gitlab/ui';
import { GlButton, GlDisclosureDropdown } from '@gitlab/ui';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
@ -17,7 +17,7 @@ describe('Work Item Note Actions', () => {
const showSpy = jest.fn();
const findReplyButton = () => wrapper.findComponent(ReplyButton);
const findEditButton = () => wrapper.findByTestId('edit-work-item-note');
const findEditButton = () => wrapper.findComponent(GlButton);
const findEmojiButton = () => wrapper.findByTestId('note-emoji-button');
const findDropdown = () => wrapper.findComponent(GlDisclosureDropdown);
const findDeleteNoteButton = () => wrapper.findByTestId('delete-note-action');

View File

@ -12,7 +12,6 @@ import toast from '~/vue_shared/plugins/global_toast';
import WorkItemActions from '~/work_items/components/work_item_actions.vue';
import {
TEST_ID_CONFIDENTIALITY_TOGGLE_ACTION,
TEST_ID_NOTIFICATIONS_TOGGLE_ACTION,
TEST_ID_NOTIFICATIONS_TOGGLE_FORM,
TEST_ID_DELETE_ACTION,
TEST_ID_PROMOTE_ACTION,
@ -44,8 +43,6 @@ describe('WorkItemActions component', () => {
const findModal = () => wrapper.findComponent(GlModal);
const findConfidentialityToggleButton = () =>
wrapper.findByTestId(TEST_ID_CONFIDENTIALITY_TOGGLE_ACTION);
const findNotificationsToggleButton = () =>
wrapper.findByTestId(TEST_ID_NOTIFICATIONS_TOGGLE_ACTION);
const findDeleteButton = () => wrapper.findByTestId(TEST_ID_DELETE_ACTION);
const findPromoteButton = () => wrapper.findByTestId(TEST_ID_PROMOTE_ACTION);
const findCopyReferenceButton = () => wrapper.findByTestId(TEST_ID_COPY_REFERENCE_ACTION);
@ -248,7 +245,7 @@ describe('WorkItemActions component', () => {
});
it('renders toggle button', () => {
expect(findNotificationsToggleButton().exists()).toBe(true);
expect(findNotificationsToggle().exists()).toBe(true);
});
it.each`

View File

@ -560,6 +560,7 @@ RSpec.shared_examples 'edits content using the content editor' do |params = {
it 'adds the correct prefix for /assign' do
type_in_content_editor '/assign'
expect(find(suggestions_dropdown)).to have_text('/assign')
send_keys [:arrow_down, :enter]
expect(page).to have_text('/assign @')
@ -568,6 +569,7 @@ RSpec.shared_examples 'edits content using the content editor' do |params = {
it 'adds the correct prefix for /label' do
type_in_content_editor '/label'
expect(find(suggestions_dropdown)).to have_text('/label')
send_keys [:arrow_down, :enter]
expect(page).to have_text('/label ~')
@ -576,6 +578,7 @@ RSpec.shared_examples 'edits content using the content editor' do |params = {
it 'adds the correct prefix for /milestone' do
type_in_content_editor '/milestone'
expect(find(suggestions_dropdown)).to have_text('/milestone')
send_keys [:arrow_down, :enter]
expect(page).to have_text('/milestone %')
@ -597,6 +600,39 @@ RSpec.shared_examples 'edits content using the content editor' do |params = {
expect(page).to have_text('@abc123')
end
it 'allows dismissing the suggestion popup and typing more text' do
type_in_content_editor '@ab'
expect(find(suggestions_dropdown)).to have_text('abc123')
send_keys :escape
expect(page).not_to have_css(suggestions_dropdown)
type_in_content_editor :enter
type_in_content_editor 'foobar'
# ensure that the texts are in separate paragraphs
expect(page).to have_selector('p', text: '@ab')
expect(page).to have_selector('p', text: 'foobar')
expect(page).not_to have_selector('p', text: '@abfoobar')
end
it 'allows typing more text after the popup has disappeared because no suggestions match' do
type_in_content_editor '@ab'
expect(find(suggestions_dropdown)).to have_text('abc123')
type_in_content_editor 'foo'
type_in_content_editor :enter
type_in_content_editor 'bar'
# ensure that the texts are in separate paragraphs
expect(page).to have_selector('p', text: '@abfoo')
expect(page).to have_selector('p', text: 'bar')
expect(page).not_to have_selector('p', text: '@abfoobar')
end
context 'when `disable_all_mention` is enabled' do
before do
stub_feature_flags(disable_all_mention: true)

View File

@ -8,7 +8,6 @@ RSpec.shared_examples 'work items title' do
find(title_selector).set("Work item title")
find(title_selector).native.send_keys(:return)
wait_for_requests
expect(work_item.reload.title).to eq 'Work item title'
@ -16,54 +15,37 @@ RSpec.shared_examples 'work items title' do
end
RSpec.shared_examples 'work items toggle status button' do
let(:state_button) { '[data-testid="work-item-state-toggle"]' }
it 'successfully shows and changes the status of the work item' do
expect(find(state_button, match: :first)).to have_content 'Close'
click_button 'Close', match: :first
find(state_button, match: :first).click
wait_for_requests
expect(find(state_button, match: :first)).to have_content 'Reopen'
expect(page).to have_button 'Reopen'
expect(work_item.reload.state).to eq('closed')
end
end
RSpec.shared_examples 'work items comments' do |type|
let(:form_selector) { '[data-testid="work-item-add-comment"]' }
let(:edit_button) { '[data-testid="edit-work-item-note"]' }
let(:textarea_selector) { '[data-testid="work-item-add-comment"] #work-item-add-or-edit-comment' }
let(:is_mac) { page.evaluate_script('navigator.platform').include?('Mac') }
let(:modifier_key) { is_mac ? :command : :control }
let(:comment) { 'Test comment' }
def set_comment
find(form_selector).fill_in(with: comment)
fill_in _('Add a reply'), with: 'Test comment'
end
it 'successfully creates and shows comments' do
set_comment
click_button "Comment"
wait_for_requests
page.within(".main-notes-list") do
expect(page).to have_content comment
expect(page).to have_text 'Test comment'
end
end
it 'successfully updates existing comments' do
set_comment
click_button "Comment"
wait_for_all_requests
find(edit_button).click
click_button _('Edit comment')
send_keys(" updated")
click_button "Save comment"
wait_for_all_requests
click_button _('Save comment')
page.within(".main-notes-list") do
expect(page).to have_content "Test comment updated"
@ -79,39 +61,31 @@ RSpec.shared_examples 'work items comments' do |type|
it 'shows work item note actions' do
set_comment
send_keys([modifier_key, :enter])
wait_for_requests
page.within(".main-notes-list") do
expect(page).to have_content comment
expect(page).to have_text 'Test comment'
end
page.within('.timeline-entry.note.note-wrapper.note-comment:last-child') do
expect(page).to have_selector('[data-testid="work-item-note-actions"]')
click_button _('More actions')
find('[data-testid="work-item-note-actions"]').click
expect(page).to have_selector('[data-testid="copy-link-action"]')
expect(page).to have_selector('[data-testid="assign-note-action"]')
expect(page).to have_selector('[data-testid="delete-note-action"]')
expect(page).to have_selector('[data-testid="edit-work-item-note"]')
expect(page).to have_button _('Copy link')
expect(page).to have_button _('Assign to commenting user')
expect(page).to have_button _('Delete comment')
expect(page).to have_button _('Edit comment')
end
end
end
it 'successfully posts comments using shortcut and checks if textarea is blank when reinitiated' do
set_comment
send_keys([modifier_key, :enter])
wait_for_requests
page.within(".main-notes-list") do
expect(page).to have_content comment
expect(page).to have_content 'Test comment'
end
expect(find(textarea_selector)).to have_content ""
expect(page).to have_field _('Add a reply'), with: ''
end
context 'when using quick actions' do
@ -158,9 +132,7 @@ RSpec.shared_examples 'work items comments' do |type|
end
def click_reply_and_enter_slash
find(form_selector).fill_in(with: "/")
wait_for_all_requests
fill_in _('Add a reply'), with: '/'
end
end
end
@ -171,7 +143,6 @@ RSpec.shared_examples 'work items assignees' do
# The button is only when the mouse is over the input
find('[data-testid="work-item-assignees-input"]').fill_in(with: user.username)
wait_for_requests
# submit and simulate blur to save
send_keys(:enter)
find("body").click
@ -182,21 +153,19 @@ RSpec.shared_examples 'work items assignees' do
it 'successfully assigns the current user by clicking `Assign myself` button' do
find('[data-testid="work-item-assignees-input"]').hover
find('[data-testid="assign-self"]').click
wait_for_requests
click_button _('Assign yourself')
expect(work_item.reload.assignees).to include(user)
end
it 'successfully removes all users on clear all button click' do
find('[data-testid="work-item-assignees-input"]').hover
find('[data-testid="assign-self"]').click
wait_for_requests
click_button _('Assign yourself')
expect(work_item.reload.assignees).to include(user)
find('[data-testid="work-item-assignees-input"]').click
find('[data-testid="clear-all-button"]').click
click_button 'Clear all'
find("body").click
wait_for_requests
@ -205,13 +174,12 @@ RSpec.shared_examples 'work items assignees' do
it 'successfully removes user on clicking badge cross button' do
find('[data-testid="work-item-assignees-input"]').hover
find('[data-testid="assign-self"]').click
wait_for_requests
click_button _('Assign yourself')
expect(work_item.reload.assignees).to include(user)
within('[data-testid="work-item-assignees-input"]') do
find('[data-testid="close-icon"]').click
click_button 'Close'
end
find("body").click
wait_for_requests
@ -228,11 +196,9 @@ RSpec.shared_examples 'work items assignees' do
end
find('[data-testid="work-item-assignees-input"]').hover
find('[data-testid="assign-self"]').click
wait_for_requests
click_button _('Assign yourself')
expect(work_item.reload.assignees).to include(user)
using_session :other_session do
expect(work_item.reload.assignees).to include(user)
end
@ -246,7 +212,6 @@ RSpec.shared_examples 'work items labels' do
it 'successfully assigns a label' do
find(labels_input_selector).fill_in(with: label.title)
wait_for_requests
# submit and simulate blur to save
send_keys(:enter)
find(label_title_selector).click
@ -276,7 +241,6 @@ RSpec.shared_examples 'work items labels' do
it 'removes all labels on clear all button click' do
find(labels_input_selector).fill_in(with: label.title)
wait_for_requests
send_keys(:enter)
find(label_title_selector).click
wait_for_requests
@ -285,9 +249,8 @@ RSpec.shared_examples 'work items labels' do
within(labels_input_selector) do
find('input').click
find('[data-testid="clear-all-button"]').click
click_button 'Clear all'
end
find(label_title_selector).click
wait_for_requests
@ -297,7 +260,6 @@ RSpec.shared_examples 'work items labels' do
it 'removes label on clicking badge cross button' do
find(labels_input_selector).fill_in(with: label.title)
wait_for_requests
send_keys(:enter)
find(label_title_selector).click
wait_for_requests
@ -305,9 +267,8 @@ RSpec.shared_examples 'work items labels' do
expect(page).to have_text(label.title)
within(labels_input_selector) do
find('[data-testid="close-icon"]').click
click_button 'Remove label'
end
find(label_title_selector).click
wait_for_requests
@ -324,7 +285,6 @@ RSpec.shared_examples 'work items labels' do
find(labels_input_selector).fill_in(with: label.title)
wait_for_requests
send_keys(:enter)
find(label_title_selector).click
wait_for_requests
@ -341,10 +301,7 @@ end
RSpec.shared_examples 'work items description' do
it 'shows GFM autocomplete', :aggregate_failures do
click_button "Edit description"
find('[aria-label="Description"]').send_keys("@#{user.username}")
wait_for_requests
fill_in _('Description'), with: "@#{user.username}"
page.within('.atwho-container') do
expect(page).to have_text(user.name)
@ -353,10 +310,7 @@ RSpec.shared_examples 'work items description' do
it 'autocompletes available quick actions', :aggregate_failures do
click_button "Edit description"
find('[aria-label="Description"]').send_keys("/")
wait_for_requests
fill_in _('Description'), with: '/'
page.within('#at-view-commands') do
expect(page).to have_text("title")
@ -378,8 +332,6 @@ RSpec.shared_examples 'work items description' do
it 'shows conflict message when description changes', :aggregate_failures do
click_button "Edit description"
wait_for_requests
::WorkItems::UpdateService.new(
container: work_item.project,
current_user: other_user,
@ -388,11 +340,11 @@ RSpec.shared_examples 'work items description' do
wait_for_requests
find('[aria-label="Description"]').send_keys("oh yeah!")
fill_in _('Description'), with: 'oh yeah!'
expect(page.find('[data-testid="work-item-description-conflicts"]')).to have_text(expected_warning)
expect(page).to have_text(expected_warning)
click_button "Save and overwrite"
click_button s_('WorkItem|Save and overwrite')
expect(page.find('[data-testid="work-item-description"]')).to have_text("oh yeah!")
end
@ -410,32 +362,23 @@ RSpec.shared_examples 'work items invite members' do
click_button('Invite members')
page.within invite_modal_selector do
expect(page).to have_content("You're inviting members to the #{work_item.project.name} project")
expect(page).to have_text("You're inviting members to the #{work_item.project.name} project")
end
end
end
RSpec.shared_examples 'work items milestone' do
def set_milestone(milestone_dropdown, milestone_text)
milestone_dropdown.click
find('[data-testid="work-item-milestone-dropdown"] .gl-form-input', visible: true).send_keys "\"#{milestone_text}\""
wait_for_requests
click_button(milestone_text)
wait_for_requests
end
let(:milestone_dropdown_selector) { '[data-testid="work-item-milestone-dropdown"]' }
it 'searches and sets or removes milestone for the work item' do
set_milestone(find(milestone_dropdown_selector), milestone.title)
click_button s_('WorkItem|Add to milestone')
send_keys "\"#{milestone.title}\""
click_button milestone.title
expect(page.find(milestone_dropdown_selector)).to have_text(milestone.title)
expect(page).to have_button(milestone.title)
set_milestone(find(milestone_dropdown_selector), 'No milestone')
click_button milestone.title
click_button s_('WorkItem|No milestone')
expect(page.find(milestone_dropdown_selector)).to have_text('Add to milestone')
expect(page).to have_button(s_('WorkItem|Add to milestone'))
end
end
@ -443,56 +386,35 @@ RSpec.shared_examples 'work items comment actions for guest users' do
context 'for guest user' do
it 'hides other actions other than copy link' do
page.within(".main-notes-list") do
expect(page).to have_selector('[data-testid="work-item-note-actions"]')
click_button _('More actions'), match: :first
find('[data-testid="work-item-note-actions"]', match: :first).click
expect(page).to have_selector('[data-testid="copy-link-action"]')
expect(page).not_to have_selector('[data-testid="assign-note-action"]')
expect(page).to have_button _('Copy link')
expect(page).not_to have_button _('Assign to commenting user')
end
end
end
end
RSpec.shared_examples 'work items notifications' do
let(:actions_dropdown_selector) { '[data-testid="work-item-actions-dropdown"]' }
let(:notifications_toggle_selector) { '[data-testid="notifications-toggle-action"] button[role="switch"]' }
it 'displays toast when notification is toggled' do
find(actions_dropdown_selector).click
click_button _('More actions'), match: :first
page.within('[data-testid="notifications-toggle-form"]') do
expect(page).not_to have_css(".is-checked")
expect(page).not_to have_button(class: 'gl-toggle is-checked')
find(notifications_toggle_selector).click
wait_for_requests
click_button(class: 'gl-toggle')
expect(page).to have_css(".is-checked")
end
page.within('.gl-toast') do
expect(find('.toast-body')).to have_content(_('Notifications turned on.'))
end
expect(page).to have_button(class: 'gl-toggle is-checked')
expect(page).to have_css('.gl-toast', text: _('Notifications turned on.'))
end
end
RSpec.shared_examples 'work items todos' do
let(:todos_action_selector) { '[data-testid="work-item-todos-action"]' }
let(:todos_icon_selector) { '[data-testid="work-item-todos-icon"]' }
let(:header_section_selector) { '[data-testid="work-item-body"]' }
def toggle_todo_action
find(todos_action_selector).click
wait_for_requests
end
it 'adds item to the list' do
page.within(header_section_selector) do
expect(find(todos_action_selector)['aria-label']).to eq('Add a to do')
expect(page).to have_button s_('WorkItem|Add a to do')
toggle_todo_action
click_button s_('WorkItem|Add a to do')
expect(find(todos_action_selector)['aria-label']).to eq('Mark as done')
end
expect(page).to have_button s_('WorkItem|Mark as done')
page.within ".header-content span[aria-label='#{_('Todos count')}']" do
expect(page).to have_content '1'
@ -500,12 +422,10 @@ RSpec.shared_examples 'work items todos' do
end
it 'marks a todo as done' do
page.within(header_section_selector) do
toggle_todo_action
toggle_todo_action
end
click_button s_('WorkItem|Add a to do')
click_button s_('WorkItem|Mark as done')
expect(find(todos_action_selector)['aria-label']).to eq('Add a to do')
expect(page).to have_button s_('WorkItem|Add a to do')
expect(page).to have_selector(".header-content span[aria-label='#{_('Todos count')}']", visible: :hidden)
end
end
@ -514,7 +434,6 @@ RSpec.shared_examples 'work items award emoji' do
let(:award_section_selector) { '.awards' }
let(:award_button_selector) { '[data-testid="award-button"]' }
let(:selected_award_button_selector) { '[data-testid="award-button"].selected' }
let(:emoji_picker_button_selector) { '[data-testid="emoji-picker"]' }
let(:grinning_emoji_selector) { 'gl-emoji[data-name="grinning"]' }
let(:tooltip_selector) { '.gl-tooltip' }
@ -560,7 +479,7 @@ RSpec.shared_examples 'work items award emoji' do
it 'add custom award to the work item for current user' do
within(award_section_selector) do
find(emoji_picker_button_selector).click
click_button _('Add reaction')
find(grinning_emoji_selector).click
expect(page).to have_selector(grinning_emoji_selector)

View File

@ -1025,10 +1025,10 @@
resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-3.0.0.tgz#798622546b63847e82389e473fd67f2707d82247"
integrity sha512-hBI9tfBtuPIi885ZsZ32IMEU/5nlZH/KOVYJCOh7gyMxaVLGmLedYqFN6Ui1LXkI8JlC8IsuC0rF0btcRZKd5g==
"@cubejs-client/core@^0.34.2":
version "0.34.2"
resolved "https://registry.yarnpkg.com/@cubejs-client/core/-/core-0.34.2.tgz#a063a605a8c824bc465c786c7ef8d54d916aed1c"
integrity sha512-KnVw2w2TfOcnUJ6a79M+z9k83h7Bp+2NgX5ADwuIiJLU13HmT5p/BJGGo7A5BLOrRs25+e0SdJsGYmT/R0ViyA==
"@cubejs-client/core@^0.34.9":
version "0.34.9"
resolved "https://registry.yarnpkg.com/@cubejs-client/core/-/core-0.34.9.tgz#a402450b08e52ef7b87d44a6044910d94218783b"
integrity sha512-F1+VHnhZt3t+709PoFzaNdqKiQ1QaLAiDcU/S5/yZlohU4pYOL4I57nA/AaFqg9XTJd2/cUPCkvrxBtft3xoJA==
dependencies:
"@babel/runtime" "^7.1.2"
core-js "^3.6.5"
@ -1038,12 +1038,12 @@
url-search-params-polyfill "^7.0.0"
uuid "^8.3.2"
"@cubejs-client/vue@^0.34.2":
version "0.34.2"
resolved "https://registry.yarnpkg.com/@cubejs-client/vue/-/vue-0.34.2.tgz#bdc44763b9dbb828a2b22c9b2e84b28e35046275"
integrity sha512-9hOO8V9m1HxJqWomsJEg+O0GdNXmfmOXsSZPuUeFdPsR0mW6i3mOa3Blbo0HpmMdy5ui3Sc449gZC0+/dXKRXw==
"@cubejs-client/vue@^0.34.9":
version "0.34.9"
resolved "https://registry.yarnpkg.com/@cubejs-client/vue/-/vue-0.34.9.tgz#2a39db0099829304f9f42e6a2a19bc9be597cb8e"
integrity sha512-nbhaXvXdN9+NpFYdDsH+QVfDvD+sduRixoKRVbh3ImkIeMRcRAyv404+W62rQy3qhzINDivTnKNRtch1bS+xLA==
dependencies:
"@cubejs-client/core" "^0.34.2"
"@cubejs-client/core" "^0.34.9"
core-js "^3.6.5"
ramda "^0.27.2"