Commit c4e5e2f1 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 20308a92 b06f1cdd
<script>
/* eslint-disable @gitlab/vue-require-i18n-strings */
import { GlTabs, GlTab, GlBadge } from '@gitlab/ui';
import ModalStore from '../../stores/modal_store';
import modalMixin from '../../mixins/modal_mixins';
export default {
components: {
GlTabs,
GlTab,
GlBadge,
},
mixins: [modalMixin],
data() {
return ModalStore.store;
......@@ -19,18 +25,18 @@ export default {
};
</script>
<template>
<div class="top-area gl-mt-3 gl-mb-3">
<ul class="nav-links issues-state-filters">
<li :class="{ active: activeTab == 'all' }">
<a href="#" role="button" @click.prevent="changeTab('all')">
Open issues <span class="badge badge-pill"> {{ issuesCount }} </span>
</a>
</li>
<li :class="{ active: activeTab == 'selected' }">
<a href="#" role="button" @click.prevent="changeTab('selected')">
Selected issues <span class="badge badge-pill"> {{ selectedCount }} </span>
</a>
</li>
</ul>
</div>
<gl-tabs class="gl-mt-3">
<gl-tab @click.prevent="changeTab('all')">
<template slot="title">
<span>Open issues</span>
<gl-badge size="sm" class="gl-tab-counter-badge">{{ issuesCount }}</gl-badge>
</template>
</gl-tab>
<gl-tab @click.prevent="changeTab('selected')">
<template slot="title">
<span>Selected issues</span>
<gl-badge size="sm" class="gl-tab-counter-badge">{{ selectedCount }}</gl-badge>
</template>
</gl-tab>
</gl-tabs>
</template>
<script>
import $ from 'jquery';
import { mapGetters, mapActions } from 'vuex';
import { GlIcon } from '@gitlab/ui';
import { GlDropdown, GlDropdownItem, GlDropdownDivider } from '@gitlab/ui';
import { getLocationHash, doesHashExistInUrl } from '../../lib/utils/url_utility';
import {
DISCUSSION_FILTERS_DEFAULT_VALUE,
......@@ -14,7 +13,9 @@ import notesEventHub from '../event_hub';
export default {
components: {
GlIcon,
GlDropdown,
GlDropdownItem,
GlDropdownDivider,
},
props: {
filters: {
......@@ -66,9 +67,6 @@ export default {
selectFilter(value, persistFilter = true) {
const filter = parseInt(value, 10);
// close dropdown
this.toggleDropdown();
if (filter === this.currentValue) return;
this.currentValue = filter;
this.filterDiscussion({
......@@ -78,9 +76,6 @@ export default {
});
this.toggleCommentsForm();
},
toggleDropdown() {
$(this.$refs.dropdownToggle).dropdown('toggle');
},
toggleCommentsForm() {
this.setCommentsDisabled(this.currentValue === HISTORY_ONLY_FILTER_VALUE);
},
......@@ -92,7 +87,6 @@ export default {
if (/^note_/.test(hash) && this.currentValue !== DISCUSSION_FILTERS_DEFAULT_VALUE) {
this.selectFilter(this.defaultValue, false);
this.toggleDropdown(); // close dropdown
this.setTargetNoteHash(hash);
}
},
......@@ -109,43 +103,24 @@ export default {
</script>
<template>
<div
<gl-dropdown
v-if="displayFilters"
class="discussion-filter-container js-discussion-filter-container d-inline-block align-bottom full-width-mobile"
id="discussion-filter-dropdown"
class="gl-mr-3 full-width-mobile discussion-filter-container js-discussion-filter-container qa-discussion-filter"
:text="currentFilter.title"
>
<button
id="discussion-filter-dropdown"
ref="dropdownToggle"
class="btn btn-sm qa-discussion-filter"
data-toggle="dropdown"
aria-expanded="false"
>
{{ currentFilter.title }} <gl-icon name="chevron-down" />
</button>
<div
ref="dropdownMenu"
class="dropdown-menu dropdown-menu-selectable dropdown-menu-right"
aria-labelledby="discussion-filter-dropdown"
>
<div class="dropdown-content">
<ul>
<li
v-for="filter in filters"
:key="filter.value"
:data-filter-type="filterType(filter.value)"
>
<button
:class="{ 'is-active': filter.value === currentValue }"
class="qa-filter-options"
type="button"
@click="selectFilter(filter.value)"
>
{{ filter.title }}
</button>
<div v-if="filter.value === defaultValue" class="dropdown-divider"></div>
</li>
</ul>
</div>
<div v-for="filter in filters" :key="filter.value" class="dropdown-item-wrapper">
<gl-dropdown-item
:is-check-item="true"
:is-checked="filter.value === currentValue"
:class="{ 'is-active': filter.value === currentValue }"
:data-filter-type="filterType(filter.value)"
class="qa-filter-options"
@click.prevent="selectFilter(filter.value)"
>
{{ filter.title }}
</gl-dropdown-item>
<gl-dropdown-divider v-if="filter.value === defaultValue" />
</div>
</div>
</gl-dropdown>
</template>
gs
<script>
import { GlIcon } from '@gitlab/ui';
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { mapActions, mapGetters } from 'vuex';
import { __ } from '~/locale';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
......@@ -15,7 +14,8 @@ const SORT_OPTIONS = [
export default {
SORT_OPTIONS,
components: {
GlIcon,
GlDropdown,
GlDropdownItem,
LocalStorageSync,
},
mixins: [Tracking.mixin()],
......@@ -49,33 +49,27 @@ export default {
</script>
<template>
<div
data-testid="sort-discussion-filter"
class="gl-mr-2 gl-display-inline-block gl-vertical-align-bottom full-width-mobile"
>
<div class="gl-mr-3 gl-display-inline-block gl-vertical-align-bottom full-width-mobile">
<local-storage-sync
:value="sortDirection"
:storage-key="storageKey"
@input="setDiscussionSortDirection"
/>
<button class="btn btn-sm js-dropdown-text" data-toggle="dropdown" aria-expanded="false">
{{ dropdownText }}
<gl-icon name="chevron-down" />
</button>
<div ref="dropdownMenu" class="dropdown-menu dropdown-menu-selectable dropdown-menu-right">
<div class="dropdown-content">
<ul>
<li v-for="{ text, key, cls } in $options.SORT_OPTIONS" :key="key">
<button
:class="[cls, { 'is-active': isDropdownItemActive(key) }]"
type="button"
@click="fetchSortedDiscussions(key)"
>
{{ text }}
</button>
</li>
</ul>
</div>
</div>
<gl-dropdown
:text="dropdownText"
data-testid="sort-discussion-filter"
class="js-dropdown-text full-width-mobile"
>
<gl-dropdown-item
v-for="{ text, key, cls } in $options.SORT_OPTIONS"
:key="key"
:class="cls"
:is-check-item="true"
:is-checked="isDropdownItemActive(key)"
@click="fetchSortedDiscussions(key)"
>
{{ text }}
</gl-dropdown-item>
</gl-dropdown>
</div>
</template>
<script>
import $ from 'jquery';
import { GlDeprecatedButton, GlTooltipDirective, GlIcon } from '@gitlab/ui';
import { GlButton, GlTooltipDirective } from '@gitlab/ui';
import Clipboard from 'clipboard';
import { __ } from '~/locale';
export default {
components: {
GlDeprecatedButton,
GlIcon,
GlButton,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
text: {
type: String,
......@@ -55,15 +52,12 @@ export default {
default: null,
},
},
copySuccessText: __('Copied'),
computed: {
modalDomId() {
return this.modalId ? `#${this.modalId}` : '';
},
},
mounted() {
this.$nextTick(() => {
this.clipboard = new Clipboard(this.$el, {
......@@ -83,13 +77,11 @@ export default {
.on('error', e => this.$emit('error', e));
});
},
destroyed() {
if (this.clipboard) {
this.clipboard.destroy();
}
},
methods: {
updateTooltip(target) {
const $target = $(target);
......@@ -112,15 +104,12 @@ export default {
};
</script>
<template>
<gl-deprecated-button
<gl-button
v-gl-tooltip="{ placement: tooltipPlacement, container: tooltipContainer }"
:class="cssClasses"
:data-clipboard-target="target"
:data-clipboard-text="text"
:title="title"
>
<slot>
<gl-icon name="copy-to-clipboard" />
</slot>
</gl-deprecated-button>
icon="copy-to-clipboard"
/>
</template>
# frozen_string_literal: true
# Verifies features availability based on issue type.
# This can be used, for example, for hiding UI elements or blocking specific
# quick actions for particular issue types;
module IssueAvailableFeatures
extend ActiveSupport::Concern
# EE only features are listed on EE::IssueAvailableFeatures
def available_features_for_issue_types
{}.with_indifferent_access
end
def issue_type_supports?(feature)
unless available_features_for_issue_types.has_key?(feature)
raise ArgumentError, 'invalid feature'
end
available_features_for_issue_types[feature].include?(issue_type)
end
end
IssueAvailableFeatures.prepend_if_ee('EE::IssueAvailableFeatures')
......@@ -20,6 +20,7 @@ class Issue < ApplicationRecord
include StateEventable
include IdInOrdered
include Presentable
include IssueAvailableFeatures
DueDateStruct = Struct.new(:title, :name).freeze
NoDueDate = DueDateStruct.new('No Due Date', '0').freeze
......
---
title: Update issue boards modal to gl-tabs
merge_request: 43740
author:
type: changed
---
title: Update button in modal_copy_button.vue to use GlButton from GitLab UI
merge_request: 43714
author:
type: other
......@@ -309,21 +309,22 @@ The following table gives an overview of how the API functions generally behave.
The following table shows the possible return codes for API requests.
| Return values | Description |
| ------------- | ----------- |
| `200 OK` | The `GET`, `PUT` or `DELETE` request was successful, the resource(s) itself is returned as JSON. |
| `204 No Content` | The server has successfully fulfilled the request and that there is no additional content to send in the response payload body. |
| `201 Created` | The `POST` request was successful and the resource is returned as JSON. |
| `304 Not Modified` | Indicates that the resource has not been modified since the last request. |
| `400 Bad Request` | A required attribute of the API request is missing, e.g., the title of an issue is not given. |
| `401 Unauthorized` | The user is not authenticated, a valid [user token](#authentication) is necessary. |
| `403 Forbidden` | The request is not allowed, e.g., the user is not allowed to delete a project. |
| `404 Not Found` | A resource could not be accessed, e.g., an ID for a resource could not be found. |
| Return values | Description |
| ------------------------ | ----------- |
| `200 OK` | The `GET`, `PUT` or `DELETE` request was successful, the resource(s) itself is returned as JSON. |
| `204 No Content` | The server has successfully fulfilled the request and that there is no additional content to send in the response payload body. |
| `201 Created` | The `POST` request was successful and the resource is returned as JSON. |
| `304 Not Modified` | Indicates that the resource has not been modified since the last request. |
| `400 Bad Request` | A required attribute of the API request is missing, e.g., the title of an issue is not given. |
| `401 Unauthorized` | The user is not authenticated, a valid [user token](#authentication) is necessary. |
| `403 Forbidden` | The request is not allowed, e.g., the user is not allowed to delete a project. |
| `404 Not Found` | A resource could not be accessed, e.g., an ID for a resource could not be found. |
| `405 Method Not Allowed` | The request is not supported. |
| `409 Conflict` | A conflicting resource already exists, e.g., creating a project with a name that already exists. |
| `412` | Indicates the request was denied. May happen if the `If-Unmodified-Since` header is provided when trying to delete a resource, which was modified in between. |
| `422 Unprocessable` | The entity could not be processed. |
| `500 Server Error` | While handling the request something went wrong server-side. |
| `409 Conflict` | A conflicting resource already exists, e.g., creating a project with a name that already exists. |
| `412` | Indicates the request was denied. May happen if the `If-Unmodified-Since` header is provided when trying to delete a resource, which was modified in between. |
| `422 Unprocessable` | The entity could not be processed. |
| `429 Too Many Requests` | The user exceeded the [application rate limits](../administration/instance_limits.md#rate-limits). |
| `500 Server Error` | While handling the request, something went wrong server-side. |
## Pagination
......
......@@ -434,5 +434,8 @@ npm dist-tag rm @scope/package@version my-tag # Delete a tag from the package
npm install @scope/package@my-tag # Install a specific tag
```
NOTE: **Note:**
You cannot use your `CI_JOB_TOKEN` or deploy token with the `npm dist-tag` commands. View [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/258835) for details.
CAUTION: **Warning:**
Due to a bug in NPM 6.9.0, deleting dist tags fails. Make sure your NPM version is greater than 6.9.1.
......@@ -79,6 +79,9 @@ This will enable the `Bug` dropdown option when creating or editing issues. When
to the issue description field. The 'Reset template' button will discard any
changes you made after picking the template and return it to its initial status.
TIP: **Tip:**
You can create short-cut links to create an issue using a designated template. For example: `https://gitlab.com/gitlab-org/gitlab/-/issues/new?issuable_template=Feature%20proposal`.
![Description templates](img/description_templates.png)
## Setting a default template for merge requests and issues **(STARTER)**
......
......@@ -8,8 +8,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/5554) in [GitLab 8.11](https://about.gitlab.com/releases/2016/08/22/gitlab-8-11-released/#issue-board).
## Overview
The GitLab Issue Board is a software project management tool used to plan,
organize, and visualize a workflow for a feature or product release.
It can be used as a [Kanban](https://en.wikipedia.org/wiki/Kanban_(development)) or a
......
......@@ -8,8 +8,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/1904) in [GitLab Starter 9.2](https://about.gitlab.com/releases/2017/05/22/gitlab-9-2-released/#multiple-assignees-for-issues).
## Overview
In large teams, where there is shared ownership of an issue, it can be difficult
to track who is working on it, who already completed their contributions, who
didn't even start yet.
......
......@@ -6,8 +6,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Labels
## Overview
As your count of issues, merge requests, and epics grows in GitLab, it's more and more challenging
to keep track of those items. Especially as your organization grows from just a few people to
hundreds or thousands. This is where labels come in. They help you organize and tag your work
......
......@@ -14,8 +14,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> value, so the burndown chart considers them as closed on the milestone
> `start_date`. In that case, a warning will be displayed.
## Overview
Burndown Charts are visual representations of the progress of completing a milestone.
![burndown chart](img/burndown_chart.png)
......
......@@ -7,8 +7,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Milestones
## Overview
Milestones in GitLab are a way to track issues and merge requests created to achieve a broader goal in a certain period of time.
Milestones allow you to organize issues and merge requests into a cohesive group, with an optional start date and an optional due date.
......
......@@ -10,8 +10,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/214839) to [GitLab Starter](https://about.gitlab.com/pricing/) in 13.0.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/215364) to [GitLab Core](https://about.gitlab.com/pricing/) in 13.2.
## Overview
Service Desk is a module that allows your team to connect directly
with any external party through email right inside of GitLab; no external tools required.
An ongoing conversation right where your software is built ensures that user feedback ends
......
......@@ -6,7 +6,7 @@ module EE
extend ::Gitlab::Utils::Override
def supports_epic?
is_a?(Issue) && !incident? && project.group.present?
is_a?(Issue) && issue_type_supports?(:epics) && project.group.present?
end
def supports_health_status?
......
# frozen_string_literal: true
module EE
module IssueAvailableFeatures
include ::Gitlab::Utils::StrongMemoize
extend ::Gitlab::Utils::Override
override :available_features_for_issue_types
def available_features_for_issue_types
strong_memoize(:available_features_for_issue_types) do
super.merge(epics: %w(issue))
end
end
end
end
......@@ -133,7 +133,7 @@ RSpec.describe 'Epic show', :js do
it 'shows epic thread filter dropdown' do
page.within('.js-noteable-awards') do
expect(find('.js-discussion-filter-container #discussion-filter-dropdown')).to have_content('Show all activity')
expect(find('#discussion-filter-dropdown')).to have_content('Show all activity')
end
end
......@@ -142,9 +142,7 @@ RSpec.describe 'Epic show', :js do
context 'when sorted by `Oldest first`' do
it 'shows comments in the correct order' do
page.within('[data-testid="sort-discussion-filter"]') do
expect(find('.js-dropdown-text')).to have_content('Oldest first')
end
expect(find('.js-dropdown-text')).to have_content('Oldest first')
items = all('.timeline-entry .timeline-discussion-body .note-text')
expect(items[0]).to have_content(notes[0].note)
......@@ -155,7 +153,7 @@ RSpec.describe 'Epic show', :js do
context 'when sorted by `Newest first`' do
before do
page.within('[data-testid="sort-discussion-filter"]') do
find('button').click
find('.js-dropdown-text').click
find('.js-newest-first').click
wait_for_requests
end
......@@ -163,7 +161,7 @@ RSpec.describe 'Epic show', :js do
it 'shows comments in the correct order', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/225637' do
page.within('[data-testid="sort-discussion-filter"]') do
expect(find('.js-dropdown-text')).to have_content('Newest first')
expect(find('.js-newest-first')).to have_content('Newest first')
end
items = all('.timeline-entry .timeline-discussion-body .note-text')
......
......@@ -758,4 +758,16 @@ RSpec.describe Issue do
it { is_expected.to eq(supports_iterations) }
end
end
describe '#issue_type_supports?' do
let_it_be(:issue) { create(:issue) }
let_it_be(:test_case) { create(:quality_test_case) }
let_it_be(:incident) { create(:incident) }
it do
expect(issue.issue_type_supports?(:epics)).to be(true)
expect(test_case.issue_type_supports?(:epics)).to be(false)
expect(incident.issue_type_supports?(:epics)).to be(false)
end
end
end
......@@ -121,6 +121,16 @@ RSpec.describe Notes::QuickActionsService do
end
end
context 'on a test case' do
before do
issue.update!(issue_type: :test_case)
end
it 'leaves the note empty' do
expect(execute(note)).to be_empty
end
end
context 'on a merge request' do
let(:note_mr) { create(:note_on_merge_request, project: project, note: note_text) }
......
......@@ -168,12 +168,28 @@ RSpec.describe Epics::IssuePromoteService do
end
end
context 'on an incident' do
it 'raises error' do
issue.update!(issue_type: :incident)
context 'on other issue types' do
shared_examples_for 'raising error' do
before do
issue.update(issue_type: issue_type)
end
expect { subject.execute(issue) }
.to raise_error(Epics::IssuePromoteService::PromoteError, /is not supported/)
it 'raises error' do
expect { subject.execute(issue) }
.to raise_error(Epics::IssuePromoteService::PromoteError, /is not supported/)
end
end
context 'on an incident' do
let(:issue_type) { :incident }
it_behaves_like 'raising error'
end
context 'on a test case' do
let(:issue_type) { :test_case }
it_behaves_like 'raising error'
end
end
end
......
......@@ -76,6 +76,15 @@ module QA
parse_body(response)[:title].include?(title)
end
end
private
def api_get
with_paginated_response_body(Runtime::API::Request.new(api_client, '/user/keys', per_page: '100').url) do |page|
key = page.find { |key| key[:title] == title }
break process_api_response(key) if key
end
end
end
end
end
......@@ -36,6 +36,10 @@ module QA
Flow::Login.sign_in
end
after do
ssh_key.remove_via_api!
end
it 'clones, pushes, and pulls a snippet over HTTP, edits via UI', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/826' do
Resource::Repository::Push.fabricate! do |push|
push.repository_http_uri = repository_uri_http
......
......@@ -36,6 +36,10 @@ module QA
Flow::Login.sign_in
end
after do
ssh_key.remove_via_api!
end
it 'clones, pushes, and pulls a project snippet over HTTP, edits via UI', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/833' do
Resource::Repository::Push.fabricate! do |push|
push.repository_http_uri = repository_uri_http
......
......@@ -83,6 +83,10 @@ module QA
end
end
after do
key.remove_via_api!
end
it 'denies access', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/860' do
expect { push_a_project_with_ssh_key(key) }.to raise_error(QA::Git::Repository::RepositoryCommandError, /fatal: Could not read from remote repository/)
end
......@@ -126,6 +130,10 @@ module QA
end
end
after do
key.remove_via_api!
end
it 'allows access', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/863' do
expect { push_a_project_with_ssh_key(key) }.not_to raise_error
end
......
......@@ -45,13 +45,19 @@ module QA
end
context 'Add SSH key', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/738' do
key = nil
before do
sign_in
Resource::SSHKey.fabricate_via_browser_ui! do |resource|
key = Resource::SSHKey.fabricate_via_browser_ui! do |resource|
resource.title = "key for audit event test #{Time.now.to_f}"
end
end
after do
key&.reload!&.remove_via_api!
end
it_behaves_like 'audit event', ["Added SSH key"]
end
......
# frozen_string_literal: true
module QA
RSpec.describe 'Plan' do
RSpec.describe 'Plan', :reliable do
describe 'Epics Management' do
before do
Flow::Login.sign_in
end
it 'creates an epic', :reliable, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/522' do
it 'creates an epic', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/522' do
epic_title = 'Epic created via GUI'
EE::Resource::Epic.fabricate_via_browser_ui! do |epic|
epic.title = epic_title
......@@ -36,7 +36,7 @@ module QA
epic.visit!
end
it 'adds/removes issue to/from epic', :reliable, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/526' do
it 'adds/removes issue to/from epic', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/526' do
EE::Page::Group::Epic::Show.perform do |show|
show.add_issue_to_epic(issue.web_url)
......@@ -48,7 +48,7 @@ module QA
end
end
it 'comments on epic', :reliable, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/525' do
it 'comments on epic', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/525' do
comment = 'My Epic Comment'
EE::Page::Group::Epic::Show.perform do |show|
show.add_comment_to_epic(comment)
......
......@@ -5,12 +5,17 @@ module QA
describe 'GitLab SSH push' do
let(:file_name) { 'README.md' }
key = nil
after do
key&.remove_via_api!
end
context 'regular git commit' do
it "is replicated to the secondary", testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/686' do
key_title = "Geo SSH #{Time.now.to_f}"
file_content = 'This is a Geo project! Commit from primary.'
project = nil
key = nil
QA::Flow::Login.while_signed_in(address: :geo_primary) do
# Create a new SSH key for the user
......@@ -73,7 +78,6 @@ module QA
key_title = "Geo SSH LFS #{Time.now.to_f}"
file_content = 'The rendered file could not be displayed because it is stored in LFS.'
project = nil
key = nil
QA::Flow::Login.while_signed_in(address: :geo_primary) do
# Create a new SSH key for the user
......
......@@ -6,12 +6,17 @@ module QA
let(:file_content_primary) { 'This is a Geo project! Commit from primary.' }
let(:file_content_secondary) { 'This is a Geo project! Commit from secondary.' }
key = nil
after do
key&.remove_via_api!
end
context 'regular git commit' do
it 'is proxied to the primary and ultimately replicated to the secondary', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/698' do
file_name = 'README.md'
key_title = "Geo SSH to 2nd #{Time.now.to_f}"
project = nil
key = nil
QA::Flow::Login.while_signed_in(address: :geo_primary) do
# Create a new SSH key for the user
......@@ -95,7 +100,6 @@ module QA
file_name_primary = 'README.md'
file_name_secondary = 'README_MORE.md'
project = nil
key = nil
QA::Flow::Login.while_signed_in(address: :geo_primary) do
# Create a new SSH key for the user
......
......@@ -3,12 +3,17 @@
module QA
RSpec.describe 'Geo', :orchestrated, :geo do
describe 'GitLab wiki SSH push' do
key = nil
after do
key&.remove_via_api!
end
context 'wiki commit' do
it 'is replicated to the secondary', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/688' do
wiki_content = 'This tests replication of wikis via SSH'
push_content = 'This is from the Geo wiki push via SSH!'
project = nil
key = nil
QA::Flow::Login.while_signed_in(address: :geo_primary) do
# Create a new SSH key
......
......@@ -34,6 +34,10 @@ module QA
end
end
after do
key.remove_via_api!
end
it 'proxies wiki commit to primary node and ultmately replicates to secondary node', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/694' do
QA::Runtime::Logger.debug('*****Visiting the secondary geo node*****')
......
......@@ -79,7 +79,7 @@ RSpec.describe 'Issue Boards add issue modal', :js do
it 'loads issues' do
page.within('.add-issues-modal') do
page.within('.nav-links') do
page.within('.gl-tabs') do
expect(page).to have_content('2')
end
......@@ -146,7 +146,7 @@ RSpec.describe 'Issue Boards add issue modal', :js do
page.within('.add-issues-modal') do
first('.board-card .board-card-number').click
page.within('.nav-links') do
page.within('.gl-tabs') do
expect(page).to have_content('Selected issues 1')
end
end
......
......@@ -74,13 +74,15 @@ describe('DiscussionFilter component', () => {
});
it('renders the all filters', () => {
expect(wrapper.findAll('.dropdown-menu li').length).toBe(discussionFiltersMock.length);
expect(wrapper.findAll('.discussion-filter-container .dropdown-item').length).toBe(
discussionFiltersMock.length,
);
});
it('renders the default selected item', () => {
expect(
wrapper
.find('#discussion-filter-dropdown')
.find('#discussion-filter-dropdown .dropdown-item')
.text()
.trim(),
).toBe(discussionFiltersMock[0].title);
......@@ -88,7 +90,7 @@ describe('DiscussionFilter component', () => {
it('updates to the selected item', () => {
const filterItem = wrapper.find(
`.dropdown-menu li[data-filter-type="${DISCUSSION_FILTER_TYPES.HISTORY}"] button`,
`.discussion-filter-container .dropdown-item[data-filter-type="${DISCUSSION_FILTER_TYPES.HISTORY}"]`,
);
filterItem.trigger('click');
......@@ -98,7 +100,9 @@ describe('DiscussionFilter component', () => {
it('only updates when selected filter changes', () => {
wrapper
.find(`.dropdown-menu li[data-filter-type="${DISCUSSION_FILTER_TYPES.ALL}"] button`)
.find(
`.discussion-filter-container .dropdown-item[data-filter-type="${DISCUSSION_FILTER_TYPES.ALL}"]`,
)
.trigger('click');
expect(filterDiscussion).not.toHaveBeenCalled();
......@@ -106,7 +110,7 @@ describe('DiscussionFilter component', () => {
it('disables commenting when "Show history only" filter is applied', () => {
const filterItem = wrapper.find(
`.dropdown-menu li[data-filter-type="${DISCUSSION_FILTER_TYPES.HISTORY}"] button`,
`.discussion-filter-container .dropdown-item[data-filter-type="${DISCUSSION_FILTER_TYPES.HISTORY}"]`,
);
filterItem.trigger('click');
......@@ -115,7 +119,7 @@ describe('DiscussionFilter component', () => {
it('enables commenting when "Show history only" filter is not applied', () => {
const filterItem = wrapper.find(
`.dropdown-menu li[data-filter-type="${DISCUSSION_FILTER_TYPES.ALL}"] button`,
`.discussion-filter-container .dropdown-item[data-filter-type="${DISCUSSION_FILTER_TYPES.ALL}"]`,
);
filterItem.trigger('click');
......@@ -124,10 +128,10 @@ describe('DiscussionFilter component', () => {
it('renders a dropdown divider for the default filter', () => {
const defaultFilter = wrapper.findAll(
`.dropdown-menu li[data-filter-type="${DISCUSSION_FILTER_TYPES.ALL}"] > *`,
`.discussion-filter-container .dropdown-item-wrapper > *`,
);
expect(defaultFilter.at(defaultFilter.length - 1).classes('dropdown-divider')).toBe(true);
expect(defaultFilter.at(1).classes('gl-new-dropdown-divider')).toBe(true);
});
describe('Merge request tabs', () => {
......
......@@ -55,7 +55,7 @@ describe('Sort Discussion component', () => {
it('calls the right actions', () => {
createComponent();
wrapper.find('.js-newest-first').trigger('click');
wrapper.find('.js-newest-first').vm.$emit('click');
expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', DESC);
expect(Tracking.event).toHaveBeenCalledWith(undefined, 'change_discussion_sort_direction', {
......@@ -67,7 +67,7 @@ describe('Sort Discussion component', () => {
it('shows the "Oldest First" as the dropdown', () => {
createComponent();
expect(wrapper.find('.js-dropdown-text').text()).toBe('Oldest first');
expect(wrapper.find('.js-dropdown-text').props('text')).toBe('Oldest first');
});
});
......@@ -79,7 +79,7 @@ describe('Sort Discussion component', () => {
describe('when the dropdown item is clicked', () => {
it('calls the right actions', () => {
wrapper.find('.js-oldest-first').trigger('click');
wrapper.find('.js-oldest-first').vm.$emit('click');
expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', ASC);
expect(Tracking.event).toHaveBeenCalledWith(undefined, 'change_discussion_sort_direction', {
......@@ -87,13 +87,13 @@ describe('Sort Discussion component', () => {
});
});
it('applies the active class to the correct button in the dropdown', () => {
expect(wrapper.find('.js-newest-first').classes()).toContain('is-active');
it('sets is-checked to true on the active button in the dropdown', () => {
expect(wrapper.find('.js-newest-first').props('isChecked')).toBe(true);
});
});
it('shows the "Newest First" as the dropdown', () => {
expect(wrapper.find('.js-dropdown-text').text()).toBe('Newest first');
expect(wrapper.find('.js-dropdown-text').props('text')).toBe('Newest first');
});
});
});
......@@ -1238,4 +1238,12 @@ RSpec.describe Issue do
expect(issue.allows_reviewers?).to be(false)
end
end
describe '#issue_type_supports?' do
let_it_be(:issue) { create(:issue) }
it 'raises error when feature is invalid' do
expect { issue.issue_type_supports?(:unkown_feature) }.to raise_error(ArgumentError)
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment