Commit 80e51340 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 8d78e95a
...@@ -139,7 +139,7 @@ gem 'html-pipeline', '~> 2.8' ...@@ -139,7 +139,7 @@ gem 'html-pipeline', '~> 2.8'
gem 'deckar01-task_list', '2.2.1' gem 'deckar01-task_list', '2.2.1'
gem 'gitlab-markup', '~> 1.7.0' gem 'gitlab-markup', '~> 1.7.0'
gem 'github-markup', '~> 1.7.0', require: 'github/markup' gem 'github-markup', '~> 1.7.0', require: 'github/markup'
gem 'commonmarker', '~> 0.17' gem 'commonmarker', '~> 0.20'
gem 'RedCloth', '~> 4.3.2' gem 'RedCloth', '~> 4.3.2'
gem 'rdoc', '~> 6.0' gem 'rdoc', '~> 6.0'
gem 'org-ruby', '~> 0.9.12' gem 'org-ruby', '~> 0.9.12'
......
...@@ -157,7 +157,7 @@ GEM ...@@ -157,7 +157,7 @@ GEM
coercible (1.0.0) coercible (1.0.0)
descendants_tracker (~> 0.0.1) descendants_tracker (~> 0.0.1)
colored2 (3.1.2) colored2 (3.1.2)
commonmarker (0.17.13) commonmarker (0.20.1)
ruby-enum (~> 0.5) ruby-enum (~> 0.5)
concord (0.1.5) concord (0.1.5)
adamantium (~> 0.2.0) adamantium (~> 0.2.0)
...@@ -1146,7 +1146,7 @@ DEPENDENCIES ...@@ -1146,7 +1146,7 @@ DEPENDENCIES
carrierwave (~> 1.3) carrierwave (~> 1.3)
charlock_holmes (~> 0.7.5) charlock_holmes (~> 0.7.5)
chronic (~> 0.10.2) chronic (~> 0.10.2)
commonmarker (~> 0.17) commonmarker (~> 0.20)
concurrent-ruby (~> 1.1) concurrent-ruby (~> 1.1)
connection_pool (~> 2.0) connection_pool (~> 2.0)
countries (~> 3.0) countries (~> 3.0)
......
import $ from 'jquery'; import $ from 'jquery';
import Sortable from 'sortablejs'; import Sortable from 'sortablejs';
import Vue from 'vue'; import Vue from 'vue';
import { GlButtonGroup, GlButton, GlTooltip } from '@gitlab/ui';
import { n__, s__ } from '~/locale'; import { n__, s__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import Tooltip from '~/vue_shared/directives/tooltip'; import Tooltip from '~/vue_shared/directives/tooltip';
import isWipLimitsOn from 'ee_else_ce/boards/mixins/is_wip_limits';
import AccessorUtilities from '../../lib/utils/accessor'; import AccessorUtilities from '../../lib/utils/accessor';
import BoardBlankState from './board_blank_state.vue'; import BoardBlankState from './board_blank_state.vue';
import BoardDelete from './board_delete'; import BoardDelete from './board_delete';
import BoardList from './board_list.vue'; import BoardList from './board_list.vue';
import IssueCount from './issue_count.vue';
import boardsStore from '../stores/boards_store'; import boardsStore from '../stores/boards_store';
import { getBoardSortableDefaultOptions, sortableEnd } from '../mixins/sortable_default_options'; import { getBoardSortableDefaultOptions, sortableEnd } from '../mixins/sortable_default_options';
import { ListType } from '../constants';
export default Vue.extend({ export default Vue.extend({
components: { components: {
...@@ -17,10 +21,15 @@ export default Vue.extend({ ...@@ -17,10 +21,15 @@ export default Vue.extend({
BoardDelete, BoardDelete,
BoardList, BoardList,
Icon, Icon,
GlButtonGroup,
IssueCount,
GlButton,
GlTooltip,
}, },
directives: { directives: {
Tooltip, Tooltip,
}, },
mixins: [isWipLimitsOn],
props: { props: {
list: { list: {
type: Object, type: Object,
...@@ -53,6 +62,11 @@ export default Vue.extend({ ...@@ -53,6 +62,11 @@ export default Vue.extend({
isLoggedIn() { isLoggedIn() {
return Boolean(gon.current_user_id); return Boolean(gon.current_user_id);
}, },
showListHeaderButton() {
return (
!this.disabled && this.list.type !== ListType.closed && this.list.type !== ListType.blank
);
},
counterTooltip() { counterTooltip() {
const { issuesSize } = this.list; const { issuesSize } = this.list;
return `${n__('%d issue', '%d issues', issuesSize)}`; return `${n__('%d issue', '%d issues', issuesSize)}`;
...@@ -61,11 +75,19 @@ export default Vue.extend({ ...@@ -61,11 +75,19 @@ export default Vue.extend({
return this.list.isExpanded ? s__('Boards|Collapse') : s__('Boards|Expand'); return this.list.isExpanded ? s__('Boards|Collapse') : s__('Boards|Expand');
}, },
isNewIssueShown() { isNewIssueShown() {
return this.list.type === ListType.backlog || this.showListHeaderButton;
},
isSettingsShown() {
return ( return (
this.list.type === 'backlog' || this.list.type !== ListType.backlog &&
(!this.disabled && this.list.type !== 'closed' && this.list.type !== 'blank') this.showListHeaderButton &&
this.list.isExpanded &&
this.isWipLimitsOn
); );
}, },
showBoardListAndBoardInfo() {
return this.list.type !== ListType.blank && this.list.type !== ListType.promotion;
},
uniqueKey() { uniqueKey() {
// eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
return `boards.${this.boardId}.${this.list.type}.${this.list.id}`; return `boards.${this.boardId}.${this.list.type}.${this.list.id}`;
......
...@@ -71,6 +71,9 @@ export default { ...@@ -71,6 +71,9 @@ export default {
total: this.list.issuesSize, total: this.list.issuesSize,
}); });
}, },
issuesSizeExceedsMax() {
return this.list.maxIssueCount > 0 && this.list.issuesSize > this.list.maxIssueCount;
},
}, },
watch: { watch: {
filters: { filters: {
...@@ -435,7 +438,7 @@ export default { ...@@ -435,7 +438,7 @@ export default {
ref="list" ref="list"
:data-board="list.id" :data-board="list.id"
:data-board-type="list.type" :data-board-type="list.type"
:class="{ 'is-smaller': showIssueForm }" :class="{ 'is-smaller': showIssueForm, 'bg-danger-100': issuesSizeExceedsMax }"
class="board-list w-100 h-100 list-unstyled mb-0 p-1 js-board-list" class="board-list w-100 h-100 list-unstyled mb-0 p-1 js-board-list"
> >
<board-card <board-card
......
<script>
export default {
name: 'IssueCount',
props: {
maxIssueCount: {
type: Number,
required: false,
default: 0,
},
issuesSize: {
type: Number,
required: false,
default: 0,
},
},
computed: {
isMaxLimitSet() {
return this.maxIssueCount !== 0;
},
issuesExceedMax() {
return this.isMaxLimitSet && this.issuesSize > this.maxIssueCount;
},
},
};
</script>
<template>
<div class="issue-count">
<span class="js-issue-size" :class="{ 'text-danger': issuesExceedMax }">
{{ issuesSize }}
</span>
<span v-if="isMaxLimitSet" class="js-max-issue-size">
{{ maxIssueCount }}
</span>
</div>
</template>
...@@ -4,6 +4,8 @@ export const ListType = { ...@@ -4,6 +4,8 @@ export const ListType = {
backlog: 'backlog', backlog: 'backlog',
closed: 'closed', closed: 'closed',
label: 'label', label: 'label',
promotion: 'promotion',
blank: 'blank',
}; };
export default { export default {
......
export default {
computed: {
isWipLimitsOn() {
return false;
},
},
};
...@@ -52,6 +52,9 @@ class List { ...@@ -52,6 +52,9 @@ class List {
this.loadingMore = false; this.loadingMore = false;
this.issues = obj.issues || []; this.issues = obj.issues || [];
this.issuesSize = obj.issuesSize ? obj.issuesSize : 0; this.issuesSize = obj.issuesSize ? obj.issuesSize : 0;
this.maxIssueCount = Object.hasOwnProperty.call(obj, 'max_issue_count')
? obj.max_issue_count
: 0;
this.defaultAvatar = defaultAvatar; this.defaultAvatar = defaultAvatar;
if (obj.label) { if (obj.label) {
......
...@@ -72,7 +72,7 @@ export default { ...@@ -72,7 +72,7 @@ export default {
{{ __('Related merge requests') }} {{ __('Related merge requests') }}
</span> </span>
<div v-if="totalCount" class="d-inline-flex lh-100 align-middle"> <div v-if="totalCount" class="d-inline-flex lh-100 align-middle">
<div class="mr-count-badge"> <div class="mr-count-badge border-width-1px border-style-solid border-color-default">
<div class="mr-count-badge-count"> <div class="mr-count-badge-count">
<svg class="s16 mr-1 text-secondary"> <svg class="s16 mr-1 text-secondary">
<icon name="merge-request" class="mr-1 text-secondary" /> <icon name="merge-request" class="mr-1 text-secondary" />
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
color: $gl-text-color; color: $gl-text-color;
border: 1px solid $border-color; border: 1px solid $border-color;
border-radius: $border-radius-default; border-radius: $border-radius-default;
margin-bottom: $gl-padding; margin-bottom: $gl-padding-8;
.card.card-body-segment { .card.card-body-segment {
padding: $gl-padding; padding: $gl-padding;
......
...@@ -187,6 +187,10 @@ ...@@ -187,6 +187,10 @@
font-size: 1em; font-size: 1em;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
padding: $gl-padding-8 $gl-padding; padding: $gl-padding-8 $gl-padding;
.js-max-issue-size::before {
content: '/';
}
} }
.board-title-text { .board-title-text {
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
.mr-count-badge { .mr-count-badge {
display: inline-flex; display: inline-flex;
border-radius: $border-radius-base; border-radius: $border-radius-base;
border: 1px solid $border-color;
padding: 5px $gl-padding-8; padding: 5px $gl-padding-8;
} }
......
...@@ -748,7 +748,7 @@ ...@@ -748,7 +748,7 @@
display: inline-block; display: inline-block;
&:not(:last-child) { &:not(:last-child) {
margin-right: $gl-padding; margin-right: $gl-padding-8;
} }
&.right { &.right {
...@@ -798,7 +798,7 @@ ...@@ -798,7 +798,7 @@
} }
.btn { .btn {
margin-top: $gl-padding; margin-top: $gl-padding-8;
padding: $gl-btn-vert-padding $gl-btn-padding; padding: $gl-btn-vert-padding $gl-btn-padding;
line-height: $gl-btn-line-height; line-height: $gl-btn-line-height;
......
.tree-holder { .tree-holder {
.nav-block { .nav-block {
margin: 10px 0; margin: 16px 0;
.btn .fa, .btn .fa,
.btn svg { .btn svg {
...@@ -15,8 +15,13 @@ ...@@ -15,8 +15,13 @@
} }
.tree-controls { .tree-controls {
display: flex;
text-align: right; text-align: right;
.btn {
margin-left: 8px;
}
.btn-group { .btn-group {
margin-left: 10px; margin-left: 10px;
} }
......
...@@ -9,6 +9,7 @@ module BoardsActions ...@@ -9,6 +9,7 @@ module BoardsActions
before_action :boards, only: :index before_action :boards, only: :index
before_action :board, only: :show before_action :board, only: :show
before_action :push_wip_limits, only: :index
end end
def index def index
...@@ -24,6 +25,10 @@ module BoardsActions ...@@ -24,6 +25,10 @@ module BoardsActions
private private
# Noop on FOSS
def push_wip_limits
end
def boards def boards
strong_memoize(:boards) do strong_memoize(:boards) do
Boards::ListService.new(parent, current_user).execute Boards::ListService.new(parent, current_user).execute
......
- public_project_count = ProjectsFinder.new(current_user: current_user).execute.count
.blank-state-row .blank-state-row
- if current_user.can_create_project? - if current_user.can_create_project?
= link_to new_project_path, class: "blank-state blank-state-link" do = link_to new_project_path, class: "blank-state blank-state-link" do
...@@ -30,19 +28,15 @@ ...@@ -30,19 +28,15 @@
%p.blank-state-text %p.blank-state-text
Groups are the best way to manage projects and members. Groups are the best way to manage projects and members.
- if public_project_count > 0 = link_to trending_explore_projects_path, class: "blank-state blank-state-link" do
= link_to trending_explore_projects_path, class: "blank-state blank-state-link" do .blank-state-icon
.blank-state-icon = custom_icon("globe", size: 50)
= custom_icon("globe", size: 50) .blank-state-body
.blank-state-body %h3.blank-state-title
%h3.blank-state-title Explore public projects
Explore public projects %p.blank-state-text
%p.blank-state-text Public projects are an easy way to allow
There are everyone to have read-only access.
= number_with_delimiter(public_project_count)
public projects on this server.
Public projects are an easy way to allow
everyone to have read-only access.
= link_to "https://docs.gitlab.com/", class: "blank-state blank-state-link" do = link_to "https://docs.gitlab.com/", class: "blank-state blank-state-link" do
.blank-state-icon .blank-state-icon
......
...@@ -42,23 +42,27 @@ ...@@ -42,23 +42,27 @@
%button.board-delete.no-drag.p-0.border-0.has-tooltip.float-right{ type: "button", title: _("Delete list"), ":class": "{ 'd-none': !list.isExpanded }", "aria-label" => _("Delete list"), data: { placement: "bottom" }, "@click.stop" => "deleteBoard" } %button.board-delete.no-drag.p-0.border-0.has-tooltip.float-right{ type: "button", title: _("Delete list"), ":class": "{ 'd-none': !list.isExpanded }", "aria-label" => _("Delete list"), data: { placement: "bottom" }, "@click.stop" => "deleteBoard" }
= icon("trash") = icon("trash")
.issue-count-badge.no-drag.text-secondary{ "v-if" => 'list.type !== "blank" && list.type !== "promotion"', ":title": "counterTooltip", "v-tooltip": true, data: { placement: "top" } } .issue-count-badge.pr-0.no-drag.text-secondary{ "v-if" => "showBoardListAndBoardInfo", ":title": "counterTooltip", "v-tooltip": true, data: { placement: "top" } }
%span.d-inline-flex %span.d-inline-flex
%span.issue-count-badge-count %span.issue-count-badge-count
%icon.mr-1{ name: "issues" } %icon.mr-1{ name: "issues" }
{{ list.issuesSize }} %issue-count{ ":maxIssueCount" => "list.maxIssueCount",
":issuesSize" => "list.issuesSize" }
= render_if_exists "shared/boards/components/list_weight" = render_if_exists "shared/boards/components/list_weight"
%button.issue-count-badge-add-button.no-drag.btn.btn-sm.btn-default.ml-1.has-tooltip{ type: "button", %gl-button-group.board-list-button-group.pl-2{ "v-if" => "isNewIssueShown || isSettingsShown" }
"@click" => "showNewIssueForm", %gl-button.issue-count-badge-add-button.no-drag{ type: "button",
"v-if" => "isNewIssueShown", "@click" => "showNewIssueForm",
":class": "{ 'd-none': !list.isExpanded }", "v-if" => "isNewIssueShown",
"aria-label" => _("New issue"), ":class": "{ 'd-none': !list.isExpanded, 'rounded-right': isNewIssueShown && !isSettingsShown }",
"title" => _("New issue"), "aria-label" => _("New issue"),
data: { placement: "top", container: "body" } } "ref" => "newIssueBtn" }
= icon("plus") = icon("plus")
%gl-tooltip{ ":target" => "() => $refs.newIssueBtn" }
= _("New Issue")
= render_if_exists 'shared/boards/components/list_settings'
%board-list{ "v-if" => 'list.type !== "blank" && list.type !== "promotion"', %board-list{ "v-if" => "showBoardListAndBoardInfo",
":list" => "list", ":list" => "list",
":issues" => "list.issues", ":issues" => "list.issues",
":loading" => "list.loading", ":loading" => "list.loading",
......
---
title: Replacing incorrect icon for Retry in Pipeline list page
merge_request: 20510
author:
type: changed
---
title: Fixes to inconsistent margins/sapcing in the project detail page
merge_request: 20395
author:
type: changed
---
title: Fix documentation link from empty environment dashboard
merge_request: 20415
author:
type: fixed
---
title: Disable public project counts on welcome page
merge_request: 20517
author:
type: performance
...@@ -110,12 +110,23 @@ following. ...@@ -110,12 +110,23 @@ following.
1. [Restart GitLab][restart] for the changes to take effect. 1. [Restart GitLab][restart] for the changes to take effect.
To take advantage of group sync, group owners or maintainers will need to create an To take advantage of group sync, group owners or maintainers will need to [create one
LDAP group link in their group **Settings > LDAP Groups** page. or more LDAP group links](#adding-group-links).
Multiple LDAP groups and [filters](#filters-premium-only) can be linked with ### Adding group links
a single GitLab group. When the link is created, an access level/role is
specified (Guest, Reporter, Developer, Maintainer, or Owner). Once [group sync has been configured](#group-sync) on the instance, one or more LDAP
groups can be linked to a GitLab group to grant their members access to its
contents.
Group owners or maintainers can add and use LDAP group links by:
1. Navigating to the group's **Settings > LDAP Synchronization** page. Here, one or more
LDAP groups and [filters](#filters-premium-only) can be linked to this GitLab group,
each one with a configured [permission level](../../user/permissions.md#group-members-permissions)
for its members.
1. Updating the group's membership by navigating to the group's **Settings > Members**
page and clicking **Sync now**.
### Filters **(PREMIUM ONLY)** ### Filters **(PREMIUM ONLY)**
......
...@@ -718,6 +718,14 @@ Note that `enforced="true"`, meaning that authentication is being enforced. ...@@ -718,6 +718,14 @@ Note that `enforced="true"`, meaning that authentication is being enforced.
## Troubleshooting Gitaly ## Troubleshooting Gitaly
### Checking versions when using standalone Gitaly nodes
When using standalone Gitaly nodes, you must make sure they are the same version
as GitLab to ensure full compatibility. Check **Admin Area > Gitaly Servers** on
your GitLab instance and confirm all Gitaly Servers are `Up to date`.
![Gitaly standalone software versions diagram](img/gitlab_gitaly_version_mismatch_v12_4.png)
### `gitaly-debug` ### `gitaly-debug`
The `gitaly-debug` command provides "production debugging" tools for Gitaly and Git The `gitaly-debug` command provides "production debugging" tools for Gitaly and Git
......
...@@ -766,10 +766,17 @@ or a `.buildpacks` file in your project: ...@@ -766,10 +766,17 @@ or a `.buildpacks` file in your project:
and add the URL of the buildpack to use on a line in the file. If you want to and add the URL of the buildpack to use on a line in the file. If you want to
use multiple buildpacks, you can enter them in, one on each line. use multiple buildpacks, you can enter them in, one on each line.
CAUTION: **Caution:** #### Multiple buildpacks
Using multiple buildpacks isn't yet supported by Auto DevOps.
Using multiple buildpacks isn't fully supported by Auto DevOps because, when using the `.buildpacks`
file, Auto Test will not work.
The buildpack [heroku-buildpack-multi](https://github.com/heroku/heroku-buildpack-multi/),
which is used under the hood to parse the `.buildpacks` file, doesn't provide the necessary commands
`bin/test-compile` and `bin/test`.
CAUTION: **Caution:** When using the `.buildpacks` file, Auto Test will not work. The buildpack [heroku-buildpack-multi](https://github.com/heroku/heroku-buildpack-multi/) (which is used under the hood to parse the `.buildpacks` file) doesn't provide the necessary commands `bin/test-compile` and `bin/test`. Make sure to provide the project variable `BUILDPACK_URL` instead. If your goal is to use only a single custom buildpack, you should provide the project variable
`BUILDPACK_URL` instead.
### Custom `Dockerfile` ### Custom `Dockerfile`
......
...@@ -123,6 +123,25 @@ NOTE: **Note:** GitLab is unable to provide support for IdPs that are not listed ...@@ -123,6 +123,25 @@ NOTE: **Note:** GitLab is unable to provide support for IdPs that are not listed
When [configuring your identify provider](#configuring-your-identity-provider), please consider the notes below for specific providers to help avoid common issues and as a guide for terminology used. When [configuring your identify provider](#configuring-your-identity-provider), please consider the notes below for specific providers to help avoid common issues and as a guide for terminology used.
### Azure setup notes
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For a demo of the Azure SAML setup including SCIM, see [SCIM Provisioning on Azure Using SAML SSO for Groups Demo](https://youtu.be/24-ZxmTeEBU).
| GitLab Setting | Azure Field |
|--------------|----------------|
| Identifier | Identifier (Entity ID) |
| Assertion consumer service URL | Reply URL (Assertion Consumer Service URL) |
| Identity provider single sign on URL | Login URL |
| Certificate fingerprint | Thumbprint |
We recommend:
- **Unique User Identifier (Name identifier)** set to `user.objectID`.
- **nameid-format** set to persistent.
Set other user attributes and claims according to the [assertions table](#assertions).
### Okta setup notes ### Okta setup notes
| GitLab Setting | Okta Field | | GitLab Setting | Okta Field |
......
...@@ -29,7 +29,9 @@ module Banzai ...@@ -29,7 +29,9 @@ module Banzai
# If in the future the syntax is about to be made GitHub-compatible, please, add `:GITHUB_PRE_LANG` render option below # If in the future the syntax is about to be made GitHub-compatible, please, add `:GITHUB_PRE_LANG` render option below
# and remove `code_block` method from `lib/banzai/renderer/common_mark/html.rb`. # and remove `code_block` method from `lib/banzai/renderer/common_mark/html.rb`.
RENDER_OPTIONS = [ RENDER_OPTIONS = [
:DEFAULT # default rendering system. Nothing special. # as of commonmarker 0.18.0, we need to use :UNSAFE to get the same as the original :DEFAULT
# https://github.com/gjtorikian/commonmarker/pull/81
:UNSAFE
].freeze ].freeze
RENDER_OPTIONS_SOURCEPOS = RENDER_OPTIONS + [ RENDER_OPTIONS_SOURCEPOS = RENDER_OPTIONS + [
......
...@@ -10291,6 +10291,9 @@ msgstr "" ...@@ -10291,6 +10291,9 @@ msgstr ""
msgid "List" msgid "List"
msgstr "" msgstr ""
msgid "List Settings"
msgstr ""
msgid "List Your Gitea Repositories" msgid "List Your Gitea Repositories"
msgstr "" msgstr ""
......
...@@ -5,6 +5,7 @@ FactoryBot.define do ...@@ -5,6 +5,7 @@ FactoryBot.define do
board board
label label
list_type { :label } list_type { :label }
max_issue_count { 0 }
sequence(:position) sequence(:position)
end end
......
...@@ -72,7 +72,6 @@ describe 'Issue Boards', :js do ...@@ -72,7 +72,6 @@ describe 'Issue Boards', :js do
let!(:closed) { create(:label, project: project, name: 'Closed') } let!(:closed) { create(:label, project: project, name: 'Closed') }
let!(:accepting) { create(:label, project: project, name: 'Accepting Merge Requests') } let!(:accepting) { create(:label, project: project, name: 'Accepting Merge Requests') }
let!(:a_plus) { create(:label, project: project, name: 'A+') } let!(:a_plus) { create(:label, project: project, name: 'A+') }
let!(:list1) { create(:list, board: board, label: planning, position: 0) } let!(:list1) { create(:list, board: board, label: planning, position: 0) }
let!(:list2) { create(:list, board: board, label: development, position: 1) } let!(:list2) { create(:list, board: board, label: development, position: 1) }
...@@ -289,6 +288,17 @@ describe 'Issue Boards', :js do ...@@ -289,6 +288,17 @@ describe 'Issue Boards', :js do
expect(page).to have_selector('.avatar', count: 1) expect(page).to have_selector('.avatar', count: 1)
end end
end end
context 'list header' do
let(:total_planning_issues) { "8" }
it 'shows issue count on the list' do
page.within(find(".board:nth-child(2)")) do
expect(page.find('.js-issue-size')).to have_text(total_planning_issues)
expect(page).not_to have_selector('.js-max-issue-size')
end
end
end
end end
context 'new list' do context 'new list' do
......
import { shallowMount } from '@vue/test-utils';
import IssueCount from '~/boards/components/issue_count.vue';
describe('IssueCount', () => {
let vm;
let maxIssueCount;
let issuesSize;
const createComponent = props => {
vm = shallowMount(IssueCount, { propsData: props });
};
afterEach(() => {
maxIssueCount = 0;
issuesSize = 0;
if (vm) vm.destroy();
});
describe('when maxIssueCount is zero', () => {
beforeEach(() => {
issuesSize = 3;
createComponent({ maxIssueCount: 0, issuesSize });
});
it('contains issueSize in the template', () => {
expect(vm.find('.js-issue-size').text()).toEqual(String(issuesSize));
});
it('does not contains maxIssueCount in the template', () => {
expect(vm.contains('.js-max-issue-size')).toBe(false);
});
});
describe('when maxIssueCount is greater than zero', () => {
beforeEach(() => {
maxIssueCount = 2;
issuesSize = 1;
createComponent({ maxIssueCount, issuesSize });
});
afterEach(() => {
vm.destroy();
});
it('contains issueSize in the template', () => {
expect(vm.find('.js-issue-size').text()).toEqual(String(issuesSize));
});
it('contains maxIssueCount in the template', () => {
expect(vm.find('.js-max-issue-size').text()).toEqual(String(maxIssueCount));
});
it('does not have text-danger class when issueSize is less than maxIssueCount', () => {
expect(vm.classes('.text-danger')).toBe(false);
});
});
describe('when issueSize is greater than maxIssueCount', () => {
beforeEach(() => {
issuesSize = 3;
maxIssueCount = 2;
createComponent({ maxIssueCount, issuesSize });
});
afterEach(() => {
vm.destroy();
});
it('contains issueSize in the template', () => {
expect(vm.find('.js-issue-size').text()).toEqual(String(issuesSize));
});
it('contains maxIssueCount in the template', () => {
expect(vm.find('.js-max-issue-size').text()).toEqual(String(maxIssueCount));
});
it('has text-danger class', () => {
expect(vm.find('.text-danger').text()).toEqual(String(issuesSize));
});
});
});
...@@ -207,4 +207,56 @@ describe('Board list component', () => { ...@@ -207,4 +207,56 @@ describe('Board list component', () => {
.catch(done.fail); .catch(done.fail);
}); });
}); });
describe('max issue count warning', () => {
beforeEach(done => {
({ mock, component } = createComponent({
done,
listProps: { type: 'closed', collapsed: true, issuesSize: 50 },
}));
});
afterEach(() => {
mock.restore();
component.$destroy();
});
describe('when issue count exceeds max issue count', () => {
it('sets background to bg-danger-100', done => {
component.list.issuesSize = 4;
component.list.maxIssueCount = 3;
Vue.nextTick(() => {
expect(component.$el.querySelector('.bg-danger-100')).not.toBeNull();
done();
});
});
});
describe('when list issue count does NOT exceed list max issue count', () => {
it('does not sets background to bg-danger-100', done => {
component.list.issuesSize = 2;
component.list.maxIssueCount = 3;
Vue.nextTick(() => {
expect(component.$el.querySelector('.bg-danger-100')).toBeNull();
done();
});
});
});
describe('when list max issue count is 0', () => {
it('does not sets background to bg-danger-100', done => {
component.list.maxIssueCount = 0;
Vue.nextTick(() => {
expect(component.$el.querySelector('.bg-danger-100')).toBeNull();
done();
});
});
});
});
}); });
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