diff options
15 files changed, 340 insertions, 115 deletions
diff --git a/app/assets/javascripts/pages/projects/settings/repository/show/index.js b/app/assets/javascripts/pages/projects/settings/repository/show/index.js index 655243eee30..d2263fa815d 100644 --- a/app/assets/javascripts/pages/projects/settings/repository/show/index.js +++ b/app/assets/javascripts/pages/projects/settings/repository/show/index.js @@ -1,5 +1,7 @@ import MirrorRepos from '~/mirrors/mirror_repos'; import mountBranchRules from '~/projects/settings/repository/branch_rules/mount_branch_rules'; +import mountDefaultBranchSelector from '~/projects/settings/mount_default_branch_selector'; + import initForm from '../form'; initForm(); @@ -8,3 +10,4 @@ const mirrorReposContainer = document.querySelector('.js-mirror-settings'); if (mirrorReposContainer) new MirrorRepos(mirrorReposContainer).init(); mountBranchRules(document.getElementById('js-branch-rules')); +mountDefaultBranchSelector(document.querySelector('.js-select-default-branch')); diff --git a/app/assets/javascripts/projects/settings/components/default_branch_selector.vue b/app/assets/javascripts/projects/settings/components/default_branch_selector.vue new file mode 100644 index 00000000000..fee2f591216 --- /dev/null +++ b/app/assets/javascripts/projects/settings/components/default_branch_selector.vue @@ -0,0 +1,38 @@ +<script> +import RefSelector from '~/ref/components/ref_selector.vue'; +import { REF_TYPE_BRANCHES } from '~/ref/constants'; +import { __ } from '~/locale'; + +export default { + components: { + RefSelector, + }, + props: { + persistedDefaultBranch: { + type: String, + required: true, + }, + projectId: { + type: String, + required: true, + }, + }, + refTypes: [REF_TYPE_BRANCHES], + i18n: { + dropdownHeader: __('Select default branch'), + searchPlaceholder: __('Search branch'), + }, +}; +</script> +<template> + <ref-selector + :value="persistedDefaultBranch" + class="gl-w-full" + :project-id="projectId" + :enabled-ref-types="$options.refTypes" + :translations="$options.i18n" + name="project[default_branch]" + data-testid="default-branch-dropdown" + data-qa-selector="default_branch_dropdown" + /> +</template> diff --git a/app/assets/javascripts/projects/settings/mount_default_branch_selector.js b/app/assets/javascripts/projects/settings/mount_default_branch_selector.js new file mode 100644 index 00000000000..611561e38f2 --- /dev/null +++ b/app/assets/javascripts/projects/settings/mount_default_branch_selector.js @@ -0,0 +1,22 @@ +import Vue from 'vue'; +import DefaultBranchSelector from './components/default_branch_selector.vue'; + +export default (el) => { + if (!el) { + return null; + } + + const { projectId, defaultBranch } = el.dataset; + + return new Vue({ + el, + render(createElement) { + return createElement(DefaultBranchSelector, { + props: { + persistedDefaultBranch: defaultBranch, + projectId, + }, + }); + }, + }); +}; diff --git a/app/assets/javascripts/ref/components/ref_selector.vue b/app/assets/javascripts/ref/components/ref_selector.vue index 1343ad8246c..b75958e2ced 100644 --- a/app/assets/javascripts/ref/components/ref_selector.vue +++ b/app/assets/javascripts/ref/components/ref_selector.vue @@ -29,6 +29,7 @@ export default { GlLoadingIcon, RefResultsSection, }, + inheritAttrs: false, props: { enabledRefTypes: { type: Array, @@ -70,6 +71,15 @@ export default { required: false, default: true, }, + + /* Underlying form field name for scenarios where ref_selector + * is used as part of submitting an HTML form + */ + name: { + type: String, + required: false, + default: '', + }, }, data() { return { @@ -213,89 +223,103 @@ export default { </script> <template> - <gl-dropdown - :header-text="i18n.dropdownHeader" - :toggle-class="toggleButtonClass" - :text="buttonText" - class="ref-selector" - v-bind="$attrs" - v-on="$listeners" - @shown="focusSearchBox" - > - <template #header> - <gl-search-box-by-type - ref="searchBox" - v-model.trim="query" - :placeholder="i18n.searchPlaceholder" - autocomplete="off" - @input="onSearchBoxInput" - @keydown.enter.prevent="onSearchBoxEnter" - /> - </template> + <div> + <gl-dropdown + :header-text="i18n.dropdownHeader" + :toggle-class="toggleButtonClass" + :text="buttonText" + class="ref-selector gl-w-full" + v-bind="$attrs" + v-on="$listeners" + @shown="focusSearchBox" + > + <template #header> + <gl-search-box-by-type + ref="searchBox" + v-model.trim="query" + :placeholder="i18n.searchPlaceholder" + autocomplete="off" + data-qa-selector="ref_selector_searchbox" + @input="onSearchBoxInput" + @keydown.enter.prevent="onSearchBoxEnter" + /> + </template> - <gl-loading-icon v-if="isLoading" size="lg" class="gl-my-3" /> + <gl-loading-icon v-if="isLoading" size="lg" class="gl-my-3" /> - <div v-else-if="showNoResults" class="gl-text-center gl-mx-3 gl-py-3" data-testid="no-results"> - <gl-sprintf v-if="lastQuery" :message="i18n.noResultsWithQuery"> - <template #query> - <b class="gl-word-break-all">{{ lastQuery }}</b> - </template> - </gl-sprintf> + <div + v-else-if="showNoResults" + class="gl-text-center gl-mx-3 gl-py-3" + data-testid="no-results" + > + <gl-sprintf v-if="lastQuery" :message="i18n.noResultsWithQuery"> + <template #query> + <b class="gl-word-break-all">{{ lastQuery }}</b> + </template> + </gl-sprintf> - <span v-else>{{ i18n.noResults }}</span> - </div> + <span v-else>{{ i18n.noResults }}</span> + </div> - <template v-else> - <template v-if="showBranchesSection"> - <ref-results-section - :section-title="i18n.branches" - :total-count="matches.branches.totalCount" - :items="matches.branches.list" - :selected-ref="selectedRef" - :error="matches.branches.error" - :error-message="i18n.branchesErrorMessage" - :show-header="showSectionHeaders" - data-testid="branches-section" - data-qa-selector="branches_section" - @selected="selectRef($event)" - /> + <template v-else> + <template v-if="showBranchesSection"> + <ref-results-section + :section-title="i18n.branches" + :total-count="matches.branches.totalCount" + :items="matches.branches.list" + :selected-ref="selectedRef" + :error="matches.branches.error" + :error-message="i18n.branchesErrorMessage" + :show-header="showSectionHeaders" + data-testid="branches-section" + data-qa-selector="branches_section" + @selected="selectRef($event)" + /> - <gl-dropdown-divider v-if="showTagsSection || showCommitsSection" /> - </template> + <gl-dropdown-divider v-if="showTagsSection || showCommitsSection" /> + </template> - <template v-if="showTagsSection"> - <ref-results-section - :section-title="i18n.tags" - :total-count="matches.tags.totalCount" - :items="matches.tags.list" - :selected-ref="selectedRef" - :error="matches.tags.error" - :error-message="i18n.tagsErrorMessage" - :show-header="showSectionHeaders" - data-testid="tags-section" - @selected="selectRef($event)" - /> + <template v-if="showTagsSection"> + <ref-results-section + :section-title="i18n.tags" + :total-count="matches.tags.totalCount" + :items="matches.tags.list" + :selected-ref="selectedRef" + :error="matches.tags.error" + :error-message="i18n.tagsErrorMessage" + :show-header="showSectionHeaders" + data-testid="tags-section" + @selected="selectRef($event)" + /> - <gl-dropdown-divider v-if="showCommitsSection" /> - </template> + <gl-dropdown-divider v-if="showCommitsSection" /> + </template> - <template v-if="showCommitsSection"> - <ref-results-section - :section-title="i18n.commits" - :total-count="matches.commits.totalCount" - :items="matches.commits.list" - :selected-ref="selectedRef" - :error="matches.commits.error" - :error-message="i18n.commitsErrorMessage" - :show-header="showSectionHeaders" - data-testid="commits-section" - @selected="selectRef($event)" - /> + <template v-if="showCommitsSection"> + <ref-results-section + :section-title="i18n.commits" + :total-count="matches.commits.totalCount" + :items="matches.commits.list" + :selected-ref="selectedRef" + :error="matches.commits.error" + :error-message="i18n.commitsErrorMessage" + :show-header="showSectionHeaders" + data-testid="commits-section" + @selected="selectRef($event)" + /> + </template> </template> - </template> - <template #footer> - <slot name="footer" v-bind="footerSlotProps"></slot> - </template> - </gl-dropdown> + <template #footer> + <slot name="footer" v-bind="footerSlotProps"></slot> + </template> + </gl-dropdown> + <input + v-if="name" + data-testid="selected-ref-form-field" + type="hidden" + :value="selectedRef" + :name="name" + /> + </div> </template> diff --git a/app/views/projects/default_branch/_show.html.haml b/app/views/projects/default_branch/_show.html.haml index eba0f336f80..23b46e02202 100644 --- a/app/views/projects/default_branch/_show.html.haml +++ b/app/views/projects/default_branch/_show.html.haml @@ -17,7 +17,7 @@ - else .form-group = f.label :default_branch, _("Default branch"), class: 'label-bold' - = f.select(:default_branch, @project.repository.branch_names, {}, {class: 'select2 select-wide', data: { qa_selector: 'default_branch_dropdown' }}) + .js-select-default-branch{ data: { default_branch: @project.default_branch, project_id: @project.id } } .form-group - help_text = _("When merge requests and commits in the default branch close, any issues they reference also close.") diff --git a/doc/development/database/batched_background_migrations.md b/doc/development/database/batched_background_migrations.md index 947e92bf83f..9730d767eed 100644 --- a/doc/development/database/batched_background_migrations.md +++ b/doc/development/database/batched_background_migrations.md @@ -545,7 +545,9 @@ To list the batched background migrations in the system, run this command: This command supports the following options: - Database selection: - - `--database DATABASE_NAME`: Connects to the given database. + - `--database DATABASE_NAME`: Connects to the given database: + - `main`: Uses the main database (default). + - `ci`: Uses the CI database. - Environment selection: - `--dev`: Uses the `dev` environment. - `--staging`: Uses the `staging` environment. @@ -568,7 +570,9 @@ To see the status and progress of a specific batched background migration, run t This command supports the following options: - Database selection: - - `--database DATABASE_NAME`: Connects to the given database. + - `--database DATABASE_NAME`: Connects to the given database: + - `main`: Uses the main database (default) + - `ci`: Uses the CI database - Environment selection: - `--dev`: Uses the `dev` environment. - `--staging`: Uses the `staging` environment. @@ -600,7 +604,9 @@ If you want to pause a batched background migration, you need to run the followi This command supports the following options: - Database selection: - - `--database DATABASE_NAME`: Connects to the given database. + - `--database DATABASE_NAME`: Connects to the given database: + - `main`: Uses the main database (default). + - `ci`: Uses the CI database. - Environment selection: - `--dev`: Uses the `dev` environment. - `--staging`: Uses the `staging` environment. @@ -623,7 +629,9 @@ If you want to resume a batched background migration, you need to run the follow This command supports the following options: - Database selection: - - `--database DATABASE_NAME`: Connects to the given database. + - `--database DATABASE_NAME`: Connects to the given database: + - `main`: Uses the main database (default). + - `ci`: Uses the CI database. - Environment selection: - `--dev`: Uses the `dev` environment. - `--staging`: Uses the `staging` environment. diff --git a/doc/user/application_security/terminology/index.md b/doc/user/application_security/terminology/index.md index c5cc59805eb..2215c89aa4f 100644 --- a/doc/user/application_security/terminology/index.md +++ b/doc/user/application_security/terminology/index.md @@ -5,16 +5,17 @@ info: To determine the technical writer assigned to the Stage/Group associated w type: reference --- -# Secure and Protect terminology **(FREE)** +# Secure and Govern terminology **(FREE)** -This terminology list for GitLab Secure and Protect aims to: +The glossary of terms aims to achieve the following: -- Promote a ubiquitous language for discussing application security. -- Improve the effectiveness of communication regarding GitLab application security features. -- Get new contributors up to speed faster. +- Promote a ubiquitous language that can be used everywhere - with customers, on issues, in Slack, in code. +- Improve the effectiveness of communication between team members. +- Reduce the potential for miscommunication. +- Bring new team members and community contributors up to speed faster, reducing the time to productivity. -This document defines application security terms in the specific context of GitLab Secure and -Protect features. Terms may therefore have different meanings outside that context. +The definitions of the terms outlined in this document are in the context of the GitLab +products. Therefore, a term may have a different meaning to users outside of GitLab. ## Terms @@ -28,9 +29,7 @@ an artifact after the job is complete. GitLab ingests this report, allowing user manage found vulnerabilities. For more information, see [Security Scanner Integration](../../../development/integrations/secure.md). Many GitLab analyzers follow a standard approach using Docker to run a wrapped scanner. For example, -the Docker image `bandit-sast` is an analyzer that wraps the scanner `Bandit`. You can optionally -use the [Common library](https://gitlab.com/gitlab-org/security-products/analyzers/common) -to assist in building an Analyzer. +the image `semgrep` is an analyzer that wraps the scanner `Semgrep`. ### Attack surface @@ -44,6 +43,12 @@ The set of meaningful test cases that are generated while the fuzzer is running. test case produces new coverage in the tested program. It's advised to re-use the corpus and pass it to subsequent runs. +### CNA + +[CVE](#cve) Numbering Authorities (CNAs) are organizations from around the world that are authorized by +the [Mitre Corporation](https://cve.mitre.org/) to assign [CVE](#cve)s to vulnerabilities in products or +services within their respective scope. [GitLab is a CNA](https://about.gitlab.com/security/cve/). + ### CVE Common Vulnerabilities and Exposures (CVE®) is a list of common identifiers for publicly known @@ -63,6 +68,11 @@ architecture. If left unaddressed, weaknesses could result in systems, networks, vulnerable to attack. The CWE List and associated classification taxonomy serve as a language that you can use to identify and describe these weaknesses in terms of CWEs. +### Deduplication + +When a category's process deems findings to be the same, or if they are similar enough that a noise reduction is +required, only one finding is kept and the others are eliminated. Read more about the [deduplication process](../vulnerability_report/pipeline.md#deduplication-process). + ### Duplicate finding A legitimate finding that is reported multiple times. This can occur when different scanners @@ -86,6 +96,13 @@ applications, and infrastructure. Findings are all potential vulnerability items scanners identify in MRs/feature branches. Only after merging to default does a finding become a [vulnerability](#vulnerability). +### Grouping + +A flexible and non-destructive way to visually organize vulnerabilities in groups when there are multiple findings +that are likely related but do not qualify for deduplication. For example, you can include findings that should be +evaluated together, would be fixed by the same action, or come from the same source. Grouping behavior for vulnerabilities is +under development and tracked in issue [267588](https://gitlab.com/gitlab-org/gitlab/-/issues/267588). + ### Insignificant finding A legitimate finding that a particular customer doesn't care about. @@ -93,16 +110,18 @@ A legitimate finding that a particular customer doesn't care about. ### Location fingerprint A finding's location fingerprint is a text value that's unique for each location on the attack -surface. Each Secure product defines this according to its type of attack surface. For example, SAST +surface. Each security product defines this according to its type of attack surface. For example, SAST incorporates file path and line number. -### Package managers +### Package managers and package types + +#### Package managers -A Package manager is a system that manages your project dependencies. +A package manager is a system that manages your project dependencies. The package manager provides a method to install new dependencies (also referred to as "packages"), manage where packages are stored on your file system, and offer capabilities for you to publish your own packages. -### Package types +#### Package types Each package manager, platform, type, or ecosystem has its own conventions and protocols to identify, locate, and provision software packages. @@ -200,9 +219,26 @@ table.package-managers-and-types ul { A page that displays findings discovered in the associated CI pipeline. +### Post-filter + +Post-filters help reduce noise in the scanner results and automate manual tasks. You can specify criteria that updates +or modifies vulnerability data based on scanner results. For example, you can flag findings as likely False Positives +and automatically resolve vulnerabilities that are no longer detected. These are not permanent actions and can be changed. + +Support for automatically resolving findings is tracked in epic [7478](https://gitlab.com/groups/gitlab-org/-/epics/7478) and +support for cheap scan is proposed in issue [349926](https://gitlab.com/gitlab-org/gitlab/-/issues/349926). + +### Pre-filter + +An irreversible action that is done to filter out target(s) before analysis occurs. This is usually provided to allow +the user to reduce scope and noise as well as speed up the analysis. This should not be done if a record is needed as +we currently do not store anything related to the skipped/excluded code or assets. + +Examples: `DS_EXCLUDED_PATHS` should `Exclude files and directories from the scan based on the paths provided.` + ### Primary identifier -A finding's primary identifier is a value unique to that finding. The external type and external ID +A finding's primary identifier is a value that is unique to each finding. The external type and external ID of the finding's [first identifier](https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/blob/v2.4.0-rc1/dist/sast-report-format.json#L228) combine to create the value. @@ -218,15 +254,19 @@ once it's imported into the database. ### Scan type (report type) -The type of scan. This must be one of the following: +Describes the type of scan. This must be one of the following: +- `api_fuzzing` - `cluster_image_scanning` - `container_scanning` +- `coverage_fuzzing` - `dast` - `dependency_scanning` - `sast` - `secret_detection` +This list is subject to change as scanners are added. + ### Scanner Software that can scan for vulnerabilities. The resulting scan report is typically not in the @@ -235,9 +275,12 @@ Software that can scan for vulnerabilities. The resulting scan report is typical ### Secure product A group of features related to a specific area of application security with first-class support by -GitLab. Products include Container Scanning, Dependency Scanning, Dynamic Application Security -Testing (DAST), Secret Detection, Static Application Security Testing (SAST), and Fuzz Testing. Each -of these products typically include one or more analyzers. +GitLab. + +Products include Container Scanning, Dependency Scanning, Dynamic Application Security +Testing (DAST), Secret Detection, Static Application Security Testing (SAST), and Fuzz Testing. + +Each of these products typically include one or more analyzers. ### Secure report format @@ -267,6 +310,7 @@ is listed as GitLab. A flaw that has a negative impact on the security of its environment. Vulnerabilities describe the error or weakness, and don't describe where the error is located (see [finding](#finding)). + Each vulnerability maps to a unique finding. Vulnerabilities exist in the default branch. Findings (see [finding](#finding)) are all potential vulnerability items scanners identify in MRs/feature branches. Only after merging to default does a finding become a vulnerability. @@ -280,8 +324,9 @@ When a [report finding](#report-finding) is stored to the database, it becomes a Deals with the responsibility of matching findings across scans so that a finding's life cycle can be understood. Engineers and security teams use this information to decide whether to merge code -changes, and to see unresolved findings and when they were introduced. Vulnerabilities are tracked -by comparing the location fingerprint, primary identifier, and report type. +changes, and to see unresolved findings and when they were introduced. + +Vulnerabilities are tracked by comparing the location fingerprint, primary identifier, and report type. ### Vulnerability occurrence diff --git a/lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml index 46aac3ee119..aba971a1abb 100644 --- a/lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml @@ -1,5 +1,5 @@ variables: - DAST_AUTO_DEPLOY_IMAGE_VERSION: 'v2.38.0' + DAST_AUTO_DEPLOY_IMAGE_VERSION: 'v2.38.1' .dast-auto-deploy: image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${DAST_AUTO_DEPLOY_IMAGE_VERSION}" diff --git a/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml index 54d2a8b6b7b..e59dc88aeb9 100644 --- a/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml @@ -1,5 +1,5 @@ variables: - AUTO_DEPLOY_IMAGE_VERSION: 'v2.38.0' + AUTO_DEPLOY_IMAGE_VERSION: 'v2.38.1' .auto-deploy: image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${AUTO_DEPLOY_IMAGE_VERSION}" diff --git a/lib/gitlab/ci/templates/Jobs/Deploy.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Deploy.latest.gitlab-ci.yml index 9802b667851..bf48db90bb1 100644 --- a/lib/gitlab/ci/templates/Jobs/Deploy.latest.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Jobs/Deploy.latest.gitlab-ci.yml @@ -1,5 +1,5 @@ variables: - AUTO_DEPLOY_IMAGE_VERSION: 'v2.38.0' + AUTO_DEPLOY_IMAGE_VERSION: 'v2.38.1' .auto-deploy: image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${AUTO_DEPLOY_IMAGE_VERSION}" diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 39fc1672707..c6a314ff538 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -35291,6 +35291,9 @@ msgstr "" msgid "Search authors" msgstr "" +msgid "Search branch" +msgstr "" + msgid "Search branches" msgstr "" @@ -36525,6 +36528,9 @@ msgstr "" msgid "Select branches" msgstr "" +msgid "Select default branch" +msgstr "" + msgid "Select due date" msgstr "" diff --git a/qa/qa/page/project/settings/default_branch.rb b/qa/qa/page/project/settings/default_branch.rb index cc28b37b88f..575f9006c84 100644 --- a/qa/qa/page/project/settings/default_branch.rb +++ b/qa/qa/page/project/settings/default_branch.rb @@ -5,16 +5,22 @@ module QA module Project module Settings class DefaultBranch < Page::Base - include Page::Component::Select2 - view 'app/views/projects/default_branch/_show.html.haml' do element :save_changes_button + end + + view 'app/assets/javascripts/projects/settings/components/default_branch_selector.vue' do element :default_branch_dropdown end + view 'app/assets/javascripts/ref/components/ref_selector.vue' do + element :ref_selector_searchbox + end + def set_default_branch(branch) - find('.select2-chosen').click - search_and_select(branch) + click_button :default_branch_dropdown + fill_in :ref_selector_searchbox, with: branch + click_button branch end def click_save_changes_button diff --git a/spec/features/projects/settings/user_changes_default_branch_spec.rb b/spec/features/projects/settings/user_changes_default_branch_spec.rb index 84e6c50cf61..508bbcc5327 100644 --- a/spec/features/projects/settings/user_changes_default_branch_spec.rb +++ b/spec/features/projects/settings/user_changes_default_branch_spec.rb @@ -3,8 +3,6 @@ require 'spec_helper' RSpec.describe 'Projects > Settings > User changes default branch' do - include Select2Helper - let(:user) { create(:user) } before do @@ -17,16 +15,21 @@ RSpec.describe 'Projects > Settings > User changes default branch' do let(:project) { create(:project, :repository, namespace: user.namespace) } it 'allows to change the default branch', :js do + dropdown_selector = '[data-testid="default-branch-dropdown"]' # Otherwise, running JS may overwrite our change to project_default_branch wait_for_requests - select2('fix', from: '#project_default_branch') + expect(page).to have_selector(dropdown_selector) + find(dropdown_selector).click + + fill_in 'Search branch', with: 'fix' + click_button 'fix' page.within '#default-branch-settings' do click_button 'Save changes' end - expect(find('#project_default_branch', visible: false).value).to eq 'fix' + expect(find("#{dropdown_selector} input", visible: false).value).to eq 'fix' end end @@ -34,7 +37,7 @@ RSpec.describe 'Projects > Settings > User changes default branch' do let(:project) { create(:project_empty_repo, namespace: user.namespace) } it 'does not show default branch selector' do - expect(page).not_to have_selector('#project_default_branch') + expect(page).not_to have_selector('[data-testid="default-branch-dropdown"]') end end end diff --git a/spec/frontend/projects/settings/components/default_branch_selector_spec.js b/spec/frontend/projects/settings/components/default_branch_selector_spec.js new file mode 100644 index 00000000000..94648d87524 --- /dev/null +++ b/spec/frontend/projects/settings/components/default_branch_selector_spec.js @@ -0,0 +1,46 @@ +import { shallowMount } from '@vue/test-utils'; +import DefaultBranchSelector from '~/projects/settings/components/default_branch_selector.vue'; +import RefSelector from '~/ref/components/ref_selector.vue'; +import { REF_TYPE_BRANCHES } from '~/ref/constants'; + +describe('projects/settings/components/default_branch_selector', () => { + const persistedDefaultBranch = 'main'; + const projectId = '123'; + let wrapper; + + const findRefSelector = () => wrapper.findComponent(RefSelector); + + const buildWrapper = () => { + wrapper = shallowMount(DefaultBranchSelector, { + propsData: { + persistedDefaultBranch, + projectId, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + }); + + beforeEach(() => { + buildWrapper(); + }); + + it('displays a RefSelector component', () => { + expect(findRefSelector().props()).toEqual({ + value: persistedDefaultBranch, + enabledRefTypes: [REF_TYPE_BRANCHES], + projectId, + state: true, + translations: { + dropdownHeader: expect.any(String), + searchPlaceholder: expect.any(String), + }, + useSymbolicRefNames: false, + name: 'project[default_branch]', + }); + + expect(findRefSelector().classes()).toContain('gl-w-full'); + }); +}); diff --git a/spec/frontend/ref/components/ref_selector_spec.js b/spec/frontend/ref/components/ref_selector_spec.js index 6c5af5a2625..96601a729b2 100644 --- a/spec/frontend/ref/components/ref_selector_spec.js +++ b/spec/frontend/ref/components/ref_selector_spec.js @@ -109,6 +109,8 @@ describe('Ref selector component', () => { const findCommitDropdownItems = () => findCommitsSection().findAllComponents(GlDropdownItem); const findFirstCommitDropdownItem = () => findCommitDropdownItems().at(0); + const findHiddenInputField = () => wrapper.find('[data-testid="selected-ref-form-field"]'); + // // Expecters // @@ -181,6 +183,24 @@ describe('Ref selector component', () => { expect(findLoadingIcon().exists()).toBe(false); }); }); + + describe('when name property is provided', () => { + it('renders an forrm input hidden field', () => { + const name = 'default_tag'; + + createComponent({ propsData: { name } }); + + expect(findHiddenInputField().attributes().name).toBe(name); + }); + }); + + describe('when name property is not provided', () => { + it('renders an forrm input hidden field', () => { + createComponent(); + + expect(findHiddenInputField().exists()).toBe(false); + }); + }); }); describe('post-initialization behavior', () => { @@ -194,7 +214,7 @@ describe('Ref selector component', () => { }); it('adds the provided ID to the GlDropdown instance', () => { - expect(wrapper.attributes().id).toBe(id); + expect(wrapper.findComponent(GlDropdown).attributes().id).toBe(id); }); }); @@ -202,7 +222,7 @@ describe('Ref selector component', () => { const preselectedRef = fixtures.branches[0].name; beforeEach(() => { - createComponent({ propsData: { value: preselectedRef } }); + createComponent({ propsData: { value: preselectedRef, name: 'selectedRef' } }); return waitForRequests(); }); @@ -210,6 +230,10 @@ describe('Ref selector component', () => { it('renders the pre-selected ref name', () => { expect(findButtonContent().text()).toBe(preselectedRef); }); + + it('binds hidden input field to the pre-selected ref', () => { + expect(findHiddenInputField().attributes().value).toBe(preselectedRef); + }); }); describe('when the selected ref is updated by the parent component', () => { |