Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
453bb35fc8
commit
b6cb3bc944
|
|
@ -161,6 +161,8 @@ export default {
|
|||
},
|
||||
|
||||
onKeyDown({ event }) {
|
||||
if (!this.items.length) return false;
|
||||
|
||||
if (event.key === 'ArrowUp') {
|
||||
this.upHandler();
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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__(
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||

|
||||
|
||||
## 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.
|
||||
|
||||

|
||||
|
||||
## 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).
|
||||
|
|
|
|||
|
|
@ -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) |
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)**
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ""
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
18
yarn.lock
18
yarn.lock
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue