diff options
25 files changed, 287 insertions, 160 deletions
diff --git a/.rubocop.yml b/.rubocop.yml index 99fa10d4b44..99424713bd8 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -46,6 +46,10 @@ AllCops: CacheRootDirectory: <%= Dir.getwd %>/tmp MaxFilesInCache: 30000 +Metrics/ParameterLists: + Exclude: + - 'app/components/**/*' + Cop/AvoidKeywordArgumentsInSidekiqWorkers: Enabled: true Include: @@ -11,6 +11,8 @@ gem 'responders', '~> 3.0' gem 'sprockets', '~> 3.7.0' +gem 'view_component', '~> 2.50.0' + # Default values for AR models gem 'default_value_for', '~> 3.4.0' diff --git a/Gemfile.lock b/Gemfile.lock index 00d16ddbfd5..f17b8dcc43d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -645,7 +645,7 @@ GEM mime-types (~> 3.0) multi_xml (>= 0.5.2) httpclient (2.8.3) - i18n (1.9.1) + i18n (1.10.0) concurrent-ruby (~> 1.0) i18n_data (0.8.0) icalendar (2.4.1) @@ -1362,6 +1362,9 @@ GEM activerecord (>= 3.0) activesupport (>= 3.0) version_sorter (2.2.4) + view_component (2.50.0) + activesupport (>= 5.0.0, < 8.0) + method_source (~> 1.0) vmstat (2.3.0) warden (1.2.8) rack (>= 2.0.6) @@ -1682,6 +1685,7 @@ DEPENDENCIES valid_email (~> 0.1) validates_hostname (~> 1.0.11) version_sorter (~> 2.2.4) + view_component (~> 2.50.0) vmstat (~> 2.3.0) warning (~> 1.2.0) webauthn (~> 2.3) diff --git a/app/components/pajamas/component.rb b/app/components/pajamas/component.rb new file mode 100644 index 00000000000..b05d93b680e --- /dev/null +++ b/app/components/pajamas/component.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Pajamas + class Component < ViewComponent::Base + private + + # :nocov: + + # Filter a given a value against a list of allowed values + # If no value is given or value is not allowed return default one + # + # @param [Object] value + # @param [Enumerable] allowed_values + # @param [Object] default + def filter_attribute(value, allowed_values, default: nil) + return default unless value + return value if allowed_values.include?(value) + + default + end + # :nocov: + end +end diff --git a/app/components/pajamas/toggle_component.html.haml b/app/components/pajamas/toggle_component.html.haml new file mode 100644 index 00000000000..1716e8b69c3 --- /dev/null +++ b/app/components/pajamas/toggle_component.html.haml @@ -0,0 +1,16 @@ +%span{ class: @classes, + data: { name: @name, + id: @id, + is_checked: @is_checked.to_s, + disabled: @is_disabled.to_s, + is_loading: @is_loading.to_s, + label: @label, + help: @help, + label_position: @label_position, + **@data } } + +-# Leverage this block to render a rich help text. To render a plain text help text, +-# prefer the `help` parameter. +- if content.present? + .gl-text-secondary.gl-mt-1 + = content diff --git a/app/components/pajamas/toggle_component.rb b/app/components/pajamas/toggle_component.rb new file mode 100644 index 00000000000..2d99f3d3b69 --- /dev/null +++ b/app/components/pajamas/toggle_component.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +# Renders a GlToggle root element +# To actually initialize the component, make sure to call the initToggle helper from ~/toggles. +class Pajamas::ToggleComponent < Pajamas::Component + LABEL_POSITION_OPTIONS = [:top, :left, :hidden].freeze + + # @param [String] classes + # @param [String] label + # @param [Symbol] label_position :top, :left or :hidden + # @param [String] id + # @param [String] name + # @param [String] help + # @param [Hash] data + # @param [Boolean] is_disabled + # @param [Boolean] is_checked + # @param [Boolean] is_loading + def initialize( + classes:, label: nil, label_position: nil, + id: nil, name: nil, help: nil, data: {}, + is_disabled: false, is_checked: false, is_loading: false) + + @id = id + @name = name + @classes = classes + @label = label + @label_position = filter_attribute(label_position, LABEL_POSITION_OPTIONS) + @help = help + @data = data + @is_disabled = is_disabled + @is_checked = is_checked + @is_loading = is_loading + end +end diff --git a/app/views/projects/pages_domains/_certificate.html.haml b/app/views/projects/pages_domains/_certificate.html.haml index 91683c1b316..861305dc93b 100644 --- a/app/views/projects/pages_domains/_certificate.html.haml +++ b/app/views/projects/pages_domains/_certificate.html.haml @@ -14,12 +14,11 @@ - lets_encrypt_link_start = "<a href=\"%{lets_encrypt_link_url}\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-nowrap\">".html_safe % { lets_encrypt_link_url: lets_encrypt_link_url } - lets_encrypt_link_end = "</a>".html_safe = _("Automatic certificate management using %{lets_encrypt_link_start}Let's Encrypt%{lets_encrypt_link_end}").html_safe % { lets_encrypt_link_start: lets_encrypt_link_start, lets_encrypt_link_end: lets_encrypt_link_end } - = render "shared/gl_toggle", - id: "pages_domain_auto_ssl_enabled_button", - is_checked: auto_ssl_available_and_enabled, - classes: "js-project-feature-toggle js-enable-ssl-gl-toggle mt-2", - label: _("Automatic certificate management using Let's Encrypt"), - label_position: 'hidden' + = render Pajamas::ToggleComponent.new(id: 'pages_domain_auto_ssl_enabled_button', + classes: 'js-project-feature-toggle js-enable-ssl-gl-toggle mt-2', + is_checked: auto_ssl_available_and_enabled, + label: _("Automatic certificate management using Let's Encrypt"), + label_position: :hidden) = f.hidden_field :auto_ssl_enabled?, class: "js-project-feature-toggle-input" %p.gl-text-secondary.gl-mt-1 - docs_link_url = help_page_path("user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md") diff --git a/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml b/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml index 809fb01ae6a..e5810930be2 100644 --- a/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml +++ b/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml @@ -24,10 +24,9 @@ .form-group.row = f.label :allow_force_push, s_("ProtectedBranch|Allowed to force push:"), class: 'col-md-2 gl-text-left text-md-right' .col-md-10 - = render "shared/gl_toggle", - classes: 'js-force-push-toggle', + = render Pajamas::ToggleComponent.new(classes: 'js-force-push-toggle', label: s_("ProtectedBranch|Allowed to force push"), - label_position: 'hidden' do + label_position: :hidden) do - force_push_docs_url = help_page_url('topics/git/git_rebase', anchor: 'force-push') - force_push_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: force_push_docs_url } = (s_("ProtectedBranch|Allow all users with push access to %{tag_start}force push%{tag_end}.") % { tag_start: force_push_link_start, tag_end: '</a>' }).html_safe diff --git a/app/views/shared/_gl_toggle.html.haml b/app/views/shared/_gl_toggle.html.haml deleted file mode 100644 index 59a3c7ba0b5..00000000000 --- a/app/views/shared/_gl_toggle.html.haml +++ /dev/null @@ -1,30 +0,0 @@ --# This partial renders a GlToggle root element. --# To actually initialize the component, make sure to call the initToggle helper from ~/toggles. - -- classes = local_assigns.fetch(:classes) -- name = local_assigns.fetch(:name, nil) -- id = local_assigns.fetch(:id, nil) -- is_checked = local_assigns.fetch(:is_checked, false).to_s -- disabled = local_assigns.fetch(:disabled, false).to_s -- is_loading = local_assigns.fetch(:is_loading, false).to_s -- label = local_assigns.fetch(:label, nil) -- help = local_assigns.fetch(:help, nil) -- label_position = local_assigns.fetch(:label_position, nil) -- data = local_assigns.fetch(:data, {}) - -%span{ class: classes, - data: { name: name, - id: id, - is_checked: is_checked, - disabled: disabled, - is_loading: is_loading, - label: label, - help: help, - label_position: label_position, - **data } } - --# Leverage this block to render a rich help text. To render a plain text help text, --# prefer the `help` parameter. -- if yield.present? - .gl-text-secondary.gl-mt-1 - = yield diff --git a/app/views/shared/projects/protected_branches/_update_protected_branch.html.haml b/app/views/shared/projects/protected_branches/_update_protected_branch.html.haml index f3544b55b63..32b9044c551 100644 --- a/app/views/shared/projects/protected_branches/_update_protected_branch.html.haml +++ b/app/views/shared/projects/protected_branches/_update_protected_branch.html.haml @@ -34,8 +34,7 @@ = _('Members of %{group} can also push to this branch: %{branch}') % { group: (group_push_access_levels.size > 1 ? 'these groups' : 'this group'), branch: group_push_access_levels.map(&:humanize).to_sentence } %td - = render "shared/gl_toggle", - classes: 'js-force-push-toggle', + = render Pajamas::ToggleComponent.new(classes: 'js-force-push-toggle', label: s_("ProtectedBranch|Toggle allowed to force push"), is_checked: protected_branch.allow_force_push, - label_position: 'hidden' + label_position: :hidden) diff --git a/config/application.rb b/config/application.rb index e66f64ce791..76541daa9e1 100644 --- a/config/application.rb +++ b/config/application.rb @@ -18,6 +18,8 @@ module Gitlab class Application < Rails::Application config.load_defaults 6.1 + config.view_component.preview_route = "/-/view_component/previews" + # This section contains configuration from Rails upgrades to override the new defaults so that we # keep existing behavior. # diff --git a/config/feature_flags/development/secure_vulnerability_training.yml b/config/feature_flags/development/secure_vulnerability_training.yml index 58117482e25..7dbc25f1ebf 100644 --- a/config/feature_flags/development/secure_vulnerability_training.yml +++ b/config/feature_flags/development/secure_vulnerability_training.yml @@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/346308 milestone: '14.6' type: development group: group::threat insights -default_enabled: false +default_enabled: true diff --git a/config/feature_flags/ops/purge_stale_security_findings.yml b/config/feature_flags/ops/purge_stale_security_findings.yml new file mode 100644 index 00000000000..322f31b62ce --- /dev/null +++ b/config/feature_flags/ops/purge_stale_security_findings.yml @@ -0,0 +1,8 @@ +--- +name: purge_stale_security_findings +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81423 +rollout_issue_url: +milestone: '14.9' +type: ops +group: group::threat insights +default_enabled: false diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index be7f408601d..844aff1b060 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -745,6 +745,9 @@ Gitlab.ee do Settings.cron_jobs['security_orchestration_policy_rule_schedule_worker'] ||= Settingslogic.new({}) Settings.cron_jobs['security_orchestration_policy_rule_schedule_worker']['cron'] ||= '*/15 * * * *' Settings.cron_jobs['security_orchestration_policy_rule_schedule_worker']['job_class'] = 'Security::OrchestrationPolicyRuleScheduleWorker' + Settings.cron_jobs['security_findings_cleanup_worker'] ||= Settingslogic.new({}) + Settings.cron_jobs['security_findings_cleanup_worker']['cron'] ||= '0 */4 * * 6,0' + Settings.cron_jobs['security_findings_cleanup_worker']['job_class'] = 'Security::Findings::CleanupWorker' Settings.cron_jobs['app_sec_dast_profile_schedule_worker'] ||= Settingslogic.new({}) Settings.cron_jobs['app_sec_dast_profile_schedule_worker']['cron'] ||= '7-59/15 * * * *' Settings.cron_jobs['app_sec_dast_profile_schedule_worker']['job_class'] = 'AppSec::Dast::ProfileScheduleWorker' diff --git a/doc/administration/gitaly/praefect.md b/doc/administration/gitaly/praefect.md index ea9e420e556..d7a06994b6c 100644 --- a/doc/administration/gitaly/praefect.md +++ b/doc/administration/gitaly/praefect.md @@ -1196,6 +1196,9 @@ You can configure: current assignments: gitaly-1, gitaly-2 ``` +If `default_replication_factor` is unset, the repositories are always replicated on every node defined in `virtual_storages`. If a new +node is introduced to the virtual storage, both new and existing repositories are replicated to the node automatically. + ## Automatic failover and primary election strategies Praefect regularly checks the health of each Gitaly node. This is used to automatically fail over diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index dab2609bfbd..494f43fa4c2 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -11024,7 +11024,7 @@ four standard [pagination arguments](#connection-pagination-arguments): ##### `GeoNode.jobArtifactRegistries` -Find Job Artifact registries on this Geo node Available only when feature flag `geo_job_artifact_replication` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice. +Find Job Artifact registries on this Geo node. Returns [`JobArtifactRegistryConnection`](#jobartifactregistryconnection). diff --git a/doc/user/group/saml_sso/scim_setup.md b/doc/user/group/saml_sso/scim_setup.md index 4a1aee0c061..331288e33a1 100644 --- a/doc/user/group/saml_sso/scim_setup.md +++ b/doc/user/group/saml_sso/scim_setup.md @@ -100,12 +100,14 @@ Once synchronized, changing the field mapped to `id` and `externalId` may cause ### Okta configuration steps -Before you start this section, complete the [GitLab configuration](#gitlab-configuration) process. -Make sure that you've also set up a SAML application for [Okta](https://developer.okta.com/docs/guides/build-sso-integration/saml2/main/), -as described in the [Okta setup notes](index.md#okta-setup-notes) +Before you start this section: -Make sure that the Okta setup matches our documentation exactly, especially the NameID -configuration. Otherwise, the Okta SCIM app may not work properly. +- Check that you are using Okta [Lifecycle Management](https://www.okta.com/products/lifecycle-management/) product. This product tier is required to use SCIM on Okta. To check which Okta product you are using, check your signed Okta contract, contact your Okta AE, CSM, or Okta support. +- Complete the [GitLab configuration](#gitlab-configuration) process. +- Complete the setup for SAML application for [Okta](https://developer.okta.com/docs/guides/build-sso-integration/saml2/overview/), as described in the [Okta setup notes](index.md#okta-setup-notes). +- Check that your Okta SAML setup matches our documentation exactly, especially the NameID configuration. Otherwise, the Okta SCIM app may not work properly. + +After the above steps are complete: 1. Sign in to Okta. 1. Ensure you are in the Admin section by selecting the **Admin** button located in the top right. The admin button is not visible from the admin page. diff --git a/doc/user/project/working_with_projects.md b/doc/user/project/working_with_projects.md index dbd0da54cfb..19e77d18aca 100644 --- a/doc/user/project/working_with_projects.md +++ b/doc/user/project/working_with_projects.md @@ -68,7 +68,7 @@ If you're an instance administrator, you can administer all project topics from To create a project in GitLab: -1. On the top bar, select **Menu > Create new project**. +1. On the top bar, select **Menu > Project > Create new project**. 1. On the **Create new project** page, choose if you want to: - Create a [blank project](#create-a-blank-project). - Create a project from a: @@ -79,20 +79,20 @@ To create a project in GitLab: from a different repository. Contact your GitLab administrator if this option is not available. - [Connect an external repository to GitLab CI/CD](../../ci/ci_cd_for_external_repos/index.md). -NOTE: -For a list of words that can't be used as project names see +- For a list of words that you cannot use as project names, see [reserved project and group names](../../user/reserved_names.md). +- For a list of characters that you cannot use in project and group names, see +[limitations on project and group names](../../user/reserved_names.md#limitations-on-project-and-group-names). ## Create a blank project To create a blank project: -1. On the top bar, select **Menu > Create new project**. +1. On the top bar, select **Menu > Projects > Create new project**. 1. Select **Create blank project**. 1. Enter the project details: - - In the **Project name** field, enter the name of your project. You can use spaces, hyphens, - underscores, and emoji. You cannot use special characters. After you enter the name, - the **Project slug** populates. + - In the **Project name** field, enter the name of your project. You cannot use special characters at + the start or end of a project name. - In the **Project slug** field, enter the path to your project. The GitLab instance uses the slug as the URL path to the project. To change the slug, first enter the project name, then change the slug. @@ -119,16 +119,15 @@ Anyone can contribute a built-in template by following [these steps](https://abo To create a project from a built-in template: -1. On the top bar, select **Menu > Create new project**. +1. On the top bar, select **Menu > Projects > Create new project**. 1. Select **Create from template**. 1. Select the **Built-in** tab. 1. From the list of templates: - To view a preview of the template, select **Preview**. - To use a template for the project, select **Use template**. 1. Enter the project details: - - In the **Project name** field, enter the name of your project. You can use spaces, hyphens, - underscores, and emoji. You cannot use special characters. After you enter the name, - the **Project slug** populates. + - In the **Project name** field, enter the name of your project. You cannot use special characters at + the start or end of a project name. - In the **Project slug** field, enter the path to your project. The GitLab instance uses the slug as the URL path to the project. To change the slug, first enter the project name, then change the slug. @@ -146,16 +145,15 @@ Custom project templates are available at: - The [instance-level](../../user/admin_area/custom_project_templates.md) - The [group-level](../../user/group/custom_project_templates.md) -1. On the top bar, select **Menu > Create new project**. +1. On the top bar, select **Menu > Projects > Create new project**. 1. Select **Create from template**. 1. Select the **Instance** or **Group** tab. 1. From the list of templates: - To view a preview of the template, select **Preview**. - To use a template for the project, select **Use template**. 1. Enter the project details: - - In the **Project name** field, enter the name of your project. You can use spaces, hyphens, - underscores, and emoji. You cannot use special characters. After you enter the name, - the **Project slug** populates. + - In the **Project name** field, enter the name of your project. You cannot use special characters at + the start or end of a project name. - In the **Project slug** field, enter the path to your project. The GitLab instance uses the slug as the URL path to the project. To change the slug, first enter the project name, then change the slug. @@ -173,16 +171,15 @@ HIPAA Audit Protocol published by the U.S Department of Health and Human Service To create a project from the HIPAA Audit Protocol template: -1. On the top bar, select **Menu > Create new project**. +1. On the top bar, select **Menu > Projects > Create new project**. 1. Select **Create from template**. 1. Select the **Built-in** tab. 1. Locate the **HIPAA Audit Protocol** template: - To view a preview of the template, select **Preview**. - To use the template for the project, select **Use template**. 1. Enter the project details: - - In the **Project name** field, enter the name of your project. You can use spaces, hyphens, - underscores, and emoji. You cannot use special characters. After you enter the name, - the **Project slug** populates. + - In the **Project name** field, enter the name of your project. You cannot use special characters at + the start or end of a project name. - In the **Project slug** field, enter the path to your project. The GitLab instance uses the slug as the URL path to the project. To change the slug, first enter the project name, then change the slug. @@ -476,3 +473,4 @@ download starts, the `insteadOf` configuration sends the traffic to the secondar - [Connect an external repository to GitLab CI/CD](../../ci/ci_cd_for_external_repos/index.md). - [Fork a project](repository/forking_workflow.md#creating-a-fork). - [Adjust project visibility and access levels](settings/index.md#sharing-and-permissions). +- [Limitations on project and group names](../../user/reserved_names.md#limitations-on-project-and-group-names) diff --git a/spec/components/pajamas/component_spec.rb b/spec/components/pajamas/component_spec.rb new file mode 100644 index 00000000000..96f6b43bac1 --- /dev/null +++ b/spec/components/pajamas/component_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe Pajamas::Component do + describe '#filter_attribute' do + let(:allowed) { %w[default something] } + + it 'returns default value when no value is given' do + value = subject.send(:filter_attribute, nil, allowed, default: 'default') + + expect(value).to eq('default') + end + + it 'returns default value when invalid value is given' do + value = subject.send(:filter_attribute, 'invalid', allowed, default: 'default') + + expect(value).to eq('default') + end + + it 'returns given value when it is part of allowed list' do + value = subject.send(:filter_attribute, 'something', allowed, default: 'default') + + expect(value).to eq('something') + end + end +end diff --git a/spec/components/pajamas/toggle_component_spec.rb b/spec/components/pajamas/toggle_component_spec.rb new file mode 100644 index 00000000000..b2727dec318 --- /dev/null +++ b/spec/components/pajamas/toggle_component_spec.rb @@ -0,0 +1,107 @@ +# frozen_string_literal: true +require "spec_helper" + +RSpec.describe Pajamas::ToggleComponent, type: :component do + context 'with defaults' do + before do + render_inline described_class.new(classes: 'js-feature-toggle') + end + + it 'renders a toggle container with provided class' do + expect(rendered_component).to have_selector "[class='js-feature-toggle']" + end + + it 'does not set a name' do + expect(rendered_component).not_to have_selector('[data-name]') + end + + it 'sets default is-checked attributes' do + expect(rendered_component).to have_selector('[data-is-checked="false"]') + end + + it 'sets default disabled attributes' do + expect(rendered_component).to have_selector('[data-disabled="false"]') + end + + it 'sets default is-loading attributes' do + expect(rendered_component).to have_selector('[data-is-loading="false"]') + end + + it 'does not set a label' do + expect(rendered_component).not_to have_selector('[data-label]') + end + + it 'does not set a label position' do + expect(rendered_component).not_to have_selector('[data-label-position]') + end + end + + context 'with custom options' do + before do + render_inline described_class.new( + classes: 'js-custom-gl-toggle', + name: 'toggle-name', + is_checked: true, + is_disabled: true, + is_loading: true, + label: 'Custom label', + label_position: :top, + data: { + foo: 'bar' + }) + end + + it 'sets the custom class' do + expect(rendered_component).to have_selector('.js-custom-gl-toggle') + end + + it 'sets the custom name' do + expect(rendered_component).to have_selector('[data-name="toggle-name"]') + end + + it 'sets the custom is-checked attributes' do + expect(rendered_component).to have_selector('[data-is-checked="true"]') + end + + it 'sets the custom disabled attributes' do + expect(rendered_component).to have_selector('[data-disabled="true"]') + end + + it 'sets the custom is-loading attributes' do + expect(rendered_component).to have_selector('[data-is-loading="true"]') + end + + it 'sets the custom label' do + expect(rendered_component).to have_selector('[data-label="Custom label"]') + end + + it 'sets the custom label position' do + expect(rendered_component).to have_selector('[data-label-position="top"]') + end + + it 'sets custom data attributes' do + expect(rendered_component).to have_selector('[data-foo="bar"]') + end + end + + context 'with setting label_position' do + using RSpec::Parameterized::TableSyntax + + where(:position, :count) do + :top | 1 + :left | 1 + :hidden | 1 + :bogus | 0 + 'bogus' | 0 + nil | 0 + end + + before do + render_inline described_class.new(classes: '_class_', label_position: position) + end + + with_them do + it { expect(rendered_component).to have_selector("[data-label-position='#{position}']", count: count) } + end + end +end diff --git a/spec/frontend/sidebar/components/incidents/sidebar_escalation_status_spec.js b/spec/frontend/sidebar/components/incidents/sidebar_escalation_status_spec.js index 6173088b247..a8dc610672c 100644 --- a/spec/frontend/sidebar/components/incidents/sidebar_escalation_status_spec.js +++ b/spec/frontend/sidebar/components/incidents/sidebar_escalation_status_spec.js @@ -153,7 +153,7 @@ describe('SidebarEscalationStatus', () => { await selectAcknowledgedStatus(); }); - it('calls the mutation', async () => { + it('calls the mutation', () => { const mutationVariables = { iid: '1', projectPath: 'gitlab-org/gitlab', @@ -163,12 +163,17 @@ describe('SidebarEscalationStatus', () => { expect(mutationResolverMock).toHaveBeenCalledWith(mutationVariables); }); - it('closes the dropdown', async () => { + it('closes the dropdown', () => { expect(findStatusComponent().isVisible()).toBe(false); }); - it('updates the status', async () => { - expect(findStatusComponent().attributes('value')).toBe(STATUS_ACKNOWLEDGED); + it('updates the status', () => { + // Sometimes status has a intermediate wrapping component. A quirk of vue-test-utils + // means that in that case 'value' is exposed as a prop. If no wrapping component + // exists it is exposed as an attribute. + const statusValue = + findStatusComponent().props('value') || findStatusComponent().attributes('value'); + expect(statusValue).toBe(STATUS_ACKNOWLEDGED); }); }); diff --git a/spec/support/view_component.rb b/spec/support/view_component.rb new file mode 100644 index 00000000000..9166a06fc8c --- /dev/null +++ b/spec/support/view_component.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true +require 'view_component/test_helpers' + +RSpec.configure do |config| + config.include ViewComponent::TestHelpers, type: :component + config.include Capybara::RSpecMatchers, type: :component +end diff --git a/spec/tooling/quality/test_level_spec.rb b/spec/tooling/quality/test_level_spec.rb index 33d3a5b49b3..c72e90dc713 100644 --- a/spec/tooling/quality/test_level_spec.rb +++ b/spec/tooling/quality/test_level_spec.rb @@ -28,7 +28,7 @@ RSpec.describe Quality::TestLevel do context 'when level is unit' do it 'returns a pattern' do expect(subject.pattern(:unit)) - .to eq("spec/{bin,channels,config,db,dependencies,elastic,elastic_integration,experiments,events,factories,finders,frontend,graphql,haml_lint,helpers,initializers,javascripts,lib,metrics_server,models,policies,presenters,rack_servers,replicators,routing,rubocop,scripts,serializers,services,sidekiq,sidekiq_cluster,spam,support_specs,tasks,uploaders,validators,views,workers,tooling}{,/**/}*_spec.rb") + .to eq("spec/{bin,channels,config,db,dependencies,elastic,elastic_integration,experiments,events,factories,finders,frontend,graphql,haml_lint,helpers,initializers,javascripts,lib,metrics_server,models,policies,presenters,rack_servers,replicators,routing,rubocop,scripts,serializers,services,sidekiq,sidekiq_cluster,spam,support_specs,tasks,uploaders,validators,views,workers,tooling,component}{,/**/}*_spec.rb") end end @@ -110,7 +110,7 @@ RSpec.describe Quality::TestLevel do context 'when level is unit' do it 'returns a regexp' do expect(subject.regexp(:unit)) - .to eq(%r{spec/(bin|channels|config|db|dependencies|elastic|elastic_integration|experiments|events|factories|finders|frontend|graphql|haml_lint|helpers|initializers|javascripts|lib|metrics_server|models|policies|presenters|rack_servers|replicators|routing|rubocop|scripts|serializers|services|sidekiq|sidekiq_cluster|spam|support_specs|tasks|uploaders|validators|views|workers|tooling)}) + .to eq(%r{spec/(bin|channels|config|db|dependencies|elastic|elastic_integration|experiments|events|factories|finders|frontend|graphql|haml_lint|helpers|initializers|javascripts|lib|metrics_server|models|policies|presenters|rack_servers|replicators|routing|rubocop|scripts|serializers|services|sidekiq|sidekiq_cluster|spam|support_specs|tasks|uploaders|validators|views|workers|tooling|component)}) end end diff --git a/spec/views/shared/_gl_toggle.haml_spec.rb b/spec/views/shared/_gl_toggle.haml_spec.rb deleted file mode 100644 index 3ac1ef30c84..00000000000 --- a/spec/views/shared/_gl_toggle.haml_spec.rb +++ /dev/null @@ -1,85 +0,0 @@ -# frozen_string_literal: true -require 'spec_helper' - -RSpec.describe 'shared/_gl_toggle.html.haml' do - context 'defaults' do - before do - render partial: 'shared/gl_toggle', locals: { - classes: '.js-gl-toggle' - } - end - - it 'does not set a name' do - expect(rendered).not_to have_selector('[data-name]') - end - - it 'sets default is-checked attributes' do - expect(rendered).to have_selector('[data-is-checked="false"]') - end - - it 'sets default disabled attributes' do - expect(rendered).to have_selector('[data-disabled="false"]') - end - - it 'sets default is-loading attributes' do - expect(rendered).to have_selector('[data-is-loading="false"]') - end - - it 'does not set a label' do - expect(rendered).not_to have_selector('[data-label]') - end - - it 'does not set a label position' do - expect(rendered).not_to have_selector('[data-label-position]') - end - end - - context 'with custom options' do - before do - render partial: 'shared/gl_toggle', locals: { - classes: 'js-custom-gl-toggle', - name: 'toggle-name', - is_checked: true, - disabled: true, - is_loading: true, - label: 'Custom label', - label_position: 'top', - data: { - foo: 'bar' - } - } - end - - it 'sets the custom class' do - expect(rendered).to have_selector('.js-custom-gl-toggle') - end - - it 'sets the custom name' do - expect(rendered).to have_selector('[data-name="toggle-name"]') - end - - it 'sets the custom is-checked attributes' do - expect(rendered).to have_selector('[data-is-checked="true"]') - end - - it 'sets the custom disabled attributes' do - expect(rendered).to have_selector('[data-disabled="true"]') - end - - it 'sets the custom is-loading attributes' do - expect(rendered).to have_selector('[data-is-loading="true"]') - end - - it 'sets the custom label' do - expect(rendered).to have_selector('[data-label="Custom label"]') - end - - it 'sets the cutom label position' do - expect(rendered).to have_selector('[data-label-position="top"]') - end - - it 'sets cutom data attributes' do - expect(rendered).to have_selector('[data-foo="bar"]') - end - end -end diff --git a/tooling/quality/test_level.rb b/tooling/quality/test_level.rb index 624564ecd05..fd86708bb7d 100644 --- a/tooling/quality/test_level.rb +++ b/tooling/quality/test_level.rb @@ -55,6 +55,7 @@ module Quality views workers tooling + component ], integration: %w[ commands |