Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
45eb4bc75f
commit
bce07fb4d3
|
|
@ -18,7 +18,7 @@ variables:
|
|||
# Helm chart ref used by test-on-cng pipeline
|
||||
GITLAB_HELM_CHART_REF: "be52d36697ab1513512670a5f1456e294d15dbcd"
|
||||
# Specific ref for cng-mirror project to trigger builds for
|
||||
GITLAB_CNG_MIRROR_REF: "41602cd52fec706fecb9df273016b2ea6236ee8a"
|
||||
GITLAB_CNG_MIRROR_REF: "4893053f6121ca32c87d7d28a7f76479cce58a72"
|
||||
# Makes sure some of the common scripts from pipeline-common use bundler to execute commands
|
||||
RUN_WITH_BUNDLE: "true"
|
||||
# Makes sure reporting script defined in .gitlab-qa-report from pipeline-common is executed from correct folder
|
||||
|
|
|
|||
|
|
@ -643,6 +643,9 @@ Cop/ActiveModelErrorsDirectManipulation:
|
|||
Gitlab/AvoidFeatureGet:
|
||||
Enabled: true
|
||||
|
||||
Gitlab/FeatureFlagKeyDynamic:
|
||||
Enabled: true
|
||||
|
||||
RSpec/WebMockEnable:
|
||||
Enabled: true
|
||||
Include:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
# Cop supports --autocorrect.
|
||||
Gitlab/FeatureFlagKeyDynamic:
|
||||
Details: grace period
|
||||
Exclude:
|
||||
- 'app/graphql/resolvers/app_config/gitlab_instance_feature_flags_resolver.rb'
|
||||
- 'app/graphql/resolvers/feature_flag_resolver.rb'
|
||||
- 'app/services/concerns/measurable.rb'
|
||||
- 'app/services/service_desk_settings/update_service.rb'
|
||||
- 'app/workers/concerns/worker_attributes.rb'
|
||||
- 'app/workers/loose_foreign_keys/cleanup_worker.rb'
|
||||
- 'ee/app/controllers/remote_development/workspaces_feature_flag_controller.rb'
|
||||
- 'ee/app/graphql/resolvers/ai/user_available_features_resolver.rb'
|
||||
- 'ee/app/graphql/resolvers/ai/user_code_suggestions_contexts_resolver.rb'
|
||||
- 'ee/app/models/concerns/geo/verifiable_replicator.rb'
|
||||
- 'ee/app/services/search/zoekt/info_service.rb'
|
||||
- 'ee/lib/gitlab/ai_gateway.rb'
|
||||
- 'ee/lib/gitlab/geo/replicator.rb'
|
||||
- 'ee/lib/gitlab/llm/chain/concerns/use_ai_gateway_agent_prompt.rb'
|
||||
- 'ee/lib/gitlab/llm/completions_factory.rb'
|
||||
- 'ee/lib/tasks/gitlab/nav/variant_generator.rb'
|
||||
- 'ee/spec/graphql/resolvers/ai/user_available_features_resolver_spec.rb'
|
||||
- 'ee/spec/models/gitlab_subscriptions/features_spec.rb'
|
||||
- 'lib/feature/gitaly.rb'
|
||||
- 'lib/gitlab/gon_helper.rb'
|
||||
- 'lib/gitlab/redis/multi_store.rb'
|
||||
- 'lib/gitlab/sidekiq_middleware/skip_jobs.rb'
|
||||
- 'lib/gitlab/sidekiq_sharding/router.rb'
|
||||
- 'lib/web_ide/extension_marketplace.rb'
|
||||
- 'spec/lib/feature_spec.rb'
|
||||
- 'spec/requests/api/features_spec.rb'
|
||||
- 'spec/support_specs/helpers/stub_feature_flags_spec.rb'
|
||||
|
|
@ -13,7 +13,8 @@ import initSourceCodeDropdowns from '~/vue_shared/components/download_dropdown/i
|
|||
import EmptyProject from '~/pages/projects/show/empty_project';
|
||||
import initHeaderApp from '~/repository/init_header_app';
|
||||
import initWebIdeLink from '~/pages/projects/shared/web_ide_link';
|
||||
import CompactCodeDropdown from '~/repository/components/code_dropdown/compact_code_dropdown.vue';
|
||||
import CompactCodeDropdown from 'ee_else_ce/repository/components/code_dropdown/compact_code_dropdown.vue';
|
||||
import apolloProvider from '~/repository/graphql';
|
||||
import { initHomePanel } from '../home_panel';
|
||||
|
||||
// Project show page loads different overview content based on user preferences
|
||||
|
|
@ -70,7 +71,8 @@ const initCodeDropdown = () => {
|
|||
|
||||
if (!codeDropdownEl) return false;
|
||||
|
||||
const { sshUrl, httpUrl, kerberosUrl } = codeDropdownEl.dataset;
|
||||
const { sshUrl, httpUrl, kerberosUrl, newWorkspacePath, projectId, projectPath } =
|
||||
codeDropdownEl.dataset;
|
||||
|
||||
const CodeDropdownComponent =
|
||||
gon.features.directoryCodeDropdownUpdates && gon.features.blobRepositoryVueHeaderApp
|
||||
|
|
@ -79,12 +81,16 @@ const initCodeDropdown = () => {
|
|||
|
||||
return new Vue({
|
||||
el: codeDropdownEl,
|
||||
apolloProvider,
|
||||
render(createElement) {
|
||||
return createElement(CodeDropdownComponent, {
|
||||
props: {
|
||||
sshUrl,
|
||||
httpUrl,
|
||||
kerberosUrl,
|
||||
projectId,
|
||||
projectPath,
|
||||
newWorkspacePath,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import CodeDropdownIdeItem from './code_dropdown_ide_item.vue';
|
|||
import { VSCODE_BASE_URL, JETBRAINS_BASE_URL } from './constants';
|
||||
|
||||
export default {
|
||||
name: 'CECompactCodeDropdown',
|
||||
components: {
|
||||
GlDisclosureDropdown,
|
||||
GlDisclosureDropdownGroup,
|
||||
|
|
@ -241,6 +242,7 @@ export default {
|
|||
@close-dropdown="closeDropdown"
|
||||
/>
|
||||
</gl-disclosure-dropdown-group>
|
||||
<slot name="gl-ee-compact-code-dropdown"></slot>
|
||||
</gl-disclosure-dropdown>
|
||||
</template>
|
||||
<style>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import Breadcrumbs from '~/repository/components/header_area/breadcrumbs.vue';
|
|||
import BlobControls from '~/repository/components/header_area/blob_controls.vue';
|
||||
import RepositoryOverflowMenu from '~/repository/components/header_area/repository_overflow_menu.vue';
|
||||
import CodeDropdown from '~/vue_shared/components/code_dropdown/code_dropdown.vue';
|
||||
import CompactCodeDropdown from '~/repository/components/code_dropdown/compact_code_dropdown.vue';
|
||||
import SourceCodeDownloadDropdown from '~/vue_shared/components/download_dropdown/download_dropdown.vue';
|
||||
import CloneCodeDropdown from '~/vue_shared/components/code_dropdown/clone_code_dropdown.vue';
|
||||
import AddToTree from '~/repository/components/header_area/add_to_tree.vue';
|
||||
|
|
@ -36,7 +35,8 @@ export default {
|
|||
RepositoryOverflowMenu,
|
||||
BlobControls,
|
||||
CodeDropdown,
|
||||
CompactCodeDropdown,
|
||||
CompactCodeDropdown: () =>
|
||||
import('ee_else_ce/repository/components/code_dropdown/compact_code_dropdown.vue'),
|
||||
SourceCodeDownloadDropdown,
|
||||
CloneCodeDropdown,
|
||||
AddToTree,
|
||||
|
|
@ -333,6 +333,8 @@ export default {
|
|||
:gitpod-url="gitpodUrl"
|
||||
:current-path="currentPath"
|
||||
:directory-download-links="downloadLinks"
|
||||
:project-id="projectId"
|
||||
:project-path="projectPath"
|
||||
:show-web-ide-button="showWebIdeButton"
|
||||
:show-gitpod-button="isGitpodEnabledForInstance"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import createStore from '~/code_navigation/store';
|
|||
import RefSelector from '~/ref/components/ref_selector.vue';
|
||||
import HighlightWorker from '~/vue_shared/components/source_viewer/workers/highlight_worker?worker';
|
||||
import CodeDropdown from '~/vue_shared/components/code_dropdown/code_dropdown.vue';
|
||||
import CompactCodeDropdown from '~/repository/components/code_dropdown/compact_code_dropdown.vue';
|
||||
import CompactCodeDropdown from 'ee_else_ce/repository/components/code_dropdown/compact_code_dropdown.vue';
|
||||
import App from './components/app.vue';
|
||||
import Breadcrumbs from './components/header_area/breadcrumbs.vue';
|
||||
import ForkInfo from './components/fork_info.vue';
|
||||
|
|
@ -185,8 +185,15 @@ export default function setupVueRepositoryList() {
|
|||
|
||||
if (!codeDropdownEl) return false;
|
||||
|
||||
const { sshUrl, httpUrl, kerberosUrl, xcodeUrl, directoryDownloadLinks } =
|
||||
codeDropdownEl.dataset;
|
||||
const {
|
||||
sshUrl,
|
||||
httpUrl,
|
||||
kerberosUrl,
|
||||
xcodeUrl,
|
||||
directoryDownloadLinks,
|
||||
newWorkspacePath,
|
||||
projectId,
|
||||
} = codeDropdownEl.dataset;
|
||||
|
||||
const CodeDropdownComponent =
|
||||
gon.features.directoryCodeDropdownUpdates && gon.features.blobRepositoryVueHeaderApp
|
||||
|
|
@ -196,6 +203,7 @@ export default function setupVueRepositoryList() {
|
|||
return new Vue({
|
||||
el: codeDropdownEl,
|
||||
router,
|
||||
apolloProvider,
|
||||
render(createElement) {
|
||||
return createElement(CodeDropdownComponent, {
|
||||
props: {
|
||||
|
|
@ -205,6 +213,9 @@ export default function setupVueRepositoryList() {
|
|||
xcodeUrl,
|
||||
currentPath: this.$route.params.path,
|
||||
directoryDownloadLinks: JSON.parse(directoryDownloadLinks),
|
||||
projectId,
|
||||
projectPath,
|
||||
newWorkspacePath,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import Vue from 'vue';
|
||||
import { provideWebIdeLink } from 'ee_else_ce/pages/projects/shared/web_ide_link/provide_web_ide_link';
|
||||
import { parseBoolean, convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
|
||||
import apolloProvider from './graphql';
|
||||
import projectShortPathQuery from './queries/project_short_path.query.graphql';
|
||||
|
|
@ -64,7 +63,7 @@ export default function initHeaderApp({ router, isReadmeView = false, isBlobView
|
|||
downloadArtifacts,
|
||||
projectShortPath,
|
||||
isBinary,
|
||||
...options
|
||||
newWorkspacePath,
|
||||
} = headerEl.dataset;
|
||||
|
||||
const {
|
||||
|
|
@ -126,12 +125,12 @@ export default function initHeaderApp({ router, isReadmeView = false, isBlobView
|
|||
xcodeUrl,
|
||||
sshUrl,
|
||||
kerberosUrl,
|
||||
newWorkspacePath,
|
||||
downloadLinks: downloadLinks ? JSON.parse(downloadLinks) : null,
|
||||
downloadArtifacts: downloadArtifacts ? JSON.parse(downloadArtifacts) : [],
|
||||
isBlobView,
|
||||
isBinary: parseBoolean(isBinary),
|
||||
rootRef,
|
||||
...provideWebIdeLink(options),
|
||||
},
|
||||
apolloProvider,
|
||||
router: router || createRouter(projectPath, escapedRef),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module ReadmeHelper
|
||||
# @return [Hash]
|
||||
def vue_readme_header_additional_data
|
||||
{}
|
||||
end
|
||||
end
|
||||
|
||||
ReadmeHelper.prepend_mod_with("ReadmeHelper")
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
web_ide_button_options: web_ide_button_data.merge(fork_options).to_json,
|
||||
web_ide_button_default_branch: @project.default_branch_or_main,
|
||||
escaped_ref: ActionDispatch::Journey::Router::Utils.escape_path(ref)
|
||||
} }
|
||||
}.merge(vue_readme_header_additional_data) }
|
||||
|
||||
- else
|
||||
.nav-block.mt-0
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@ feature_category: geo_replication
|
|||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/169237
|
||||
milestone: '17.6'
|
||||
queued_migration_version: 20241015075957
|
||||
finalized_by: # version of the migration that finalized this BBM
|
||||
finalized_by: '20250325231842'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class FinalizeHkBackfillDependencyProxyBlobStatesGroupId < Gitlab::Database::Migration[2.2]
|
||||
milestone '17.11'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_main_cell
|
||||
|
||||
def up
|
||||
ensure_batched_background_migration_is_finished(
|
||||
job_class_name: 'BackfillDependencyProxyBlobStatesGroupId',
|
||||
table_name: :dependency_proxy_blob_states,
|
||||
column_name: :dependency_proxy_blob_id,
|
||||
job_arguments: [:group_id, :dependency_proxy_blobs, :group_id, :dependency_proxy_blob_id],
|
||||
finalize: true
|
||||
)
|
||||
end
|
||||
|
||||
def down; end
|
||||
end
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class SyncRemovePagesDeploymentsDeletedAtIndex < Gitlab::Database::Migration[2.2]
|
||||
disable_ddl_transaction!
|
||||
milestone '17.11'
|
||||
|
||||
INDEX_NAME = 'pages_deployments_deleted_at_index'
|
||||
COLUMNS = [:id, :project_id, :path_prefix]
|
||||
|
||||
def up
|
||||
remove_concurrent_index_by_name :pages_deployments, name: INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
add_concurrent_index :pages_deployments, COLUMNS, where: 'deleted_at IS NULL', name: INDEX_NAME
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
170c287c4509f48acc2e923f15145ec3cdcac85e7bbe71b6737873e33891acd1
|
||||
|
|
@ -0,0 +1 @@
|
|||
8829b0f5ebc0935cb6ecf1f9e1fa8f1cedab3db3a1565c1fcc008a2e750ab467
|
||||
|
|
@ -37978,8 +37978,6 @@ CREATE INDEX packages_packages_needs_verification ON packages_package_files USIN
|
|||
|
||||
CREATE INDEX packages_packages_pending_verification ON packages_package_files USING btree (verified_at NULLS FIRST) WHERE (verification_state = 0);
|
||||
|
||||
CREATE INDEX pages_deployments_deleted_at_index ON pages_deployments USING btree (id, project_id, path_prefix) WHERE (deleted_at IS NULL);
|
||||
|
||||
CREATE INDEX pages_deployments_deleted_at_null_index ON pages_deployments USING btree (project_id, path_prefix, id) WHERE (deleted_at IS NULL);
|
||||
|
||||
CREATE UNIQUE INDEX partial_idx_bulk_import_exports_on_group_user_and_relation ON bulk_import_exports USING btree (group_id, relation, user_id) WHERE ((group_id IS NOT NULL) AND (user_id IS NOT NULL));
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ For guidelines on specific words, see [the word list](word_list.md).
|
|||
## The GitLab voice
|
||||
|
||||
The GitLab brand guidelines define the
|
||||
[voice used by the larger organization](https://design.gitlab.com/brand-overview/introduction/#brand-personality).
|
||||
[voice used by the larger organization](https://design.gitlab.com/brand-messaging/brand-voice).
|
||||
|
||||
Building on that guidance, the voice in the GitLab documentation strives to be concise,
|
||||
direct, and precise. The goal is to provide information that's easy to search and scan.
|
||||
|
|
|
|||
|
|
@ -16159,6 +16159,9 @@ msgstr ""
|
|||
msgid "Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for Auto DevOps to work."
|
||||
msgstr ""
|
||||
|
||||
msgid "Container scanning"
|
||||
msgstr ""
|
||||
|
||||
msgid "ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -20227,9 +20230,6 @@ msgstr ""
|
|||
msgid "Dependencies|+%{remainingLicensesCount} more"
|
||||
msgstr ""
|
||||
|
||||
msgid "Dependencies|All"
|
||||
msgstr ""
|
||||
|
||||
msgid "Dependencies|Component"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -20350,9 +20350,6 @@ msgstr ""
|
|||
msgid "Dependencies|Vulnerabilities"
|
||||
msgstr ""
|
||||
|
||||
msgid "Dependencies|Vulnerable components"
|
||||
msgstr ""
|
||||
|
||||
msgid "Dependencies|unknown"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -20365,6 +20362,9 @@ msgstr ""
|
|||
msgid "Dependency list"
|
||||
msgstr ""
|
||||
|
||||
msgid "Dependency scanning"
|
||||
msgstr ""
|
||||
|
||||
msgid "DependencyListExport|License Identifiers"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -22609,6 +22609,9 @@ msgstr ""
|
|||
msgid "Dynamic Application Security Testing (DAST)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Dynamic application security testing (DAST)"
|
||||
msgstr ""
|
||||
|
||||
msgid "E-mail:"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -31444,6 +31447,9 @@ msgstr ""
|
|||
msgid "Infrastructure as Code (IaC) Scanning"
|
||||
msgstr ""
|
||||
|
||||
msgid "Infrastructure as code scanning (IaC)"
|
||||
msgstr ""
|
||||
|
||||
msgid "InfrastructureRegistry|Copy Terraform Command"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -52641,6 +52647,9 @@ msgstr ""
|
|||
msgid "Secret Detection"
|
||||
msgstr ""
|
||||
|
||||
msgid "Secret detection"
|
||||
msgstr ""
|
||||
|
||||
msgid "Secret push protection"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -57490,6 +57499,9 @@ msgstr ""
|
|||
msgid "Static Application Security Testing (SAST)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Static application security testing (SAST)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Statistics"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ module QA
|
|||
end
|
||||
|
||||
def select_label(label)
|
||||
select_labels([label])
|
||||
select_labels([label.title])
|
||||
end
|
||||
|
||||
def has_label?(label)
|
||||
|
|
|
|||
|
|
@ -377,11 +377,11 @@ module QA
|
|||
# merge button, in such case we must retry loop otherwise find_element will raise ElementNotFound error
|
||||
next false unless has_element?('merge-button', wait: 1)
|
||||
|
||||
break true unless find_element('merge-button').disabled?
|
||||
|
||||
# If the widget shows "Merge blocked: new changes were just added" we can refresh the page and check again
|
||||
next false if merge_blocked_by_new_changes?
|
||||
|
||||
break true unless find_element('merge-button').disabled?
|
||||
|
||||
QA::Runtime::Logger.debug("MR widget text: \"#{mr_widget_text}\"")
|
||||
|
||||
false
|
||||
|
|
|
|||
|
|
@ -2,10 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'Merge request rebasing', product_group: :code_review, quarantine: {
|
||||
issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/527224',
|
||||
type: :investigating
|
||||
} do
|
||||
describe 'Merge request rebasing', product_group: :code_review do
|
||||
let!(:merge_request) { create(:merge_request) }
|
||||
|
||||
before do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Gitlab
|
||||
# The first argument to `Feature.enabled?` and `Feature.disabled?` should be a literal symbol.
|
||||
# Dynamic keys are discouraged because they are harder to explicitly search for in the codebase by name.
|
||||
# Strings are similarly discouraged to simplify exact matching when searching for flag usage.
|
||||
#
|
||||
# Feature flags are technical debt (should be short lived), so it is important to ensure we can find all usages in
|
||||
# order to remove the flag safely.
|
||||
# More information at https://docs.gitlab.com/development/feature_flags/#feature-flag-definition-and-validation
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# Feature.enabled?('some_flag')
|
||||
# Feature.enabled?(flag_name)
|
||||
# Feature.disabled?(flag)
|
||||
#
|
||||
# # good
|
||||
# Feature.enabled?(:some_flag)
|
||||
# Feature.disabled?(:other_flag)
|
||||
class FeatureFlagKeyDynamic < RuboCop::Cop::Base
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'First argument to `Feature.%<method>s` must be a literal symbol.'
|
||||
|
||||
FEATURE_METHODS = %i[enabled? disabled?].freeze
|
||||
|
||||
# @!method feature_flag_method?(node)
|
||||
def_node_matcher :feature_flag_method?, <<~PATTERN
|
||||
(send
|
||||
(const nil? :Feature)
|
||||
${:enabled? :disabled?}
|
||||
...
|
||||
)
|
||||
PATTERN
|
||||
|
||||
def on_send(node)
|
||||
method_name = feature_flag_method?(node)
|
||||
return unless method_name
|
||||
|
||||
first_arg = node.first_argument
|
||||
return if first_arg&.sym_type?
|
||||
|
||||
message = format(MSG, method: method_name)
|
||||
|
||||
add_offense(first_arg, message: message) do |corrector|
|
||||
autocorrect(corrector, first_arg) if first_arg&.str_type?
|
||||
end
|
||||
end
|
||||
alias_method :on_csend, :on_send
|
||||
|
||||
private
|
||||
|
||||
def autocorrect(corrector, arg_node)
|
||||
return unless arg_node.str_type?
|
||||
|
||||
corrector.replace(arg_node, ":#{arg_node.value}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -11,7 +11,7 @@ import {
|
|||
expectedDirectoryDownloadItems,
|
||||
} from 'jest/repository/components/code_dropdown/mock_data';
|
||||
|
||||
describe('Compact Code Dropdown coomponent', () => {
|
||||
describe('Compact Code Dropdown component', () => {
|
||||
let wrapper;
|
||||
const sshUrl = 'ssh://foo.bar';
|
||||
const httpUrl = 'http://foo.bar';
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import RefSelector from '~/ref/components/ref_selector.vue';
|
|||
import HeaderArea from '~/repository/components/header_area.vue';
|
||||
import Breadcrumbs from '~/repository/components/header_area/breadcrumbs.vue';
|
||||
import CodeDropdown from '~/vue_shared/components/code_dropdown/code_dropdown.vue';
|
||||
import CompactCodeDropdown from '~/repository/components/code_dropdown/compact_code_dropdown.vue';
|
||||
import SourceCodeDownloadDropdown from '~/vue_shared/components/download_dropdown/download_dropdown.vue';
|
||||
import AddToTree from '~/repository/components/header_area/add_to_tree.vue';
|
||||
import FileIcon from '~/vue_shared/components/file_icon.vue';
|
||||
|
|
@ -14,6 +13,7 @@ import BlobControls from '~/repository/components/header_area/blob_controls.vue'
|
|||
import Shortcuts from '~/behaviors/shortcuts/shortcuts';
|
||||
import { useMockInternalEventsTracking } from 'helpers/tracking_internal_events_helper';
|
||||
import { headerAppInjected } from 'ee_else_ce_jest/repository/mock_data';
|
||||
import CompactCodeDropdown from 'ee_else_ce/repository/components/code_dropdown/compact_code_dropdown.vue';
|
||||
|
||||
const defaultMockRoute = {
|
||||
params: {
|
||||
|
|
@ -35,9 +35,9 @@ describe('HeaderArea', () => {
|
|||
const findFindFileButton = () => wrapper.findByTestId('tree-find-file-control');
|
||||
const findWebIdeButton = () => wrapper.findByTestId('js-tree-web-ide-link');
|
||||
const findCodeDropdown = () => wrapper.findComponent(CodeDropdown);
|
||||
const findCompactCodeDropdown = () => wrapper.findComponent(CompactCodeDropdown);
|
||||
const findSourceCodeDownloadDropdown = () => wrapper.findComponent(SourceCodeDownloadDropdown);
|
||||
const findCloneCodeDropdown = () => wrapper.findComponent(CloneCodeDropdown);
|
||||
const findCompactCodeDropdown = () => wrapper.findComponent(CompactCodeDropdown);
|
||||
const findAddToTreeDropdown = () => wrapper.findComponent(AddToTree);
|
||||
const findPageHeading = () => wrapper.findByTestId('repository-heading');
|
||||
const findFileIcon = () => wrapper.findComponent(FileIcon);
|
||||
|
|
@ -46,7 +46,11 @@ describe('HeaderArea', () => {
|
|||
|
||||
const { bindInternalEventDocument } = useMockInternalEventsTracking();
|
||||
|
||||
const createComponent = (props = {}, route = { name: 'blobPathDecoded' }, provided = {}) => {
|
||||
const createComponent = ({
|
||||
props = {},
|
||||
route = { name: 'blobPathDecoded' },
|
||||
provided = {},
|
||||
} = {}) => {
|
||||
return shallowMountExtended(HeaderArea, {
|
||||
provide: {
|
||||
...headerAppInjected,
|
||||
|
|
@ -62,6 +66,7 @@ describe('HeaderArea', () => {
|
|||
},
|
||||
stubs: {
|
||||
RouterLink: RouterLinkStub,
|
||||
CompactCodeDropdown,
|
||||
},
|
||||
mocks: {
|
||||
$route: {
|
||||
|
|
@ -94,7 +99,9 @@ describe('HeaderArea', () => {
|
|||
|
||||
describe('when rendered for tree view', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = createComponent({}, { name: 'treePathDecoded', params: { path: 'project' } });
|
||||
wrapper = createComponent({
|
||||
route: { name: 'treePathDecoded', params: { path: 'project' } },
|
||||
});
|
||||
});
|
||||
|
||||
describe('PageHeading', () => {
|
||||
|
|
@ -168,7 +175,15 @@ describe('HeaderArea', () => {
|
|||
|
||||
describe('when rendered for tree view and directory_code_dropdown_updates flag is true', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = createComponent({}, {}, { glFeatures: { directoryCodeDropdownUpdates: true } });
|
||||
wrapper = createComponent({
|
||||
route: { name: 'treePathDecoded' },
|
||||
provided: {
|
||||
glFeatures: {
|
||||
directoryCodeDropdownUpdates: true,
|
||||
},
|
||||
newWorkspacePath: '/workspaces/new',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
describe('Add to tree dropdown', () => {
|
||||
|
|
@ -199,7 +214,10 @@ describe('HeaderArea', () => {
|
|||
});
|
||||
|
||||
it('renders RepositoryOverflowMenu component with correct props when on ref different than default branch', () => {
|
||||
wrapper = createComponent({}, 'treePathDecoded', { comparePath: 'test/project/compare' });
|
||||
wrapper = createComponent({
|
||||
route: { name: 'treePathDecoded' },
|
||||
provided: { comparePath: 'test/project/compare' },
|
||||
});
|
||||
expect(findRepositoryOverflowMenu().exists()).toBe(true);
|
||||
expect(findRepositoryOverflowMenu().props('comparePath')).toBe(
|
||||
headerAppInjected.comparePath,
|
||||
|
|
@ -210,7 +228,7 @@ describe('HeaderArea', () => {
|
|||
|
||||
describe('when rendered for blob view', () => {
|
||||
it('renders BlobControls component with correct props', () => {
|
||||
wrapper = createComponent({ refType: 'branch' });
|
||||
wrapper = createComponent({ props: { refType: 'branch' } });
|
||||
expect(findBlobControls().exists()).toBe(true);
|
||||
expect(findBlobControls().props('projectPath')).toBe('test/project');
|
||||
expect(findBlobControls().props('refType')).toBe('');
|
||||
|
|
@ -235,7 +253,10 @@ describe('HeaderArea', () => {
|
|||
|
||||
describe('when rendered for readme project overview', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = createComponent({}, { name: 'treePathDecoded' }, { isReadmeView: true });
|
||||
wrapper = createComponent({
|
||||
route: { name: 'treePathDecoded' },
|
||||
provided: { isReadmeView: true },
|
||||
});
|
||||
});
|
||||
|
||||
it('does not render directory name and icon', () => {
|
||||
|
|
@ -260,7 +281,7 @@ describe('HeaderArea', () => {
|
|||
|
||||
describe('when rendered for full project overview', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = createComponent({}, { name: 'projectRoot' });
|
||||
wrapper = createComponent({ route: { name: 'projectRoot' } });
|
||||
});
|
||||
|
||||
it('does not render directory name and icon', () => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe ReadmeHelper, feature_category: :source_code_management do
|
||||
it 'returns a hash' do
|
||||
expect(helper.vue_readme_header_additional_data).to be_a(Hash)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop_spec_helper'
|
||||
|
||||
require_relative '../../../../rubocop/cop/gitlab/feature_flag_key_dynamic'
|
||||
|
||||
RSpec.describe RuboCop::Cop::Gitlab::FeatureFlagKeyDynamic, feature_category: :scalability do
|
||||
context 'when calling Feature.enabled?' do
|
||||
it 'registers an offense when using a variable as the first argument' do
|
||||
expect_offense(<<~RUBY)
|
||||
flag_name = :some_flag
|
||||
Feature.enabled?(flag_name)
|
||||
^^^^^^^^^ First argument to `Feature.enabled?` must be a literal symbol.
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'registers an offense when using a method call as the first argument' do
|
||||
expect_offense(<<~RUBY)
|
||||
Feature.enabled?(get_flag_name)
|
||||
^^^^^^^^^^^^^ First argument to `Feature.enabled?` must be a literal symbol.
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'registers an offense when using a string as the first argument' do
|
||||
expect_offense(<<~RUBY)
|
||||
Feature.enabled?('some_flag')
|
||||
^^^^^^^^^^^ First argument to `Feature.enabled?` must be a literal symbol.
|
||||
RUBY
|
||||
|
||||
expect_correction(<<~RUBY)
|
||||
Feature.enabled?(:some_flag)
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'does not register an offense when using a literal symbol' do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
Feature.enabled?(:some_flag)
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'does not register an offense when using a literal symbol with additional arguments' do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
Feature.enabled?(:some_flag, project)
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
||||
context 'when calling Feature.disabled?' do
|
||||
it 'registers an offense when using a variable as the first argument' do
|
||||
expect_offense(<<~RUBY)
|
||||
flag_name = :some_flag
|
||||
Feature.disabled?(flag_name)
|
||||
^^^^^^^^^ First argument to `Feature.disabled?` must be a literal symbol.
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'registers an offense when using a method call as the first argument' do
|
||||
expect_offense(<<~RUBY)
|
||||
Feature.disabled?(get_flag_name)
|
||||
^^^^^^^^^^^^^ First argument to `Feature.disabled?` must be a literal symbol.
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'registers an offense when using a string as the first argument' do
|
||||
expect_offense(<<~RUBY)
|
||||
Feature.disabled?('some_flag')
|
||||
^^^^^^^^^^^ First argument to `Feature.disabled?` must be a literal symbol.
|
||||
RUBY
|
||||
|
||||
expect_correction(<<~RUBY)
|
||||
Feature.disabled?(:some_flag)
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'does not register an offense when using a literal symbol' do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
Feature.disabled?(:some_flag)
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
||||
context 'with non-Feature methods' do
|
||||
it 'does not register an offense for methods on other objects' do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
OtherClass.enabled?(flag_name)
|
||||
something.disabled?(flag_name)
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in New Issue