Add latest changes from gitlab-org/gitlab@15-3-stable-ee

This commit is contained in:
GitLab Bot 2022-09-01 14:15:38 +00:00
parent c8fd9c521b
commit 28b374eea4
26 changed files with 311 additions and 165 deletions

View File

@ -130,27 +130,6 @@ variables:
REGISTRY_HOST: "registry.gitlab.com"
REGISTRY_GROUP: "gitlab-org"
# Preparing custom clone path to reduce space used by all random forks
# on GitLab.com's Shared Runners. Our main forks - especially the security
# ones - will have this variable overwritten in the project settings, so that
# a security-related code or code using our protected variables will be never
# stored on the same path as the community forks.
# Part of the solution for the `no space left on device` problem described at
# https://gitlab.com/gitlab-org/gitlab/issues/197876.
#
# For this purpose the https://gitlab.com/gitlab-org-forks group was created
# to host a placeholder for the `/builds/gitlab-org-forks` path and ensure
# that no legitimate project will ever use it and - by mistake - execute its
# job on a shared working directory. It also requires proper configuration of
# the Runner that executes the job (which was prepared for our shared runners
# by https://ops.gitlab.net/gitlab-cookbooks/chef-repo/-/merge_requests/3977).
#
# Because of all of that PLEASE DO NOT CHANGE THE PATH.
#
# For more details and reasoning that brought this change please check
# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24887
GIT_CLONE_PATH: "/builds/gitlab-org-forks/${CI_PROJECT_NAME}"
include:
- local: .gitlab/ci/*.gitlab-ci.yml
- remote: 'https://gitlab.com/gitlab-org/frontend/untamper-my-lockfile/-/raw/main/templates/merge_request_pipelines.yml'

View File

@ -2,20 +2,10 @@
import { mapActions, mapGetters } from 'vuex';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { REVIEW_BAR_VISIBLE_CLASS_NAME } from '../constants';
import { PREVENT_LEAVING_PENDING_REVIEW } from '../i18n';
import PreviewDropdown from './preview_dropdown.vue';
import PublishButton from './publish_button.vue';
import SubmitDropdown from './submit_dropdown.vue';
function closeInterrupt(event) {
event.preventDefault();
// This is the correct way to write backwards-compatible beforeunload listeners
// https://developer.chrome.com/blog/page-lifecycle-api/#the-beforeunload-event
/* eslint-disable-next-line no-return-assign, no-param-reassign */
return (event.returnValue = PREVENT_LEAVING_PENDING_REVIEW);
}
export default {
components: {
PreviewDropdown,
@ -35,26 +25,8 @@ export default {
},
mounted() {
document.body.classList.add(REVIEW_BAR_VISIBLE_CLASS_NAME);
/*
* This stuff is a lot trickier than it looks.
*
* Mandatory reading: https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event
* Some notable sentences:
* - "[...] browsers may not display prompts created in beforeunload event handlers unless the
* page has been interacted with, or may even not display them at all."
* - "Especially on mobile, the beforeunload event is not reliably fired."
* - "The beforeunload event is not compatible with the back/forward cache (bfcache) [...]
* It is recommended that developers listen for beforeunload only in this scenario, and only
* when they actually have unsaved changes, so as to minimize the effect on performance."
*
* Please ensure that this is really not working before you modify it, because there are a LOT
* of scenarios where browser behavior will make it _seem_ like it's not working, but it actually
* is under the right combination of contexts.
*/
window.addEventListener('beforeunload', closeInterrupt, { capture: true });
},
beforeDestroy() {
window.removeEventListener('beforeunload', closeInterrupt, { capture: true });
document.body.classList.remove(REVIEW_BAR_VISIBLE_CLASS_NAME);
},
methods: {

View File

@ -1,3 +0,0 @@
import { __ } from '~/locale';
export const PREVENT_LEAVING_PENDING_REVIEW = __('There are unsubmitted review comments.');

View File

@ -1,12 +1,9 @@
import { isEmpty } from 'lodash';
import createFlash from '~/flash';
import { scrollToElement } from '~/lib/utils/common_utils';
import { __ } from '~/locale';
import { CHANGES_TAB, DISCUSSION_TAB, SHOW_TAB } from '../../../constants';
import service from '../../../services/drafts_service';
import * as types from './mutation_types';
export const saveDraft = ({ dispatch }, draft) =>
@ -18,7 +15,6 @@ export const addDraftToDiscussion = ({ commit }, { endpoint, data }) =>
.then((res) => res.data)
.then((res) => {
commit(types.ADD_NEW_DRAFT, res);
return res;
})
.catch(() => {
@ -33,7 +29,6 @@ export const createNewDraft = ({ commit }, { endpoint, data }) =>
.then((res) => res.data)
.then((res) => {
commit(types.ADD_NEW_DRAFT, res);
return res;
})
.catch(() => {

View File

@ -0,0 +1,31 @@
function addBlameLink(containerSelector, linkClass) {
const containerEl = document.querySelector(containerSelector);
if (!containerEl) {
return;
}
containerEl.addEventListener('mouseover', (e) => {
const isLineLink = e.target.classList.contains(linkClass);
if (isLineLink) {
const lineLink = e.target;
const lineLinkCopy = lineLink.cloneNode(true);
lineLinkCopy.classList.remove(linkClass, 'diff-line-num');
const { lineNumber } = lineLink.dataset;
const { blamePath } = document.querySelector('.line-numbers').dataset;
const blameLink = document.createElement('a');
blameLink.classList.add('file-line-blame');
blameLink.href = `${blamePath}#L${lineNumber}`;
const wrapper = document.createElement('div');
wrapper.classList.add('line-links', 'diff-line-num');
wrapper.appendChild(blameLink);
wrapper.appendChild(lineLinkCopy);
lineLink.replaceWith(wrapper);
}
});
}
export default addBlameLink;

View File

@ -1,7 +1,12 @@
import Tracking from '~/tracking';
function addBlobLinksTracking(containerSelector, eventsToTrack) {
const containerEl = document.querySelector(containerSelector);
const eventsToTrack = [
{ selector: '.file-line-blame', property: 'blame' },
{ selector: '.file-line-num', property: 'link' },
];
function addBlobLinksTracking() {
const containerEl = document.querySelector('.file-holder');
if (!containerEl) {
return;

View File

@ -4,7 +4,6 @@ import BlobForkSuggestion from '~/blob/blob_fork_suggestion';
import BlobLinePermalinkUpdater from '~/blob/blob_line_permalink_updater';
import LineHighlighter from '~/blob/line_highlighter';
import initBlobBundle from '~/blob_edit/blob_bundle';
import addBlobLinksTracking from '~/blob/blob_links_tracking';
export default () => {
new LineHighlighter(); // eslint-disable-line no-new
@ -16,12 +15,6 @@ export default () => {
document.querySelectorAll('.js-data-file-blob-permalink-url, .js-blob-blame-link'),
);
const eventsToTrack = [
{ selector: '.file-line-blame', property: 'blame' },
{ selector: '.file-line-num', property: 'link' },
];
addBlobLinksTracking('#blob-content-holder', eventsToTrack);
const fileBlobPermalinkUrlElement = document.querySelector('.js-data-file-blob-permalink-url');
const fileBlobPermalinkUrl =
fileBlobPermalinkUrlElement && fileBlobPermalinkUrlElement.getAttribute('href');

View File

@ -13,6 +13,7 @@ import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import WebIdeLink from '~/vue_shared/components/web_ide_link.vue';
import CodeIntelligence from '~/code_navigation/components/app.vue';
import LineHighlighter from '~/blob/line_highlighter';
import addBlameLink from '~/blob/blob_blame_link';
import getRefMixin from '../mixins/get_ref';
import blobInfoQuery from '../queries/blob_info.query.graphql';
import userInfoQuery from '../queries/user_info.query.graphql';
@ -242,6 +243,7 @@ export default {
if (type === SIMPLE_BLOB_VIEWER) {
new LineHighlighter(); // eslint-disable-line no-new
addBlameLink('.file-holder', 'js-line-links');
}
});

View File

@ -3,6 +3,7 @@ import { GlSafeHtmlDirective, GlLoadingIcon } from '@gitlab/ui';
import LineHighlighter from '~/blob/line_highlighter';
import eventHub from '~/notes/event_hub';
import languageLoader from '~/content_editor/services/highlight_js_language_loader';
import addBlobLinksTracking from '~/blob/blob_links_tracking';
import Tracking from '~/tracking';
import {
EVENT_ACTION,
@ -66,6 +67,7 @@ export default {
},
},
async created() {
addBlobLinksTracking();
this.trackEvent(EVENT_LABEL_VIEWER);
if (this.unsupportedLanguage) {

View File

@ -95,23 +95,14 @@ td.line-numbers {
.blob-viewer {
.line-numbers {
min-width: 6rem;
// for server-side-rendering
.line-links {
@include gl-display-flex;
&:first-child {
margin-top: 10px;
}
&:last-child {
margin-bottom: 10px;
}
}
// for client
&.line-links {
min-width: 6rem;
border-bottom-left-radius: 0;
+ pre {
@ -120,15 +111,15 @@ td.line-numbers {
}
}
.line-links {
&:hover a::before,
&:focus-within a::before {
@include gl-visibility-visible;
}
.line-numbers:not(.line-links) a:hover::before,
.line-numbers:not(.line-links) a:focus-within::before,
.line-links:hover a::before,
.line-links:focus-within a::before {
@include gl-visibility-visible;
}
.file-line-num {
min-width: 4.5rem;
@include gl-justify-content-end;
@include gl-flex-grow-1;
@include gl-pr-3;

View File

@ -88,6 +88,12 @@ module EventsHelper
end
end
def event_target_path(event)
return Gitlab::UrlBuilder.build(event.target, only_path: true) if event.work_item?
event.target_link_options
end
def event_feed_title(event)
words = []
words << event.author_name

View File

@ -8,7 +8,7 @@
%span.event-type.d-inline-block.gl-mr-2{ class: event.action_name }
= event.action_name
%span.event-target-type.gl-mr-2= event.target_type_name
= link_to event.target_link_options, class: 'has-tooltip event-target-link gl-mr-2', title: event.target_title do
= link_to event_target_path(event), class: 'has-tooltip event-target-link gl-mr-2', title: event.target_title do
= event.target.reference_link_text
- unless event.milestone?
%span.event-target-title.gl-text-overflow-ellipsis.gl-overflow-hidden.gl-mr-2{ dir: "auto" }

View File

@ -1,17 +1,14 @@
#blob-content.file-content.code.js-syntax-highlight
- offset = defined?(first_line_number) ? first_line_number : 1
.line-numbers{ class: "gl-p-0\!" }
- blame_path = project_blame_path(@project, tree_join(@ref, blob.path))
.line-numbers{ class: "gl-px-0!", data: { blame_path: blame_path } }
- if blob.data.present?
- link = blob_link if defined?(blob_link)
- blame_link = project_blame_path(@project, tree_join(@ref, blob.path))
- blob.data.each_line.each_with_index do |_, index|
- i = index + offset
-# We're not using `link_to` because it is too slow once we get to thousands of lines.
.line-links.diff-line-num
- if Feature.enabled?(:file_line_blame)
%a.file-line-blame{ href: "#{blame_link}#L#{i}" }
%a.file-line-num{ href: "#{link}#L#{i}", id: "L#{i}", 'data-line-number' => i }
= i
%a.file-line-num.diff-line-num{ class: ("js-line-links" if Feature.enabled?(:file_line_blame)), href: "#{link}#L#{i}", id: "L#{i}", 'data-line-number' => i }
= i
- highlight = defined?(highlight_line) && highlight_line ? highlight_line - offset : nil
.blob-content{ data: { blob_id: blob.id, path: blob.path, highlight_line: highlight, qa_selector: 'file_content' } }
%pre.code.highlight

View File

@ -0,0 +1,70 @@
- name: "Create tasks in issues"
description: |
Tasks provide a robust way to refine an issue into smaller, discrete work units. Previously in GitLab, you could break down an issue into smaller parts using markdown checklists within the description. However, these checklist items could not be easily assigned, labeled, or managed anywhere outside of the description field.
You can now create tasks within issues from the Child Items widget. Then, you can open the task directly within the issue to quickly update the title, set the weight, or add a description. Tasks break down work within projects for GitLab Free and increase the planning hierarchy for our GitLab Premium customers to three levels (epic, issue, and task). In our next iteration, you will be able to add labels, milestones, and iterations to each task.
Tasks represent our first step toward evolving issues, epics, incidents, requirements, and test cases to [work items](https://docs.gitlab.com/ee/development/work_items.html). If you have feedback or suggestions about tasks, please comment on [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/363613).
stage: plan
self-managed: true
gitlab-com: true
available_in: [Free, Premium, Ultimate]
documentation_link: https://docs.gitlab.com/ee/user/tasks.html
image_url: https://about.gitlab.com/images/15_3/create-tasks.gif
published_at: 2022-08-22
release: 15.3
- name: "GitOps features are now free"
description: |
When you use GitOps to update a Kubernetes cluster, also called a pull-based deployment, you get an improved security model, better scalability and stability.
The GitLab agent for Kubernetes has supported [GitOps workflows](https://docs.gitlab.com/ee/user/clusters/agent/gitops.html) from its initial release, but until now, the functionality was available only if you had a GitLab Premium or Ultimate subscription. Now if you have a Free subscription, you also get pull-based deployment support. The features available in GitLab Free should serve small, high-trust teams or be suitable to test the agent before upgrading to a higher tier.
In the future, we plan to add [built-in multi-tenant support](https://gitlab.com/gitlab-org/gitlab/-/issues/337904) for Premium subscriptions. This feature would be similar to the impersonation feature already available for the [CI/CD workflow](https://docs.gitlab.com/ee/user/clusters/agent/ci_cd_workflow.html#restrict-project-and-group-access-by-using-impersonation).
stage: configure
self-managed: true
gitlab-com: true
available_in: [Free, Premium, Ultimate]
documentation_link: https://docs.gitlab.com/ee/user/clusters/agent/gitops.html
image_url: https://img.youtube.com/vi/jgVxOnMfOZA/hqdefault.jpg
published_at: 2022-08-22
release: 15.3
- name: "Submit merge request review with summary comment"
description: |
When you finish reviewing a merge request, there are probably some common things that you do, like summarizing your review for others or approving the changes if they look good to you. Those common tasks are now quicker and easier: when you submit your review, you can add a summary comment along with any [quick actions](https://docs.gitlab.com/ee/user/project/quick_actions.html) like `/approve`.
stage: create
self-managed: true
gitlab-com: true
available_in: [Free, Premium, Ultimate]
documentation_link: https://docs.gitlab.com/ee/user/project/merge_requests/reviews/#submit-a-review
image_url: https://about.gitlab.com/images/15_3/create-mr-review-summary.png
published_at: 2022-08-22
release: 15.3
- name: "Define password complexity requirements"
description: |
GitLab administrators can now define password complexity requirements in addition to minimum password length. For new passwords, you can now require:
- Numbers.
- Uppercase letters.
- Lowercase letters.
- Symbols.
Complex passwords are less likely to be compromised, and the ability to configure password complexity requirements helps administrators enforce their password policies.
stage: manage
self-managed: true
gitlab-com: false
available_in: [Premium, Ultimate]
documentation_link: https://docs.gitlab.com/ee/user/admin_area/settings/sign_up_restrictions.html#password-complexity-requirements
image_url: https://about.gitlab.com/images/15_3/manage-password-complexity-policy.png
published_at: 2022-08-22
release: 15.3
- name: "Maintain SAML Group Links with API"
description: |
Until now, SAML group links had to be configured in the UI. Now, you can manage SAML group links programmatically using the API so you can automate SAML groups management.
stage: manage
self-managed: true
gitlab-com: true
available_in: [Premium, Ultimate]
documentation_link: https://docs.gitlab.com/ee/api/groups.html#saml-group-links
image_url: https://img.youtube.com/vi/Pft61UFM5LM/hqdefault.jpg
published_at: 2022-08-22
release: 15.3

View File

@ -1444,7 +1444,7 @@ response attributes:
| Attribute | Type | Description |
|:-------------------|:-------|:-------------------------------------------------------------------------------------|
| `[].name` | string | Name of the SAML group |
| `[].access_level` | string | Minimum [access level](members.md#valid-access-levels) for members of the SAML group |
| `[].access_level` | integer | Minimum [access level](members.md#valid-access-levels) for members of the SAML group |
Example request:
@ -1458,11 +1458,11 @@ Example response:
[
{
"name": "saml-group-1",
"access_level": "Guest"
"access_level": 10
},
{
"name": "saml-group-2",
"access_level": "Maintainer"
"access_level": 40
}
]
```
@ -1488,7 +1488,7 @@ response attributes:
| Attribute | Type | Description |
|:---------------|:-------|:-------------------------------------------------------------------------------------|
| `name` | string | Name of the SAML group |
| `access_level` | string | Minimum [access level](members.md#valid-access-levels) for members of the SAML group |
| `access_level` | integer | Minimum [access level](members.md#valid-access-levels) for members of the SAML group |
Example request:
@ -1501,7 +1501,7 @@ Example response:
```json
{
"name": "saml-group-1",
"access_level": "Guest"
"access_level": 10
}
```
@ -1519,7 +1519,7 @@ Supported attributes:
|:-------------------|:---------------|:---------|:-------------------------------------------------------------------------------------|
| `id` | integer/string | yes | ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) |
| `saml_group_name` | string | yes | Name of a SAML group |
| `access_level` | string | yes | Minimum [access level](members.md#valid-access-levels) for members of the SAML group |
| `access_level` | integer | yes | Minimum [access level](members.md#valid-access-levels) for members of the SAML group |
If successful, returns [`201`](index.md#status-codes) and the following
response attributes:
@ -1527,7 +1527,7 @@ response attributes:
| Attribute | Type | Description |
|:---------------|:-------|:-------------------------------------------------------------------------------------|
| `name` | string | Name of the SAML group |
| `access_level` | string | Minimum [access level](members.md#valid-access-levels) for members of the SAML group |
| `access_level` | integer | Minimum [access level](members.md#valid-access-levels) for members of the SAML group |
Example request:
@ -1540,7 +1540,7 @@ Example response:
```json
{
"name": "saml-group-1",
"access_level": "Guest"
"access_level": 10
}
```

View File

@ -274,6 +274,7 @@ listed in the descriptions of the relevant settings.
| `container_registry_token_expire_delay` | integer | no | Container Registry token duration in minutes. |
| `package_registry_cleanup_policies_worker_capacity` | integer | no | Number of workers assigned to the packages cleanup policies. |
| `deactivate_dormant_users` | boolean | no | Enable [automatic deactivation of dormant users](../user/admin_area/moderate_users.md#automatically-deactivate-dormant-users). |
| `deactivate_dormant_users_period` | integer | no | Length of time (in days) after which a user is considered dormant. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/336747) in GitLab 15.3. |
| `default_artifacts_expire_in` | string | no | Set the default expiration time for each job's artifacts. |
| `default_branch_name` | string | no | [Instance-level custom initial branch name](../user/project/repository/branches/default.md#instance-level-custom-initial-branch-name) ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/225258) in GitLab 13.2). |
| `default_branch_protection` | integer | no | Determine if developers can push to the default branch. Can take: `0` _(not protected, both users with the Developer role or Maintainer role can push new commits and force push)_, `1` _(partially protected, users with the Developer role or Maintainer role can push new commits, but cannot force push)_ or `2` _(fully protected, users with the Developer or Maintainer role cannot push new commits, but users with the Developer or Maintainer role can; no one can force push)_ as a parameter. Default is `2`. |

View File

@ -200,6 +200,10 @@ The following table lists project permissions available for each role:
| [Security dashboard](application_security/security_dashboard/index.md):<br>Use security dashboard | | | ✓ | ✓ | ✓ |
| [Security dashboard](application_security/security_dashboard/index.md):<br>View vulnerability | | | ✓ | ✓ | ✓ |
| [Security dashboard](application_security/security_dashboard/index.md):<br>View vulnerability findings in [dependency list](application_security/dependency_list/index.md) | | | ✓ | ✓ | ✓ |
| [Tasks](tasks.md):<br>Create (*18*) | ✓ | ✓ | ✓ | ✓ | ✓ |
| [Tasks](tasks.md):<br>Edit | | ✓ | ✓ | ✓ | ✓ |
| [Tasks](tasks.md):<br>Remove from issue | | ✓ | ✓ | ✓ | ✓ |
| [Tasks](tasks.md):<br>Delete (*22*) | | | | | ✓ |
| [Terraform](infrastructure/index.md):<br>Read Terraform state | | | ✓ | ✓ | ✓ |
| [Terraform](infrastructure/index.md):<br>Manage Terraform state | | | | ✓ | ✓ |
| [Test cases](../ci/test_cases/index.md):<br>Archive | | ✓ | ✓ | ✓ | ✓ |
@ -235,10 +239,11 @@ The following table lists project permissions available for each role:
16. In GitLab 14.5 or later, Guests are not allowed to [create incidents](../operations/incident_management/incidents.md#incident-creation).
In GitLab 15.1 and later, a Guest who created an issue that was promoted to an incident cannot edit, close, or reopen their incident.
17. In projects that accept contributions from external members, users can create, edit, and close their own merge requests.
18. Authors and assignees of issues can modify the title and description even if they don't have the Reporter role.
18. Authors and assignees can modify the title and description even if they don't have the Reporter role.
19. Authors and assignees can close and reopen issues even if they don't have the Reporter role.
20. The ability to view the Container Registry and pull images is controlled by the [Container Registry's visibility permissions](packages/container_registry/index.md#container-registry-visibility-permissions).
21. Maintainers cannot create, demote, or remove Owners, and they cannot promote users to the Owner role. They also cannot approve Owner role access requests.
22. Authors of tasks can delete them even if they don't have the Owner role, but they have to have at least the Guest role for the project.
<!-- markdownlint-enable MD029 -->

View File

@ -38,25 +38,64 @@ to work items and adding custom work item types, visit
[epic 6033](https://gitlab.com/groups/gitlab-org/-/epics/6033) or
[Plan direction page](https://about.gitlab.com/direction/plan/).
## View tasks
View tasks in issues, in the **Child items** section.
You can also [filter the list of issues](project/issues/managing_issues.md#filter-the-list-of-issues)
for `Type = task`.
## Create a task
Prerequisites:
- You must have at least the Guest role for the project, or the project must be public.
To create a task:
1. In an issue description, create a [task list](markdown.md#task-lists).
1. Hover over a task item and select **Create task** (**{doc-new}**).
1. In an issue description, in the **Child items** section, select **Add a task**.
1. Enter the task title.
1. Select **Create task**.
## Edit a task
Prerequisites:
- You must have at least the Reporter role for the project.
To edit a task:
1. In the issue description, view the task links.
1. Select a link. The task is displayed.
- To edit the description, select **Edit**, then select **Save**.
- To edit the title or state, make your changes, then select any area outside the field. The changes are saved automatically.
1. In the issue description, in the **Child items** section, select the task you want to edit.
The task window opens.
1. Optional. To edit the title, select it and make your changes.
1. Optional. To edit the description, select the edit icon (**{pencil}**), make your changes, and
select **Save**.
1. Select the close icon (**{close}**).
## Remove a task from an issue
Prerequisites:
- You must have at least the Reporter role for the project.
You can remove a task from an issue. The task is not deleted, but the two are no longer connected.
It's not possible to connect them again.
To remove a task from an issue:
1. In the issue description, in the **Child items** section, next to the task you want to remove, select the options menu (**{ellipsis_v}**).
1. Select **Remove task**.
## Delete a task
Prerequisites:
- You must either:
- Be the author of the task and have at least the Guest role for the project.
- Have the Owner role for the project.
To delete a task:
1. In the issue description, select the task.
1. From the options menu (**{ellipsis_v}**), select **Delete task**.
1. In the issue description, in the **Child items** section, select the task you want to edit.
1. In the task window, in the options menu (**{ellipsis_v}**), select **Delete task**.
1. Select **OK**.

View File

@ -39570,9 +39570,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
msgid "There are unsubmitted review comments."
msgstr ""
msgid "There is already a repository with that name on disk"
msgstr ""

View File

@ -101,7 +101,7 @@ RSpec.describe 'Merge request > Batch comments', :js do
write_diff_comment
visit_overview_with_pending_comment
visit_overview
end
it 'can add comment to review' do
@ -232,14 +232,6 @@ RSpec.describe 'Merge request > Batch comments', :js do
wait_for_requests
end
def visit_overview_with_pending_comment
accept_alert do
visit project_merge_request_path(merge_request.project, merge_request)
end
wait_for_requests
end
def write_diff_comment(**params)
click_diff_line(find_by_scrolling("[id='#{sample_compare.changes[0][:line_code]}']"))

View File

@ -209,7 +209,9 @@ RSpec.describe 'Projects > Settings > Repository settings' do
end
it 'generates an SSH public key on submission', :js do
fill_in 'url', with: 'ssh://user@localhost/project.git'
fill_in 'url', with: ssh_url
expect(page).to have_css(".js-mirror-url-hidden[value=\"#{ssh_url}\"]", visible: false)
select 'SSH public key', from: 'Authentication method'
select_direction

View File

@ -6,8 +6,6 @@ import createStore from '../create_batch_comments_store';
describe('Batch comments review bar component', () => {
let store;
let wrapper;
let addEventListenerSpy;
let removeEventListenerSpy;
const createComponent = (propsData = {}) => {
store = createStore();
@ -20,58 +18,25 @@ describe('Batch comments review bar component', () => {
beforeEach(() => {
document.body.className = '';
addEventListenerSpy = jest.spyOn(window, 'addEventListener');
removeEventListenerSpy = jest.spyOn(window, 'removeEventListener');
});
afterEach(() => {
addEventListenerSpy.mockRestore();
removeEventListenerSpy.mockRestore();
wrapper.destroy();
});
describe('when mounted', () => {
it('it adds review-bar-visible class to body', async () => {
expect(document.body.classList.contains(REVIEW_BAR_VISIBLE_CLASS_NAME)).toBe(false);
it('it adds review-bar-visible class to body when review bar is mounted', async () => {
expect(document.body.classList.contains(REVIEW_BAR_VISIBLE_CLASS_NAME)).toBe(false);
createComponent();
createComponent();
expect(document.body.classList.contains(REVIEW_BAR_VISIBLE_CLASS_NAME)).toBe(true);
});
it('it adds a blocking handler to the `beforeunload` window event', () => {
expect(addEventListenerSpy).not.toBeCalled();
createComponent();
expect(addEventListenerSpy).toHaveBeenCalledTimes(1);
expect(addEventListenerSpy).toBeCalledWith('beforeunload', expect.any(Function), {
capture: true,
});
});
expect(document.body.classList.contains(REVIEW_BAR_VISIBLE_CLASS_NAME)).toBe(true);
});
describe('before destroyed', () => {
it('it removes review-bar-visible class to body', async () => {
createComponent();
it('it removes review-bar-visible class to body when review bar is destroyed', async () => {
createComponent();
wrapper.destroy();
wrapper.destroy();
expect(document.body.classList.contains(REVIEW_BAR_VISIBLE_CLASS_NAME)).toBe(false);
});
it('it removes the blocking handler from the `beforeunload` window event', () => {
createComponent();
expect(removeEventListenerSpy).not.toBeCalled();
wrapper.destroy();
expect(removeEventListenerSpy).toHaveBeenCalledTimes(1);
expect(removeEventListenerSpy).toBeCalledWith('beforeunload', expect.any(Function), {
capture: true,
});
});
expect(document.body.classList.contains(REVIEW_BAR_VISIBLE_CLASS_NAME)).toBe(false);
});
});

View File

@ -0,0 +1,47 @@
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import addBlameLink from '~/blob/blob_blame_link';
describe('Blob links', () => {
const mouseoverEvent = new MouseEvent('mouseover', {
view: window,
bubbles: true,
cancelable: true,
});
beforeEach(() => {
setHTMLFixture(`
<div id="blob-content-holder">
<div class="line-numbers" data-blame-path="/blamePath">
<a id="L5" href="#L5" data-line-number="5" class="file-line-num js-line-links">5</a>
</div>
<pre id="LC5">Line 5 content</pre>
</div>
`);
addBlameLink('#blob-content-holder', 'js-line-links');
document.querySelector('.file-line-num').dispatchEvent(mouseoverEvent);
});
afterEach(() => {
resetHTMLFixture();
});
it('adds wrapper elements with correct classes', () => {
const wrapper = document.querySelector('.line-links');
expect(wrapper).toBeTruthy();
expect(wrapper.classList).toContain('diff-line-num');
});
it('adds blame link with correct classes and path', () => {
const blameLink = document.querySelector('.file-line-blame');
expect(blameLink).toBeTruthy();
expect(blameLink.getAttribute('href')).toBe('/blamePath#L5');
});
it('adds line link within wraper with correct classes and path', () => {
const lineLink = document.querySelector('.file-line-num');
expect(lineLink).toBeTruthy();
expect(lineLink.getAttribute('href')).toBe('#L5');
});
});

View File

@ -15,7 +15,7 @@ describe('Blob links Tracking', () => {
beforeEach(() => {
setHTMLFixture(`
<div id="blob-content-holder">
<div class="file-holder">
<div class="line-links diff-line-num">
<a href="#L5" class="file-line-blame"></a>
<a id="L5" href="#L5" data-line-number="5" class="file-line-num">5</a>
@ -23,7 +23,7 @@ describe('Blob links Tracking', () => {
<pre id="LC5">Line 5 content</pre>
</div>
`);
addBlobLinksTracking('#blob-content-holder', eventsToTrack);
addBlobLinksTracking();
jest.spyOn(Tracking, 'event');
});

View File

@ -5,6 +5,25 @@ require 'spec_helper'
RSpec.describe EventsHelper do
include Gitlab::Routing
describe '#event_target_path' do
subject { helper.event_target_path(event.present) }
context 'when target is a work item' do
let(:work_item) { create(:work_item) }
let(:event) { create(:event, target: work_item, target_type: 'WorkItem') }
it { is_expected.to eq(Gitlab::UrlBuilder.build(work_item, only_path: true)) }
end
context 'when target is not a work item' do
let(:project) { create(:project) }
let(:issue) { create(:issue, project: project) }
let(:event) { create(:event, target: issue, project: project) }
it { is_expected.to eq([project, issue]) }
end
end
describe '#event_commit_title' do
let(:message) { 'foo & bar ' + 'A' * 70 + '\n' + 'B' * 80 }

View File

@ -0,0 +1,39 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'events/event/_common.html.haml' do
let_it_be(:project) { create(:project) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:user) { create(:user) }
context 'when it is a work item event' do
let(:work_item) { create(:work_item, project: project) }
let(:event) do
create(:event, :created, project: project, target: work_item, target_type: 'WorkItem', author: user)
end
it 'renders the correct url' do
render partial: 'events/event/common', locals: { event: event.present }
expect(rendered).to have_link(
work_item.reference_link_text, href: "/#{project.full_path}/-/work_items/#{work_item.id}"
)
end
end
context 'when it is an isssue event' do
let(:issue) { create(:issue, project: project) }
let(:event) do
create(:event, :created, project: project, target: issue, author: user)
end
it 'renders the correct url' do
render partial: 'events/event/common', locals: { event: event.present }
expect(rendered).to have_link(issue.reference_link_text, href: "/#{project.full_path}/-/issues/#{issue.iid}")
end
end
end