Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-10-31 06:10:18 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-10-31 06:10:18 +0300
commitb6cb3bc944b0ca803e758d9e9da3790c0f310d21 (patch)
treea0712bd4d614880aa1d3f724e39308f3fa7c4e2a
parent453bb35fc81741c1dd7178cde1f64df2c9ec7252 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/content_editor/components/suggestions_dropdown.vue2
-rw-r--r--app/assets/javascripts/content_editor/extensions/suggestions.js9
-rw-r--r--app/assets/javascripts/work_items/components/notes/work_item_comment_form.vue1
-rw-r--r--app/assets/javascripts/work_items/components/notes/work_item_note_actions.vue2
-rw-r--r--app/assets/javascripts/work_items/components/work_item_actions.vue79
-rw-r--r--app/assets/javascripts/work_items/components/work_item_award_emoji.vue1
-rw-r--r--app/assets/javascripts/work_items/components/work_item_description.vue8
-rw-r--r--app/assets/javascripts/work_items/components/work_item_detail.vue2
-rw-r--r--app/assets/javascripts/work_items/components/work_item_milestone.vue1
-rw-r--r--app/assets/javascripts/work_items/components/work_item_state_toggle_button.vue9
-rw-r--r--app/assets/javascripts/work_items/components/work_item_todos.vue7
-rw-r--r--app/assets/javascripts/work_items/constants.js1
-rw-r--r--doc/administration/audit_events.md181
-rw-r--r--doc/development/export_csv.md2
-rw-r--r--doc/install/aws/manual_install_aws.md2
-rw-r--r--doc/user/group/manage.md2
-rw-r--r--locale/gitlab.pot3
-rw-r--r--package.json4
-rw-r--r--spec/features/projects/work_items/work_item_spec.rb16
-rw-r--r--spec/frontend/work_items/components/notes/work_item_note_actions_spec.js4
-rw-r--r--spec/frontend/work_items/components/work_item_actions_spec.js5
-rw-r--r--spec/support/shared_examples/features/content_editor_shared_examples.rb36
-rw-r--r--spec/support/shared_examples/features/work_items_shared_examples.rb183
-rw-r--r--yarn.lock18
24 files changed, 245 insertions, 333 deletions
diff --git a/app/assets/javascripts/content_editor/components/suggestions_dropdown.vue b/app/assets/javascripts/content_editor/components/suggestions_dropdown.vue
index 6535d9eaa5d..b34ebe85eb4 100644
--- a/app/assets/javascripts/content_editor/components/suggestions_dropdown.vue
+++ b/app/assets/javascripts/content_editor/components/suggestions_dropdown.vue
@@ -161,6 +161,8 @@ export default {
},
onKeyDown({ event }) {
+ if (!this.items.length) return false;
+
if (event.key === 'ArrowUp') {
this.upHandler();
return true;
diff --git a/app/assets/javascripts/content_editor/extensions/suggestions.js b/app/assets/javascripts/content_editor/extensions/suggestions.js
index 0560322191e..f7ff2fd6647 100644
--- a/app/assets/javascripts/content_editor/extensions/suggestions.js
+++ b/app/assets/javascripts/content_editor/extensions/suggestions.js
@@ -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();
diff --git a/app/assets/javascripts/work_items/components/notes/work_item_comment_form.vue b/app/assets/javascripts/work_items/components/notes/work_item_comment_form.vue
index c7d8a50f402..62e1ebc50a8 100644
--- a/app/assets/javascripts/work_items/components/notes/work_item_comment_form.vue
+++ b/app/assets/javascripts/work_items/components/notes/work_item_comment_form.vue
@@ -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"
diff --git a/app/assets/javascripts/work_items/components/notes/work_item_note_actions.vue b/app/assets/javascripts/work_items/components/notes/work_item_note_actions.vue
index 2d36a89169a..cb9a560f9e1 100644
--- a/app/assets/javascripts/work_items/components/notes/work_item_note_actions.vue
+++ b/app/assets/javascripts/work_items/components/notes/work_item_note_actions.vue
@@ -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"
diff --git a/app/assets/javascripts/work_items/components/work_item_actions.vue b/app/assets/javascripts/work_items/components/work_item_actions.vue
index 08ad03ad4fe..88f82fb6fb7 100644
--- a/app/assets/javascripts/work_items/components/work_item_actions.vue
+++ b/app/assets/javascripts/work_items/components/work_item_actions.vue
@@ -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" />
+ <template #list-item>{{ $options.i18n.copyReference }}</template>
+ </gl-disclosure-dropdown-item>
+
<gl-disclosure-dropdown-item
- v-if="canDelete"
- :data-testid="$options.deleteActionTestId"
- variant="danger"
- @action="handleDelete"
+ v-if="$options.isLoggedIn && workItemCreateNoteEmail"
+ :data-testid="$options.copyCreateNoteEmailTestId"
+ :data-clipboard-text="workItemCreateNoteEmail"
+ @action="copyToClipboard(workItemCreateNoteEmail, $options.i18n.emailAddressCopied)"
>
- <template #list-item
- ><span class="text-danger">{{ i18n.deleteWorkItem }}</span></template
- >
+ <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"
diff --git a/app/assets/javascripts/work_items/components/work_item_award_emoji.vue b/app/assets/javascripts/work_items/components/work_item_award_emoji.vue
index fb710836a88..f806946509f 100644
--- a/app/assets/javascripts/work_items/components/work_item_award_emoji.vue
+++ b/app/assets/javascripts/work_items/components/work_item_award_emoji.vue
@@ -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"
diff --git a/app/assets/javascripts/work_items/components/work_item_description.vue b/app/assets/javascripts/work_items/components/work_item_description.vue
index 245099d958d..77c573b47e4 100644
--- a/app/assets/javascripts/work_items/components/work_item_description.vue
+++ b/app/assets/javascripts/work_items/components/work_item_description.vue
@@ -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__(
diff --git a/app/assets/javascripts/work_items/components/work_item_detail.vue b/app/assets/javascripts/work_items/components/work_item_detail.vue
index d95b434a829..c8ea2a12038 100644
--- a/app/assets/javascripts/work_items/components/work_item_detail.vue
+++ b/app/assets/javascripts/work_items/components/work_item_detail.vue
@@ -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"
diff --git a/app/assets/javascripts/work_items/components/work_item_milestone.vue b/app/assets/javascripts/work_items/components/work_item_milestone.vue
index 867d25702d8..fe09105e173 100644
--- a/app/assets/javascripts/work_items/components/work_item_milestone.vue
+++ b/app/assets/javascripts/work_items/components/work_item_milestone.vue
@@ -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"
diff --git a/app/assets/javascripts/work_items/components/work_item_state_toggle_button.vue b/app/assets/javascripts/work_items/components/work_item_state_toggle_button.vue
index 7edda2592de..6d7a93f64c4 100644
--- a/app/assets/javascripts/work_items/components/work_item_state_toggle_button.vue
+++ b/app/assets/javascripts/work_items/components/work_item_state_toggle_button.vue
@@ -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>
diff --git a/app/assets/javascripts/work_items/components/work_item_todos.vue b/app/assets/javascripts/work_items/components/work_item_todos.vue
index e6d7f2067ba..d5633adec9a 100644
--- a/app/assets/javascripts/work_items/components/work_item_todos.vue
+++ b/app/assets/javascripts/work_items/components/work_item_todos.vue
@@ -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>
diff --git a/app/assets/javascripts/work_items/constants.js b/app/assets/javascripts/work_items/constants.js
index 8de539f06f7..e4fa277485a 100644
--- a/app/assets/javascripts/work_items/constants.js
+++ b/app/assets/javascripts/work_items/constants.js
@@ -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';
diff --git a/doc/administration/audit_events.md b/doc/administration/audit_events.md
index 736f381e9d7..ba1a4ca05c4 100644
--- a/doc/administration/audit_events.md
+++ b/doc/administration/audit_events.md
@@ -6,171 +6,146 @@ 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).
-
-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).
+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).
-You can also generate an [audit report](audit_reports.md) of audit events.
+## Prerequisites
-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 specific types of audit events, you need a minimum role.
-## Time zones
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/242014) in GitLab 15.7, GitLab UI shows dates and times in the user's local time zone instead of UTC.
+- 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.
-The time zone used for audit events depends on where you view them:
+Users with the [Auditor access level](auditor_users.md) can see group and project events for all users.
-- 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.
+## Viewing audit events
-## View 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.
-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.
+### Group audit events
To view a group's audit events:
-1. Go to the group.
+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 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.
+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.
-To view a project's audit events:
+### Project audit events
-1. Go to the project.
+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 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.
+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.
-## View instance audit events **(PREMIUM SELF)**
+### 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**.
-### Export to CSV
+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 file. To export the instance
-audit events to CSV:
+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](#filter-audit-events).
+1. Select the available search filters.
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.
+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.
-The first row contains the headers, which are listed in the following table along with a description of the values:
+### Audit event CSV encoding
-| 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`. |
+The exported CSV file is encoded as follows:
-## View sign-in events **(FREE ALL)**
+- `,` is used as the column delimiter
+- `"` is used to quote fields if necessary.
+- `\n` is used to separate rows.
-Successful sign-in events are the only audit events available at all tiers. To see successful sign-in events:
+The first row contains the headers, which are listed in the following table along
+with a description of the values:
-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.
+| 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`. |
-## 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. |
+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 additional details:
+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. 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 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)
-## Available audit events
+## Time zones
-For a list of available audit events, see [Audit event types](../administration/audit_event_streaming/audit_event_types.md).
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/242014) in GitLab 15.7, GitLab UI shows dates and times in the user's local time zone instead of UTC.
-## Unsupported events
+The time zone used for audit events depends on where you view them:
-Some events are not tracked in audit events. The following epics and issues propose support for more events:
+- 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 CSV exports, UTC is used.
-- [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).
diff --git a/doc/development/export_csv.md b/doc/development/export_csv.md
index 9b0205166bf..ce0a6e026ff 100644
--- a/doc/development/export_csv.md
+++ b/doc/development/export_csv.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) |
diff --git a/doc/install/aws/manual_install_aws.md b/doc/install/aws/manual_install_aws.md
index a952180674c..cd42e689c41 100644
--- a/doc/install/aws/manual_install_aws.md
+++ b/doc/install/aws/manual_install_aws.md
@@ -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
diff --git a/doc/user/group/manage.md b/doc/user/group/manage.md
index d67e52a6fc3..b2ca9c35d37 100644
--- a/doc/user/group/manage.md
+++ b/doc/user/group/manage.md
@@ -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)**
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 8de70afd7fa..c98b7517327 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -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 ""
diff --git a/package.json b/package.json
index df5c27b8e9d..80b869465e2 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/spec/features/projects/work_items/work_item_spec.rb b/spec/features/projects/work_items/work_item_spec.rb
index 2419effab94..09a8636359d 100644
--- a/spec/features/projects/work_items/work_item_spec.rb
+++ b/spec/features/projects/work_items/work_item_spec.rb
@@ -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
diff --git a/spec/frontend/work_items/components/notes/work_item_note_actions_spec.js b/spec/frontend/work_items/components/notes/work_item_note_actions_spec.js
index 6a24987b737..596283a9590 100644
--- a/spec/frontend/work_items/components/notes/work_item_note_actions_spec.js
+++ b/spec/frontend/work_items/components/notes/work_item_note_actions_spec.js
@@ -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');
diff --git a/spec/frontend/work_items/components/work_item_actions_spec.js b/spec/frontend/work_items/components/work_item_actions_spec.js
index 15c33bf5b1e..b41014a236b 100644
--- a/spec/frontend/work_items/components/work_item_actions_spec.js
+++ b/spec/frontend/work_items/components/work_item_actions_spec.js
@@ -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`
diff --git a/spec/support/shared_examples/features/content_editor_shared_examples.rb b/spec/support/shared_examples/features/content_editor_shared_examples.rb
index edb8e6bb2ff..5271b5bd45c 100644
--- a/spec/support/shared_examples/features/content_editor_shared_examples.rb
+++ b/spec/support/shared_examples/features/content_editor_shared_examples.rb
@@ -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)
diff --git a/spec/support/shared_examples/features/work_items_shared_examples.rb b/spec/support/shared_examples/features/work_items_shared_examples.rb
index 69d6521e5a0..8c577749107 100644
--- a/spec/support/shared_examples/features/work_items_shared_examples.rb
+++ b/spec/support/shared_examples/features/work_items_shared_examples.rb
@@ -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"]')
-
- find('[data-testid="work-item-note-actions"]').click
+ click_button _('More actions')
- 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
-
- expect(page).to have_css(".is-checked")
- end
+ click_button(class: 'gl-toggle')
- 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)
diff --git a/yarn.lock b/yarn.lock
index a5067af2d15..ccdf9749b90 100644
--- a/yarn.lock
+++ b/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"