diff options
25 files changed, 334 insertions, 52 deletions
diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js index dc06b62cebb..65721483bb0 100644 --- a/app/assets/javascripts/boards/stores/actions.js +++ b/app/assets/javascripts/boards/stores/actions.js @@ -603,7 +603,7 @@ export default { }); }, - addListItem: ({ commit }, { list, item, position, inProgress = false }) => { + addListItem: ({ commit, dispatch }, { list, item, position, inProgress = false }) => { commit(types.ADD_BOARD_ITEM_TO_LIST, { listId: list.id, itemId: item.id, @@ -611,6 +611,9 @@ export default { inProgress, }); commit(types.UPDATE_BOARD_ITEM, item); + if (!inProgress) { + dispatch('setActiveId', { id: item.id, sidebarType: ISSUABLE }); + } }, removeListItem: ({ commit }, { listId, itemId }) => { diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb index abf0c180d6b..21a19aa22a1 100644 --- a/app/finders/issues_finder.rb +++ b/app/finders/issues_finder.rb @@ -91,6 +91,12 @@ class IssuesFinder < IssuableFinder by_issue_types(issues) end + # Negates all params found in `negatable_params` + def filter_negated_items(items) + issues = super + by_negated_issue_types(issues) + end + def by_confidential(items) return items if params[:confidential].nil? @@ -122,6 +128,13 @@ class IssuesFinder < IssuableFinder items.with_issue_type(params[:issue_types]) end + + def by_negated_issue_types(items) + issue_type_params = Array(not_params[:issue_types]).map(&:to_s) & WorkItem::Type.base_types.keys + return items if issue_type_params.blank? + + items.without_issue_type(issue_type_params) + end end IssuesFinder.prepend_mod_with('IssuesFinder') diff --git a/app/models/concerns/ci/metadatable.rb b/app/models/concerns/ci/metadatable.rb index ec86746ae54..344f5aa4cd5 100644 --- a/app/models/concerns/ci/metadatable.rb +++ b/app/models/concerns/ci/metadatable.rb @@ -20,6 +20,7 @@ module Ci delegate :interruptible, to: :metadata, prefix: false, allow_nil: true delegate :has_exposed_artifacts?, to: :metadata, prefix: false, allow_nil: true delegate :environment_auto_stop_in, to: :metadata, prefix: false, allow_nil: true + delegate :runner_features, to: :metadata, prefix: false, allow_nil: false before_create :ensure_metadata end diff --git a/app/models/issue.rb b/app/models/issue.rb index e0b0c352c22..edf5dd760c3 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -127,6 +127,7 @@ class Issue < ApplicationRecord project: [:route, { namespace: :route }]) } scope :with_issue_type, ->(types) { where(issue_type: types) } + scope :without_issue_type, ->(types) { where.not(issue_type: types) } scope :public_only, -> { without_hidden.where(confidential: false) diff --git a/danger/pajamas/Dangerfile b/danger/pajamas/Dangerfile index a3ff1126bd1..fde12c08b35 100644 --- a/danger/pajamas/Dangerfile +++ b/danger/pajamas/Dangerfile @@ -59,7 +59,7 @@ MARKDOWN if blocking_components_in_mr.any? markdown(<<~MARKDOWN) - These deprecated components have already been migrated and can no longer be used. Please use [Pajamas components](https://design.gitlab.com/components/status/) instead. + These deprecated components have already been migrated and can no longer be used. Please use [Pajamas components](https://design.gitlab.com/components/overview) instead. * #{blocking_components_in_mr.join("\n* ")} @@ -70,7 +70,7 @@ end if deprecated_components_in_mr.any? markdown(<<~MARKDOWN) - These deprecated components are in the process of being migrated. Please consider using [Pajamas components](https://design.gitlab.com/components/status/) instead. + These deprecated components are in the process of being migrated. Please consider using [Pajamas components](https://design.gitlab.com/components/overview) instead. * #{deprecated_components_in_mr.join("\n* ")} diff --git a/db/migrate/20210912034903_add_runner_features_to_ci_builds_metadata.rb b/db/migrate/20210912034903_add_runner_features_to_ci_builds_metadata.rb new file mode 100644 index 00000000000..83eddf2fb0d --- /dev/null +++ b/db/migrate/20210912034903_add_runner_features_to_ci_builds_metadata.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddRunnerFeaturesToCiBuildsMetadata < Gitlab::Database::Migration[1.0] + enable_lock_retries! + + def change + add_column :ci_builds_metadata, :runner_features, :jsonb, default: {}, null: false + end +end diff --git a/db/schema_migrations/20210912034903 b/db/schema_migrations/20210912034903 new file mode 100644 index 00000000000..8003a5b110c --- /dev/null +++ b/db/schema_migrations/20210912034903 @@ -0,0 +1 @@ +76bfbf3f12fed895c3cfb891080b5a452d1204c83ce96736048f857b33458ad1
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index d7e00112d73..cb79fe4610c 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -11410,7 +11410,8 @@ CREATE TABLE ci_builds_metadata ( expanded_environment_name character varying(255), secrets jsonb DEFAULT '{}'::jsonb NOT NULL, build_id bigint NOT NULL, - id bigint NOT NULL + id bigint NOT NULL, + runner_features jsonb DEFAULT '{}'::jsonb NOT NULL ); CREATE SEQUENCE ci_builds_metadata_id_seq diff --git a/doc/integration/gitlab.md b/doc/integration/gitlab.md index 1f35faacc2e..43dfc013ade 100644 --- a/doc/integration/gitlab.md +++ b/doc/integration/gitlab.md @@ -48,7 +48,7 @@ GitLab.com generates an application ID and secret key for you to use. 1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. 1. Add the provider configuration: - For Omnibus installations: + For Omnibus installations authenticating against **GitLab.com**: ```ruby gitlab_rails['omniauth_providers'] = [ @@ -61,7 +61,20 @@ GitLab.com generates an application ID and secret key for you to use. ] ``` - For installations from source: + Or, for Omnibus installations authenticating against a different GitLab instance: + + ```ruby + gitlab_rails['omniauth_providers'] = [ + { + "name" => "gitlab", + "app_id" => "YOUR_APP_ID", + "app_secret" => "YOUR_APP_SECRET", + "args" => { "scope" => "api", "client_options" => { "site" => "https://gitlab.example.com/api/v4" } } + } + ] + ``` + + For installations from source authenticating against **GitLab.com**: ```yaml - { name: 'gitlab', @@ -70,6 +83,15 @@ GitLab.com generates an application ID and secret key for you to use. args: { scope: 'api' } } ``` + Or, for installations from source to authenticate against a different GitLab instance: + + ```yaml + - { name: 'gitlab', + app_id: 'YOUR_APP_ID', + app_secret: 'YOUR_APP_SECRET', + args: { scope: 'api', "client_options": { "site": 'https://gitlab.example.com/api/v4' } } + ``` + 1. Change `'YOUR_APP_ID'` to the Application ID from the GitLab.com application page. 1. Change `'YOUR_APP_SECRET'` to the secret from the GitLab.com application page. 1. Save the configuration file. diff --git a/qa/Gemfile b/qa/Gemfile index cc2355cdfa3..9b1735fe646 100644 --- a/qa/Gemfile +++ b/qa/Gemfile @@ -9,7 +9,7 @@ gem 'capybara', '~> 3.35.0' gem 'capybara-screenshot', '~> 1.0.23' gem 'rake', '~> 12.3.3' gem 'rspec', '~> 3.10' -gem 'selenium-webdriver', '~> 4.0.0.beta4' +gem 'selenium-webdriver', '~> 4.0.0.rc1' gem 'airborne', '~> 0.3.4', require: false # airborne is messing with rspec sandboxed mode so not requiring by default gem 'rest-client', '~> 2.1.0' gem 'rspec-retry', '~> 0.6.1', require: 'rspec/retry' @@ -26,7 +26,7 @@ gem 'webdrivers', '~> 4.6' gem 'zeitwerk', '~> 2.4' gem 'influxdb-client', '~> 1.17' -gem 'chemlab', '~> 0.7' +gem 'chemlab', '~> 0.8' gem 'chemlab-library-www-gitlab-com', '~> 0.1' gem 'deprecation_toolkit', '~> 1.5.1', require: false diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock index 5f33afaa77b..2f9f14e1ac7 100644 --- a/qa/Gemfile.lock +++ b/qa/Gemfile.lock @@ -41,7 +41,7 @@ GEM capybara-screenshot (1.0.23) capybara (>= 1.0, < 4) launchy - chemlab (0.7.2) + chemlab (0.8.0) colorize (~> 0.8) i18n (~> 1.8) rake (>= 12, < 14) @@ -135,7 +135,7 @@ GEM rack-test (1.1.0) rack (>= 1.0, < 3) rake (12.3.3) - regexp_parser (1.8.2) + regexp_parser (2.1.1) require_all (3.0.0) rest-client (2.1.0) http-accept (>= 1.7.0, < 2.0) @@ -174,7 +174,7 @@ GEM sawyer (0.8.2) addressable (>= 2.3.5) faraday (> 0.8, < 2.0) - selenium-webdriver (4.0.0.beta4) + selenium-webdriver (4.0.0.rc1) childprocess (>= 0.5, < 5.0) rexml (~> 3.2) rubyzip (>= 1.2.2) @@ -216,7 +216,7 @@ DEPENDENCIES allure-rspec (~> 2.14.5) capybara (~> 3.35.0) capybara-screenshot (~> 1.0.23) - chemlab (~> 0.7) + chemlab (~> 0.8) chemlab-library-www-gitlab-com (~> 0.1) deprecation_toolkit (~> 1.5.1) faker (~> 2.19, >= 2.19.0) @@ -235,7 +235,7 @@ DEPENDENCIES rspec-retry (~> 0.6.1) rspec_junit_formatter (~> 0.4.1) ruby-debug-ide (~> 0.7.0) - selenium-webdriver (~> 4.0.0.beta4) + selenium-webdriver (~> 4.0.0.rc1) timecop (~> 0.9.1) webdrivers (~> 4.6) zeitwerk (~> 2.4) diff --git a/qa/chemlab-library-gitlab.gemspec b/qa/chemlab-library-gitlab.gemspec index 908aad01850..d42d0e5e926 100644 --- a/qa/chemlab-library-gitlab.gemspec +++ b/qa/chemlab-library-gitlab.gemspec @@ -4,7 +4,7 @@ $:.unshift(File.expand_path('lib', __dir__)) Gem::Specification.new do |spec| spec.name = 'chemlab-library-gitlab' - spec.version = '0.1.1' + spec.version = '0.2.0' spec.authors = ['GitLab Quality'] spec.email = ['quality@gitlab.com'] @@ -18,5 +18,5 @@ Gem::Specification.new do |spec| spec.require_paths = ['lib'] - spec.add_runtime_dependency 'chemlab', '~> 0.7' + spec.add_runtime_dependency 'chemlab', '~> 0.8' end diff --git a/qa/lib/gitlab.rb b/qa/lib/gitlab.rb index d0d1d535114..5b5c8805868 100644 --- a/qa/lib/gitlab.rb +++ b/qa/lib/gitlab.rb @@ -1,7 +1,11 @@ # frozen_string_literal: true +require 'chemlab/library' + # Chemlab Page Libraries for GitLab module Gitlab + include Chemlab::Library + module Page module Main autoload :Login, 'gitlab/page/main/login' diff --git a/qa/qa/page/component/web_ide/web_terminal_panel.rb b/qa/qa/page/component/web_ide/web_terminal_panel.rb new file mode 100644 index 00000000000..80f83bcff7c --- /dev/null +++ b/qa/qa/page/component/web_ide/web_terminal_panel.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +module QA + module Page + module Component + module WebIDE + module WebTerminalPanel + extend QA::Page::PageConcern + + def self.prepended(base) + super + + base.class_eval do + view 'app/assets/javascripts/ide/components/panes/collapsible_sidebar.vue' do + element :ide_right_sidebar, %q(:data-qa-selector="`ide_${side}_sidebar`") # rubocop:disable QA/ElementWithPattern + end + + view 'app/assets/javascripts/ide/components/ide_sidebar_nav.vue' do + element :terminal_tab_button, %q(:data-qa-selector="`${tab.title.toLowerCase()}_tab_button`") # rubocop:disable QA/ElementWithPattern + end + + view 'app/assets/javascripts/ide/components/terminal/empty_state.vue' do + element :start_web_terminal_button + end + + view 'app/assets/javascripts/ide/components/terminal/terminal.vue' do + element :loading_container + element :terminal_screen + end + end + end + + def has_finished_loading? + has_no_element?(:loading_container, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME) + end + + def has_terminal_screen? + wait_until(reload: false) do + within_element :terminal_screen do + # The DOM initially just includes the :terminal_screen element + # and then the xterm package dynamically loads when the user + # clicks the Start Web Terminal button. If it loads succesfully + # an element with the class `xterm` is added to the DOM. + # The xterm is a third-party library, so we can't add a selector + find(".xterm") + end + end + end + + def start_web_terminal + within_element :ide_right_sidebar do + click_element :terminal_tab_button + end + + click_element :start_web_terminal_button + + has_element? :loading_container, text: "Starting" + end + end + end + end + end +end diff --git a/qa/qa/page/project/web_ide/edit.rb b/qa/qa/page/project/web_ide/edit.rb index 78b2db7d723..9c0a3ab691c 100644 --- a/qa/qa/page/project/web_ide/edit.rb +++ b/qa/qa/page/project/web_ide/edit.rb @@ -6,6 +6,7 @@ module QA module WebIDE class Edit < Page::Base prepend Page::Component::WebIDE::Alert + prepend Page::Component::WebIDE::WebTerminalPanel include Page::Component::DropdownFilter view 'app/assets/javascripts/ide/components/activity_bar.vue' do @@ -330,5 +331,3 @@ module QA end end end - -QA::Page::Project::WebIDE::Edit.prepend_mod_with('Page::Component::WebIDE::WebTerminalPanel', namespace: QA) diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/web_terminal_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/web_terminal_spec.rb new file mode 100644 index 00000000000..51791c01048 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/web_terminal_spec.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +module QA + RSpec.describe( + 'Create', + :runner, + quarantine: { + issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/338179', + type: :bug + } + ) do + describe 'Web IDE web terminal' do + before do + project = Resource::Project.fabricate_via_api! do |project| + project.name = 'web-terminal-project' + end + + Resource::Repository::Commit.fabricate_via_api! do |commit| + commit.project = project + commit.commit_message = 'Add .gitlab/.gitlab-webide.yml' + commit.add_files( + [ + { + file_path: '.gitlab/.gitlab-webide.yml', + content: <<~YAML + terminal: + tags: ["web-ide"] + script: sleep 60 + YAML + } + ] + ) + end + + @runner = Resource::Runner.fabricate_via_api! do |runner| + runner.project = project + runner.name = "qa-runner-#{Time.now.to_i}" + runner.tags = %w[web-ide] + runner.image = 'gitlab/gitlab-runner:latest' + runner.config = <<~END + concurrent = 1 + + [session_server] + listen_address = "0.0.0.0:8093" + advertise_address = "localhost:8093" + session_timeout = 120 + END + end + + Flow::Login.sign_in + + project.visit! + end + + after do + @runner.remove_via_api! if @runner + end + + it 'user starts the web terminal', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/quality/test_cases/1593' do + Page::Project::Show.perform(&:open_web_ide!) + + # Start the web terminal and check that there were no errors + # The terminal screen is a canvas element, so we can't read its content, + # so we infer that it's working if: + # a) The terminal JS package has loaded, and + # b) It's not stuck in a "Loading/Starting" state, and + # c) There's no alert stating there was a problem + # + # The terminal itself is a third-party package so we assume it is + # adequately tested elsewhere. + # + # There are also FE specs + # * spec/frontend/ide/components/terminal/terminal_controls_spec.js + Page::Project::WebIDE::Edit.perform do |edit| + edit.start_web_terminal + + expect(edit).to have_no_alert + expect(edit).to have_finished_loading + expect(edit).to have_terminal_screen + end + end + end + end +end diff --git a/scripts/lint-doc.sh b/scripts/lint-doc.sh index 0157f0c0812..a014a1d622c 100755 --- a/scripts/lint-doc.sh +++ b/scripts/lint-doc.sh @@ -81,6 +81,22 @@ then ((ERRORCODE++)) fi +# Do not use dashes (-) in directory names, use underscores (_) instead. +# Number of directories with dashes as of 2021-09-17 +NUMBER_DASHES=2 +FIND_DASHES=$(find doc -type d -name "*-*" | wc -l) +echo '=> Checking for directory names containing dashes...' +echo +if [ ${FIND_DASHES} -ne $NUMBER_DASHES ] +then + echo + echo ' ✖ ERROR: The number of directory names containing dashes has changed. Use underscores instead of dashes for the directory names.' >&2 + echo ' ✖ If removing a directory containing dashes, update NUMBER_DASHES in lint-doc.sh.' >&2 + echo ' https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#work-with-directories-and-files' + echo + ((ERRORCODE++)) +fi + # Run Vale and Markdownlint only on changed files. Only works on merged results # pipelines, so first checks if a merged results CI variable is present. If not present, # runs test on all files. diff --git a/scripts/rubocop-max-files-in-cache-check b/scripts/rubocop-max-files-in-cache-check new file mode 100755 index 00000000000..5b422d0a0f4 --- /dev/null +++ b/scripts/rubocop-max-files-in-cache-check @@ -0,0 +1,27 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require 'yaml' + +MINIMUM_MAX_FILES_IN_CACHE_MARGIN = 1.05 +RECOMMENDED_MAX_FILES_IN_CACHE_MARGIN = 1.25 +RUBOCOP_LIST_TARGET_FILES_COMMAND = 'bundle exec rubocop --list-target-files | wc -l' + +RuboCopMaxFilesInCacheIsTooSmall = Class.new(StandardError) + +rubocop_target_files_count = `#{RUBOCOP_LIST_TARGET_FILES_COMMAND}`.strip.to_i + +raise Error, "#{RUBOCOP_LIST_TARGET_FILES_COMMAND} failed with status #{$?}!" if rubocop_target_files_count == 0 + +rubocop_target_files_count = rubocop_target_files_count.to_i +rubocop_current_max_files_in_cache = YAML.load_file(File.expand_path('../.rubocop.yml', __dir__)).dig('AllCops', 'MaxFilesInCache').to_i +minimum_max_files_in_cache = (rubocop_target_files_count * MINIMUM_MAX_FILES_IN_CACHE_MARGIN).round(-3) + +# We want AllCops.MaxFilesInCache to be at least 5% above the actual files count at any time to give us enough time to increase it accordingly +if rubocop_current_max_files_in_cache <= minimum_max_files_in_cache + recommended_max_files_in_cache = (rubocop_target_files_count * RECOMMENDED_MAX_FILES_IN_CACHE_MARGIN).round(-3) + raise RuboCopMaxFilesInCacheIsTooSmall, "Current count of RuboCop target file is #{rubocop_target_files_count} but AllCops.MaxFilesInCache is set to #{rubocop_current_max_files_in_cache}. We recommend to increase it to #{recommended_max_files_in_cache}." +else + puts "Current count of RuboCop target file is #{rubocop_target_files_count} and AllCops.MaxFilesInCache is set to #{rubocop_current_max_files_in_cache}. All good." + exit(0) +end diff --git a/scripts/static-analysis b/scripts/static-analysis index de5a1b407f9..7911e89b113 100755 --- a/scripts/static-analysis +++ b/scripts/static-analysis @@ -54,6 +54,7 @@ class StaticAnalysis Task.new(%w[bin/rake gettext:lint], 85), Task.new(%W[bundle exec license_finder --decisions-file config/dependency_decisions.yml --project-path #{project_path}], 20), Task.new(%w[bin/rake lint:static_verification], 35), + Task.new(%w[scripts/rubocop-max-files-in-cache-check], 20), Task.new(%w[bin/rake config_lint], 10), Task.new(%w[bin/rake gitlab:sidekiq:all_queues_yml:check], 15), (Gitlab.ee? ? Task.new(%w[bin/rake gitlab:sidekiq:sidekiq_queues_yml:check], 11) : nil), diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb index e055e8092d4..020d80775d2 100644 --- a/spec/features/boards/new_issue_spec.rb +++ b/spec/features/boards/new_issue_spec.rb @@ -56,7 +56,7 @@ RSpec.describe 'Issue Boards new issue', :js do end end - it 'creates new issue' do + it 'creates new issue and opens sidebar' do page.within(first('.board')) do click_button 'New issue' end @@ -78,20 +78,6 @@ RSpec.describe 'Issue Boards new issue', :js do expect(page).to have_content(issue.to_reference) expect(page).to have_link(issue.title, href: /#{issue_path(issue)}/) end - end - - # TODO https://gitlab.com/gitlab-org/gitlab/-/issues/323446 - xit 'shows sidebar when creating new issue' do - page.within(first('.board')) do - click_button 'New issue' - end - - page.within(first('.board-new-issue-form')) do - find('.form-control').set('bug') - click_button 'Create issue' - end - - wait_for_requests expect(page).to have_selector('[data-testid="issue-boards-sidebar"]') end @@ -108,10 +94,6 @@ RSpec.describe 'Issue Boards new issue', :js do wait_for_requests - page.within(first('.board')) do - find('.board-card').click - end - page.within('[data-testid="sidebar-labels"]') do click_button 'Edit' diff --git a/spec/frontend/boards/stores/actions_spec.js b/spec/frontend/boards/stores/actions_spec.js index 62e0fa7a68a..5fc661f2fac 100644 --- a/spec/frontend/boards/stores/actions_spec.js +++ b/spec/frontend/boards/stores/actions_spec.js @@ -1331,20 +1331,54 @@ describe('addListItem', () => { list: mockLists[0], item: mockIssue, position: 0, + inProgress: true, }; - testAction(actions.addListItem, payload, {}, [ - { - type: types.ADD_BOARD_ITEM_TO_LIST, - payload: { - listId: mockLists[0].id, - itemId: mockIssue.id, - atIndex: 0, - inProgress: false, + testAction( + actions.addListItem, + payload, + {}, + [ + { + type: types.ADD_BOARD_ITEM_TO_LIST, + payload: { + listId: mockLists[0].id, + itemId: mockIssue.id, + atIndex: 0, + inProgress: true, + }, }, - }, - { type: types.UPDATE_BOARD_ITEM, payload: mockIssue }, - ]); + { type: types.UPDATE_BOARD_ITEM, payload: mockIssue }, + ], + [], + ); + }); + + it('should commit ADD_BOARD_ITEM_TO_LIST and UPDATE_BOARD_ITEM mutations, dispatch setActiveId action when inProgress is false', () => { + const payload = { + list: mockLists[0], + item: mockIssue, + position: 0, + }; + + testAction( + actions.addListItem, + payload, + {}, + [ + { + type: types.ADD_BOARD_ITEM_TO_LIST, + payload: { + listId: mockLists[0].id, + itemId: mockIssue.id, + atIndex: 0, + inProgress: false, + }, + }, + { type: types.UPDATE_BOARD_ITEM, payload: mockIssue }, + ], + [{ type: 'setActiveId', payload: { id: mockIssue.id, sidebarType: ISSUABLE } }], + ); }); }); diff --git a/spec/graphql/resolvers/board_list_issues_resolver_spec.rb b/spec/graphql/resolvers/board_list_issues_resolver_spec.rb index 26040f4ec1a..98acd9e9516 100644 --- a/spec/graphql/resolvers/board_list_issues_resolver_spec.rb +++ b/spec/graphql/resolvers/board_list_issues_resolver_spec.rb @@ -57,6 +57,13 @@ RSpec.describe Resolvers::BoardListIssuesResolver do expect(result).to match_array([issue1]) end + it 'filters issues by negated issue type' do + incident = create(:incident, project: project, labels: [label], relative_position: 15) + result = resolve_board_list_issues(args: { filters: { not: { types: ['issue'] } } }) + + expect(result).to contain_exactly(incident) + end + it 'raises an exception if both assignee_username and assignee_wildcard_id are present' do expect do resolve_board_list_issues(args: { filters: { assignee_username: ['username'], assignee_wildcard_id: 'NONE' } }) diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb index 6dd3c40f228..7dabc4af645 100644 --- a/spec/models/ci/bridge_spec.rb +++ b/spec/models/ci/bridge_spec.rb @@ -17,6 +17,8 @@ RSpec.describe Ci::Bridge do { trigger: { project: 'my/project', branch: 'master' } } end + it { is_expected.to respond_to(:runner_features) } + it 'has many sourced pipelines' do expect(bridge).to have_many(:sourced_pipelines) end diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 1e06d566c80..436af1d59e9 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -34,6 +34,7 @@ RSpec.describe Ci::Build do it { is_expected.to respond_to(:has_trace?) } it { is_expected.to respond_to(:trace) } + it { is_expected.to respond_to(:runner_features) } it { is_expected.to delegate_method(:merge_request?).to(:pipeline) } it { is_expected.to delegate_method(:merge_request_ref?).to(:pipeline) } diff --git a/spec/services/boards/issues/list_service_spec.rb b/spec/services/boards/issues/list_service_spec.rb index d1f854f72bc..72027911e51 100644 --- a/spec/services/boards/issues/list_service_spec.rb +++ b/spec/services/boards/issues/list_service_spec.rb @@ -56,12 +56,23 @@ RSpec.describe Boards::Issues::ListService do it_behaves_like 'issues list service' end - context 'when filtering by type' do - it 'only returns the specified type' do - issue = create(:labeled_issue, project: project, milestone: m1, labels: [development, p1], issue_type: 'incident') - params = { board_id: board.id, id: list1.id, issue_types: 'incident' } + context 'when filtering' do + let_it_be(:incident) { create(:labeled_issue, project: project, milestone: m1, labels: [development, p1], issue_type: 'incident') } - expect(described_class.new(parent, user, params).execute).to eq [issue] + context 'when filtering by type' do + it 'only returns the specified type' do + params = { board_id: board.id, id: list1.id, issue_types: 'incident' } + + expect(described_class.new(parent, user, params).execute).to eq [incident] + end + end + + context 'when filtering by negated type' do + it 'only returns the specified type' do + params = { board_id: board.id, id: list1.id, not: { issue_types: ['issue'] } } + + expect(described_class.new(parent, user, params).execute).to contain_exactly(incident) + end end end end |