diff options
32 files changed, 350 insertions, 48 deletions
@@ -139,7 +139,7 @@ gem 'html-pipeline', '~> 2.8' gem 'deckar01-task_list', '2.2.1' gem 'gitlab-markup', '~> 1.7.0' gem 'github-markup', '~> 1.7.0', require: 'github/markup' -gem 'commonmarker', '~> 0.17' +gem 'commonmarker', '~> 0.20' gem 'RedCloth', '~> 4.3.2' gem 'rdoc', '~> 6.0' gem 'org-ruby', '~> 0.9.12' diff --git a/Gemfile.lock b/Gemfile.lock index cf78043b1ac..a568e0c59e0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -157,7 +157,7 @@ GEM coercible (1.0.0) descendants_tracker (~> 0.0.1) colored2 (3.1.2) - commonmarker (0.17.13) + commonmarker (0.20.1) ruby-enum (~> 0.5) concord (0.1.5) adamantium (~> 0.2.0) @@ -1146,7 +1146,7 @@ DEPENDENCIES carrierwave (~> 1.3) charlock_holmes (~> 0.7.5) chronic (~> 0.10.2) - commonmarker (~> 0.17) + commonmarker (~> 0.20) concurrent-ruby (~> 1.1) connection_pool (~> 2.0) countries (~> 3.0) diff --git a/app/assets/javascripts/boards/components/board.js b/app/assets/javascripts/boards/components/board.js index 58759fd1efe..64941103eb7 100644 --- a/app/assets/javascripts/boards/components/board.js +++ b/app/assets/javascripts/boards/components/board.js @@ -1,15 +1,19 @@ import $ from 'jquery'; import Sortable from 'sortablejs'; import Vue from 'vue'; +import { GlButtonGroup, GlButton, GlTooltip } from '@gitlab/ui'; import { n__, s__ } from '~/locale'; import Icon from '~/vue_shared/components/icon.vue'; 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 BoardBlankState from './board_blank_state.vue'; import BoardDelete from './board_delete'; import BoardList from './board_list.vue'; +import IssueCount from './issue_count.vue'; import boardsStore from '../stores/boards_store'; import { getBoardSortableDefaultOptions, sortableEnd } from '../mixins/sortable_default_options'; +import { ListType } from '../constants'; export default Vue.extend({ components: { @@ -17,10 +21,15 @@ export default Vue.extend({ BoardDelete, BoardList, Icon, + GlButtonGroup, + IssueCount, + GlButton, + GlTooltip, }, directives: { Tooltip, }, + mixins: [isWipLimitsOn], props: { list: { type: Object, @@ -53,6 +62,11 @@ export default Vue.extend({ isLoggedIn() { return Boolean(gon.current_user_id); }, + showListHeaderButton() { + return ( + !this.disabled && this.list.type !== ListType.closed && this.list.type !== ListType.blank + ); + }, counterTooltip() { const { issuesSize } = this.list; return `${n__('%d issue', '%d issues', issuesSize)}`; @@ -61,11 +75,19 @@ export default Vue.extend({ return this.list.isExpanded ? s__('Boards|Collapse') : s__('Boards|Expand'); }, isNewIssueShown() { + return this.list.type === ListType.backlog || this.showListHeaderButton; + }, + isSettingsShown() { return ( - this.list.type === 'backlog' || - (!this.disabled && this.list.type !== 'closed' && this.list.type !== 'blank') + this.list.type !== ListType.backlog && + this.showListHeaderButton && + this.list.isExpanded && + this.isWipLimitsOn ); }, + showBoardListAndBoardInfo() { + return this.list.type !== ListType.blank && this.list.type !== ListType.promotion; + }, uniqueKey() { // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings return `boards.${this.boardId}.${this.list.type}.${this.list.id}`; diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue index b8439bc8741..1e54d4d6b7d 100644 --- a/app/assets/javascripts/boards/components/board_list.vue +++ b/app/assets/javascripts/boards/components/board_list.vue @@ -71,6 +71,9 @@ export default { total: this.list.issuesSize, }); }, + issuesSizeExceedsMax() { + return this.list.maxIssueCount > 0 && this.list.issuesSize > this.list.maxIssueCount; + }, }, watch: { filters: { @@ -435,7 +438,7 @@ export default { ref="list" :data-board="list.id" :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" > <board-card diff --git a/app/assets/javascripts/boards/components/issue_count.vue b/app/assets/javascripts/boards/components/issue_count.vue new file mode 100644 index 00000000000..c50a3c1c0d3 --- /dev/null +++ b/app/assets/javascripts/boards/components/issue_count.vue @@ -0,0 +1,36 @@ +<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> diff --git a/app/assets/javascripts/boards/constants.js b/app/assets/javascripts/boards/constants.js index 3c66c7a0660..dcecfe5e1bb 100644 --- a/app/assets/javascripts/boards/constants.js +++ b/app/assets/javascripts/boards/constants.js @@ -4,6 +4,8 @@ export const ListType = { backlog: 'backlog', closed: 'closed', label: 'label', + promotion: 'promotion', + blank: 'blank', }; export default { diff --git a/app/assets/javascripts/boards/mixins/is_wip_limits.js b/app/assets/javascripts/boards/mixins/is_wip_limits.js new file mode 100644 index 00000000000..f172179d3c7 --- /dev/null +++ b/app/assets/javascripts/boards/mixins/is_wip_limits.js @@ -0,0 +1,7 @@ +export default { + computed: { + isWipLimitsOn() { + return false; + }, + }, +}; diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js index bb8c8e68297..34e0d0a83ea 100644 --- a/app/assets/javascripts/boards/models/list.js +++ b/app/assets/javascripts/boards/models/list.js @@ -52,6 +52,9 @@ class List { this.loadingMore = false; this.issues = obj.issues || []; this.issuesSize = obj.issuesSize ? obj.issuesSize : 0; + this.maxIssueCount = Object.hasOwnProperty.call(obj, 'max_issue_count') + ? obj.max_issue_count + : 0; this.defaultAvatar = defaultAvatar; if (obj.label) { diff --git a/app/assets/javascripts/related_merge_requests/components/related_merge_requests.vue b/app/assets/javascripts/related_merge_requests/components/related_merge_requests.vue index f0112a5a623..dc7c9d9f174 100644 --- a/app/assets/javascripts/related_merge_requests/components/related_merge_requests.vue +++ b/app/assets/javascripts/related_merge_requests/components/related_merge_requests.vue @@ -72,7 +72,7 @@ export default { {{ __('Related merge requests') }} </span> <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"> <svg class="s16 mr-1 text-secondary"> <icon name="merge-request" class="mr-1 text-secondary" /> diff --git a/app/assets/stylesheets/framework/wells.scss b/app/assets/stylesheets/framework/wells.scss index 434cbd6d21c..3eff1807403 100644 --- a/app/assets/stylesheets/framework/wells.scss +++ b/app/assets/stylesheets/framework/wells.scss @@ -3,7 +3,7 @@ color: $gl-text-color; border: 1px solid $border-color; border-radius: $border-radius-default; - margin-bottom: $gl-padding; + margin-bottom: $gl-padding-8; .card.card-body-segment { padding: $gl-padding; diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index d26979bc174..90c2e369ccd 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -187,6 +187,10 @@ font-size: 1em; border-bottom: 1px solid $border-color; padding: $gl-padding-8 $gl-padding; + + .js-max-issue-size::before { + content: '/'; + } } .board-title-text { diff --git a/app/assets/stylesheets/pages/issues/issue_count_badge.scss b/app/assets/stylesheets/pages/issues/issue_count_badge.scss index 64ca61f7094..569f323abd8 100644 --- a/app/assets/stylesheets/pages/issues/issue_count_badge.scss +++ b/app/assets/stylesheets/pages/issues/issue_count_badge.scss @@ -2,7 +2,6 @@ .mr-count-badge { display: inline-flex; border-radius: $border-radius-base; - border: 1px solid $border-color; padding: 5px $gl-padding-8; } diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index d96cc163738..55e4c051a6b 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -748,7 +748,7 @@ display: inline-block; &:not(:last-child) { - margin-right: $gl-padding; + margin-right: $gl-padding-8; } &.right { @@ -798,7 +798,7 @@ } .btn { - margin-top: $gl-padding; + margin-top: $gl-padding-8; padding: $gl-btn-vert-padding $gl-btn-padding; line-height: $gl-btn-line-height; diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index 5664f46484e..6e7081aa4ec 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -1,6 +1,6 @@ .tree-holder { .nav-block { - margin: 10px 0; + margin: 16px 0; .btn .fa, .btn svg { @@ -15,8 +15,13 @@ } .tree-controls { + display: flex; text-align: right; + .btn { + margin-left: 8px; + } + .btn-group { margin-left: 10px; } diff --git a/app/controllers/concerns/boards_actions.rb b/app/controllers/concerns/boards_actions.rb index a093d0d6e7f..3a9a59b0ad7 100644 --- a/app/controllers/concerns/boards_actions.rb +++ b/app/controllers/concerns/boards_actions.rb @@ -9,6 +9,7 @@ module BoardsActions before_action :boards, only: :index before_action :board, only: :show + before_action :push_wip_limits, only: :index end def index @@ -24,6 +25,10 @@ module BoardsActions private + # Noop on FOSS + def push_wip_limits + end + def boards strong_memoize(:boards) do Boards::ListService.new(parent, current_user).execute diff --git a/app/views/dashboard/projects/_blank_state_welcome.html.haml b/app/views/dashboard/projects/_blank_state_welcome.html.haml index 913f0e8cfae..003e6f18b33 100644 --- a/app/views/dashboard/projects/_blank_state_welcome.html.haml +++ b/app/views/dashboard/projects/_blank_state_welcome.html.haml @@ -1,5 +1,3 @@ -- public_project_count = ProjectsFinder.new(current_user: current_user).execute.count - .blank-state-row - if current_user.can_create_project? = link_to new_project_path, class: "blank-state blank-state-link" do @@ -30,19 +28,15 @@ %p.blank-state-text 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 - .blank-state-icon - = custom_icon("globe", size: 50) - .blank-state-body - %h3.blank-state-title - Explore public projects - %p.blank-state-text - There are - = 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 trending_explore_projects_path, class: "blank-state blank-state-link" do + .blank-state-icon + = custom_icon("globe", size: 50) + .blank-state-body + %h3.blank-state-title + Explore public projects + %p.blank-state-text + 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 .blank-state-icon diff --git a/app/views/shared/boards/components/_board.html.haml b/app/views/shared/boards/components/_board.html.haml index ffa24d1c041..eb9b7f6c48a 100644 --- a/app/views/shared/boards/components/_board.html.haml +++ b/app/views/shared/boards/components/_board.html.haml @@ -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" } = 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.issue-count-badge-count %icon.mr-1{ name: "issues" } - {{ list.issuesSize }} + %issue-count{ ":maxIssueCount" => "list.maxIssueCount", + ":issuesSize" => "list.issuesSize" } = 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", - "@click" => "showNewIssueForm", - "v-if" => "isNewIssueShown", - ":class": "{ 'd-none': !list.isExpanded }", - "aria-label" => _("New issue"), - "title" => _("New issue"), - data: { placement: "top", container: "body" } } - = icon("plus") + %gl-button-group.board-list-button-group.pl-2{ "v-if" => "isNewIssueShown || isSettingsShown" } + %gl-button.issue-count-badge-add-button.no-drag{ type: "button", + "@click" => "showNewIssueForm", + "v-if" => "isNewIssueShown", + ":class": "{ 'd-none': !list.isExpanded, 'rounded-right': isNewIssueShown && !isSettingsShown }", + "aria-label" => _("New issue"), + "ref" => "newIssueBtn" } + = 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", ":issues" => "list.issues", ":loading" => "list.loading", diff --git a/changelogs/unreleased/13768-fix-redo-icn.yml b/changelogs/unreleased/13768-fix-redo-icn.yml new file mode 100644 index 00000000000..3ef194bc4b0 --- /dev/null +++ b/changelogs/unreleased/13768-fix-redo-icn.yml @@ -0,0 +1,5 @@ +--- +title: Replacing incorrect icon for Retry in Pipeline list page +merge_request: 20510 +author: +type: changed diff --git a/changelogs/unreleased/35468-fixdetail-margins.yml b/changelogs/unreleased/35468-fixdetail-margins.yml new file mode 100644 index 00000000000..74333524ed1 --- /dev/null +++ b/changelogs/unreleased/35468-fixdetail-margins.yml @@ -0,0 +1,5 @@ +--- +title: Fixes to inconsistent margins/sapcing in the project detail page +merge_request: 20395 +author: +type: changed diff --git a/changelogs/unreleased/fix-env-doc-link.yml b/changelogs/unreleased/fix-env-doc-link.yml new file mode 100644 index 00000000000..0ee877a3029 --- /dev/null +++ b/changelogs/unreleased/fix-env-doc-link.yml @@ -0,0 +1,5 @@ +--- +title: Fix documentation link from empty environment dashboard +merge_request: 20415 +author: +type: fixed diff --git a/changelogs/unreleased/sh-avoid-project-count-new-user.yml b/changelogs/unreleased/sh-avoid-project-count-new-user.yml new file mode 100644 index 00000000000..916a3579fb6 --- /dev/null +++ b/changelogs/unreleased/sh-avoid-project-count-new-user.yml @@ -0,0 +1,5 @@ +--- +title: Disable public project counts on welcome page +merge_request: 20517 +author: +type: performance diff --git a/doc/administration/auth/ldap-ee.md b/doc/administration/auth/ldap-ee.md index b1d4c8e4298..ba104a4c574 100644 --- a/doc/administration/auth/ldap-ee.md +++ b/doc/administration/auth/ldap-ee.md @@ -110,12 +110,23 @@ following. 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 -LDAP group link in their group **Settings > LDAP Groups** page. +To take advantage of group sync, group owners or maintainers will need to [create one +or more LDAP group links](#adding-group-links). -Multiple LDAP groups and [filters](#filters-premium-only) can be linked with -a single GitLab group. When the link is created, an access level/role is -specified (Guest, Reporter, Developer, Maintainer, or Owner). +### Adding group links + +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)** diff --git a/doc/administration/gitaly/img/gitlab_gitaly_version_mismatch_v12_4.png b/doc/administration/gitaly/img/gitlab_gitaly_version_mismatch_v12_4.png Binary files differnew file mode 100644 index 00000000000..4d2c5cdb00c --- /dev/null +++ b/doc/administration/gitaly/img/gitlab_gitaly_version_mismatch_v12_4.png diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md index 40e7a8d4da2..314f5aad82e 100644 --- a/doc/administration/gitaly/index.md +++ b/doc/administration/gitaly/index.md @@ -718,6 +718,14 @@ Note that `enforced="true"`, meaning that authentication is being enforced. ## 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` The `gitaly-debug` command provides "production debugging" tools for Gitaly and Git diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md index 93549ac4de5..f386c5ae42a 100644 --- a/doc/topics/autodevops/index.md +++ b/doc/topics/autodevops/index.md @@ -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 use multiple buildpacks, you can enter them in, one on each line. -CAUTION: **Caution:** -Using multiple buildpacks isn't yet supported by Auto DevOps. +#### Multiple buildpacks + +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` diff --git a/doc/user/group/saml_sso/index.md b/doc/user/group/saml_sso/index.md index 6fd56414796..fef47fba3a1 100644 --- a/doc/user/group/saml_sso/index.md +++ b/doc/user/group/saml_sso/index.md @@ -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. +### 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 | GitLab Setting | Okta Field | diff --git a/lib/banzai/filter/markdown_engines/common_mark.rb b/lib/banzai/filter/markdown_engines/common_mark.rb index d3af776db05..7be52fc497f 100644 --- a/lib/banzai/filter/markdown_engines/common_mark.rb +++ b/lib/banzai/filter/markdown_engines/common_mark.rb @@ -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 # and remove `code_block` method from `lib/banzai/renderer/common_mark/html.rb`. 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 RENDER_OPTIONS_SOURCEPOS = RENDER_OPTIONS + [ diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 16f8ae20805..4f0e112a49d 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -10291,6 +10291,9 @@ msgstr "" msgid "List" msgstr "" +msgid "List Settings" +msgstr "" + msgid "List Your Gitea Repositories" msgstr "" diff --git a/spec/factories/lists.rb b/spec/factories/lists.rb index 8785d3f0468..eb6f0f27917 100644 --- a/spec/factories/lists.rb +++ b/spec/factories/lists.rb @@ -5,6 +5,7 @@ FactoryBot.define do board label list_type { :label } + max_issue_count { 0 } sequence(:position) end diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb index e26582d3444..c740e4e26d9 100644 --- a/spec/features/boards/boards_spec.rb +++ b/spec/features/boards/boards_spec.rb @@ -72,7 +72,6 @@ describe 'Issue Boards', :js do let!(:closed) { create(:label, project: project, name: 'Closed') } let!(:accepting) { create(:label, project: project, name: 'Accepting Merge Requests') } let!(:a_plus) { create(:label, project: project, name: 'A+') } - let!(:list1) { create(:list, board: board, label: planning, position: 0) } let!(:list2) { create(:list, board: board, label: development, position: 1) } @@ -289,6 +288,17 @@ describe 'Issue Boards', :js do expect(page).to have_selector('.avatar', count: 1) 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 context 'new list' do diff --git a/spec/frontend/boards/components/issue_count_spec.js b/spec/frontend/boards/components/issue_count_spec.js new file mode 100644 index 00000000000..819d878f4e2 --- /dev/null +++ b/spec/frontend/boards/components/issue_count_spec.js @@ -0,0 +1,85 @@ +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)); + }); + }); +}); diff --git a/spec/javascripts/boards/board_list_spec.js b/spec/javascripts/boards/board_list_spec.js index 37e96e97279..b4e1d3b97b1 100644 --- a/spec/javascripts/boards/board_list_spec.js +++ b/spec/javascripts/boards/board_list_spec.js @@ -207,4 +207,56 @@ describe('Board list component', () => { .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(); + }); + }); + }); + }); }); |