From a4dacb5d7ea29aa080c79acd9e5a03e36f5e8b8d Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Mon, 17 Mar 2025 00:07:13 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../components/settings_app.vue | 160 ++++++------ .../components/settings_form.vue | 215 ++++++++++++++++ .../vscode_extension_marketplace/constants.js | 2 + app/helpers/application_settings_helper.rb | 8 +- app/models/application_setting.rb | 4 +- .../application_setting_implementation.rb | 2 +- locale/gitlab.pot | 30 +++ .../components/settings_app_spec.js | 171 +++++++------ .../components/settings_form_spec.js | 240 ++++++++++++++++++ .../vscode_extension_marketplace/mock_data.js | 11 + .../application_settings_helper_spec.rb | 7 +- spec/models/application_setting_spec.rb | 23 +- spec/requests/api/settings_spec.rb | 11 + .../_extension_marketplace.html.haml_spec.rb | 17 +- 14 files changed, 751 insertions(+), 150 deletions(-) create mode 100644 app/assets/javascripts/vscode_extension_marketplace/components/settings_form.vue create mode 100644 app/assets/javascripts/vscode_extension_marketplace/constants.js create mode 100644 spec/frontend/vscode_extension_marketplace/components/settings_form_spec.js create mode 100644 spec/frontend/vscode_extension_marketplace/mock_data.js diff --git a/app/assets/javascripts/vscode_extension_marketplace/components/settings_app.vue b/app/assets/javascripts/vscode_extension_marketplace/components/settings_app.vue index 0b620281aa7..6b314770b33 100644 --- a/app/assets/javascripts/vscode_extension_marketplace/components/settings_app.vue +++ b/app/assets/javascripts/vscode_extension_marketplace/components/settings_app.vue @@ -1,20 +1,25 @@ diff --git a/app/assets/javascripts/vscode_extension_marketplace/components/settings_form.vue b/app/assets/javascripts/vscode_extension_marketplace/components/settings_form.vue new file mode 100644 index 00000000000..0d502984e97 --- /dev/null +++ b/app/assets/javascripts/vscode_extension_marketplace/components/settings_form.vue @@ -0,0 +1,215 @@ + + + diff --git a/app/assets/javascripts/vscode_extension_marketplace/constants.js b/app/assets/javascripts/vscode_extension_marketplace/constants.js new file mode 100644 index 00000000000..527a1926e82 --- /dev/null +++ b/app/assets/javascripts/vscode_extension_marketplace/constants.js @@ -0,0 +1,2 @@ +export const PRESET_OPEN_VSX = 'open_vsx'; +export const PRESET_CUSTOM = 'custom'; diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index 74cae536d62..235e5438b27 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -587,7 +587,8 @@ module ApplicationSettingsHelper :global_search_users_enabled, :global_search_issues_enabled, :global_search_merge_requests_enabled, - :vscode_extension_marketplace + :vscode_extension_marketplace, + :vscode_extension_marketplace_enabled ].tap do |settings| unless Gitlab.com? settings << :resource_usage_limits @@ -689,10 +690,15 @@ module ApplicationSettingsHelper # NOTE: This is intentionally not scoped to a specific actor since it affects instance-level settings. return unless Feature.enabled?(:vscode_extension_marketplace_settings, nil) + presets = ::WebIde::ExtensionMarketplacePreset.all.map do |preset| + preset.to_h.deep_transform_keys { |key| key.to_s.camelize(:lower) } + end + { title: _('VS Code Extension Marketplace'), description: vscode_extension_marketplace_settings_description, view_model: { + presets: presets, initialSettings: @application_setting.vscode_extension_marketplace || {} } } diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 5e9a8a2af0b..9b6c02694eb 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -120,7 +120,6 @@ class ApplicationSetting < ApplicationRecord attribute :repository_storages_weighted, default: -> { {} } attribute :kroki_formats, default: -> { {} } attribute :default_branch_protection_defaults, default: -> { {} } - attribute :vscode_extension_marketplace, default: -> { {} } chronic_duration_attr_writer :archive_builds_in_human_readable, :archive_builds_in_seconds @@ -950,6 +949,9 @@ class ApplicationSetting < ApplicationRecord validates :vscode_extension_marketplace, json_schema: { filename: "application_setting_vscode_extension_marketplace", detail_errors: true } + jsonb_accessor :vscode_extension_marketplace, + vscode_extension_marketplace_enabled: [:boolean, { default: false, store_key: :enabled }] + before_validation :ensure_uuid! before_validation :coerce_repository_storages_weighted, if: :repository_storages_weighted_changed? before_validation :normalize_default_branch_name diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb index df5e00bca06..3c7a75d8140 100644 --- a/app/models/application_setting_implementation.rb +++ b/app/models/application_setting_implementation.rb @@ -319,7 +319,7 @@ module ApplicationSettingImplementation seat_control: 0, show_migrate_from_jenkins_banner: true, ropc_without_client_credentials: true, - vscode_extension_marketplace: {} + vscode_extension_marketplace_enabled: false }.tap do |hsh| hsh.merge!(non_production_defaults) unless Rails.env.production? end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index fe3b6d75f26..2449ab0f056 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -24507,18 +24507,48 @@ msgstr "" msgid "Exported requirements" msgstr "" +msgid "ExtensionMarketplace|A valid URL is required." +msgstr "" + msgid "ExtensionMarketplace|An unknown error occurred. Please try again." msgstr "" +msgid "ExtensionMarketplace|Disable Open VSX extension registry to set a custom value for this field." +msgstr "" + +msgid "ExtensionMarketplace|Enable Extension Marketplace" +msgstr "" + +msgid "ExtensionMarketplace|Enable the VS Code extension marketplace for all users." +msgstr "" + msgid "ExtensionMarketplace|Extension marketplace settings updated." msgstr "" +msgid "ExtensionMarketplace|Extension registry settings" +msgstr "" + msgid "ExtensionMarketplace|Failed to update extension marketplace settings." msgstr "" msgid "ExtensionMarketplace|Failed to update extension marketplace settings. %{message}" msgstr "" +msgid "ExtensionMarketplace|Item URL" +msgstr "" + +msgid "ExtensionMarketplace|Learn more about the %{linkStart}Open VSX Registry%{linkEnd}" +msgstr "" + +msgid "ExtensionMarketplace|Resource URL Template" +msgstr "" + +msgid "ExtensionMarketplace|Service URL" +msgstr "" + +msgid "ExtensionMarketplace|Use Open VSX extension registry" +msgstr "" + msgid "External URL" msgstr "" diff --git a/spec/frontend/vscode_extension_marketplace/components/settings_app_spec.js b/spec/frontend/vscode_extension_marketplace/components/settings_app_spec.js index a2f64ee3090..ab5eb5ca4bb 100644 --- a/spec/frontend/vscode_extension_marketplace/components/settings_app_spec.js +++ b/spec/frontend/vscode_extension_marketplace/components/settings_app_spec.js @@ -1,18 +1,29 @@ import { nextTick } from 'vue'; -import { GlAlert, GlButton, GlForm, GlFormFields, GlFormTextarea } from '@gitlab/ui'; +import { GlAlert, GlAccordion, GlAccordionItem, GlToggle } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import MockAdapter from 'axios-mock-adapter'; +import waitForPromises from 'helpers/wait_for_promises'; import axios from '~/lib/utils/axios_utils'; import { logError } from '~/lib/logger'; import SettingsApp from '~/vscode_extension_marketplace/components/settings_app.vue'; +import SettingsForm from '~/vscode_extension_marketplace/components/settings_form.vue'; import toast from '~/vue_shared/plugins/global_toast'; +import { PRESETS } from '../mock_data'; jest.mock('~/lib/logger'); jest.mock('~/vue_shared/plugins/global_toast'); jest.mock('lodash/uniqueId', () => (x) => `${x}testUnique`); -const TEST_NEW_SETTINGS = { enabled: false, preset: 'open_vsx' }; -const EXPECTED_FORM_ID = 'extension-marketplace-settings-form-testUnique'; +const TEST_NEW_SETTINGS = { preset: 'open_vsx' }; +const TEST_INIT_SETTINGS = { + enabled: true, + preset: 'custom', + custom_values: { + item_url: 'abc', + service_url: 'def', + resource_url_template: 'ghi', + }, +}; describe('~/vscode_extension_marketplace/components/settings_app.vue', () => { let wrapper; @@ -22,18 +33,16 @@ describe('~/vscode_extension_marketplace/components/settings_app.vue', () => { const createComponent = (props = {}) => { wrapper = shallowMount(SettingsApp, { propsData: { + presets: PRESETS, ...props, }, - stubs: { - GlFormFields, - }, }); }; - const findForm = () => wrapper.findComponent(GlForm); - const findFormFields = () => wrapper.findComponent(GlFormFields); - const findTextarea = () => wrapper.findComponent(GlFormTextarea); - const findSaveButton = () => wrapper.findComponent(GlButton); + const findAccordion = () => wrapper.findComponent(GlAccordion); + const findAccordionItem = () => findAccordion().findComponent(GlAccordionItem); + const findSettingsForm = () => findAccordionItem().findComponent(SettingsForm); + const findToggle = () => wrapper.findComponent(GlToggle); const findErrorAlert = () => wrapper.findComponent(GlAlert); const findErrorAlertItems = () => findErrorAlert() @@ -58,63 +67,67 @@ describe('~/vscode_extension_marketplace/components/settings_app.vue', () => { createComponent(); }); - it('renders form', () => { - expect(findForm().attributes('id')).toBe(EXPECTED_FORM_ID); + it('renders toggle', () => { + expect(findToggle().props()).toMatchObject({ + value: false, + isLoading: false, + label: 'Enable Extension Marketplace', + help: 'Enable the VS Code extension marketplace for all users.', + labelPosition: 'top', + }); }); - it('renders form fields', () => { - expect(findFormFields().props()).toMatchObject({ - formId: EXPECTED_FORM_ID, - values: { - settings: {}, + it('renders accordion and accordion item', () => { + expect(findAccordion().props()).toMatchObject({ + headerLevel: 3, + }); + + expect(findAccordionItem().props()).toMatchObject({ + title: 'Extension registry settings', + }); + }); + + it('renders inner form', () => { + expect(findSettingsForm().props()).toEqual({ + initialSettings: {}, + presets: PRESETS, + submitButtonAttrs: { + 'aria-describedby': 'extension-marketplace-settings-error-alert', + loading: false, }, - fields: SettingsApp.FIELDS, }); }); - - it('renders settings textarea', () => { - expect(findTextarea().attributes()).toMatchObject({ - id: 'gl-form-field-testUnique', - value: '{}', - }); - }); - - it('renders save button', () => { - expect(findSaveButton().attributes()).toMatchObject({ - type: 'submit', - variant: 'confirm', - category: 'primary', - 'aria-describedby': 'extensions-marketplace-settings-error-alert', - }); - expect(findSaveButton().props('loading')).toBe(false); - expect(findSaveButton().text()).toBe('Save changes'); - }); }); - describe('when submitted', () => { - beforeEach(async () => { + describe('when enablement toggle is changed', () => { + beforeEach(() => { createComponent(); - findTextarea().vm.$emit('input', JSON.stringify(TEST_NEW_SETTINGS)); - await nextTick(); - - findFormFields().vm.$emit('submit'); + findToggle().vm.$emit('change', true); }); it('triggers loading', () => { - expect(findSaveButton().props('loading')).toBe(true); + expect(findSettingsForm().props('submitButtonAttrs')).toEqual({ + 'aria-describedby': 'extension-marketplace-settings-error-alert', + loading: true, + }); + + expect(findToggle().props()).toMatchObject({ + value: false, + isLoading: true, + }); }); it('makes submit request', () => { expect(submitSpy).toHaveBeenCalledTimes(1); expect(submitSpy).toHaveBeenCalledWith({ - vscode_extension_marketplace: TEST_NEW_SETTINGS, + vscode_extension_marketplace_enabled: true, }); }); it('while loading, prevents extra submit', () => { - findFormFields().vm.$emit('submit'); - findFormFields().vm.$emit('submit'); + findToggle().vm.$emit('change', true); + findToggle().vm.$emit('change', true); expect(submitSpy).toHaveBeenCalledTimes(1); }); @@ -126,7 +139,7 @@ describe('~/vscode_extension_marketplace/components/settings_app.vue', () => { expect(toast).toHaveBeenCalledTimes(1); expect(toast).toHaveBeenCalledWith('Extension marketplace settings updated.'); - expect(findSaveButton().props('loading')).toBe(false); + expect(findToggle().props('isLoading')).toBe(false); }); it('does not show error alert', () => { @@ -134,13 +147,49 @@ describe('~/vscode_extension_marketplace/components/settings_app.vue', () => { }); }); + describe('with initial settings', () => { + beforeEach(() => { + createComponent({ + initialSettings: TEST_INIT_SETTINGS, + }); + }); + + it('initializes settings in toggle', () => { + expect(findToggle().props('value')).toBe(true); + }); + + it('initializes settings in form', () => { + expect(findSettingsForm().props('initialSettings')).toBe(TEST_INIT_SETTINGS); + }); + + it('when submitted, submits settings', async () => { + expect(submitSpy).not.toHaveBeenCalled(); + + findSettingsForm().vm.$emit('submit', TEST_NEW_SETTINGS); + await waitForPromises(); + + expect(submitSpy).toHaveBeenCalledTimes(1); + expect(submitSpy).toHaveBeenCalledWith({ + vscode_extension_marketplace: { + enabled: true, + preset: 'open_vsx', + custom_values: { + item_url: 'abc', + service_url: 'def', + resource_url_template: 'ghi', + }, + }, + }); + }); + }); + describe('when submitted and errored', () => { beforeEach(() => { submitSpy.mockReturnValue([400]); createComponent(); - findFormFields().vm.$emit('submit'); + findSettingsForm().vm.$emit('submit', {}); }); it('shows error message', async () => { @@ -149,7 +198,7 @@ describe('~/vscode_extension_marketplace/components/settings_app.vue', () => { await axios.waitForAll(); expect(findErrorAlert().exists()).toBe(true); - expect(findErrorAlert().attributes('id')).toBe('extensions-marketplace-settings-error-alert'); + expect(findErrorAlert().attributes('id')).toBe('extension-marketplace-settings-error-alert'); expect(findErrorAlert().props('dismissible')).toBe(false); expect(findErrorAlert().text()).toBe( 'Failed to update extension marketplace settings. An unknown error occurred. Please try again.', @@ -168,20 +217,12 @@ describe('~/vscode_extension_marketplace/components/settings_app.vue', () => { ); }); - it('updates state on textarea', async () => { - expect(findTextarea().attributes('state')).toBe('true'); - - await axios.waitForAll(); - - expect(findTextarea().attributes('state')).toBeUndefined(); - }); - it('hides error message with another submit', async () => { await axios.waitForAll(); expect(findErrorAlert().exists()).toBe(true); - findFormFields().vm.$emit('submit'); + findSettingsForm().vm.$emit('submit', TEST_NEW_SETTINGS); await nextTick(); expect(findErrorAlert().exists()).toBe(false); @@ -197,7 +238,7 @@ describe('~/vscode_extension_marketplace/components/settings_app.vue', () => { createComponent(); - findFormFields().vm.$emit('submit'); + findSettingsForm().vm.$emit('submit', TEST_NEW_SETTINGS); }); it('shows error message', async () => { @@ -213,16 +254,4 @@ describe('~/vscode_extension_marketplace/components/settings_app.vue', () => { ]); }); }); - - describe('with initialSettings', () => { - beforeEach(() => { - createComponent({ - initialSettings: TEST_NEW_SETTINGS, - }); - }); - - it('initializes the form with given settings', () => { - expect(findTextarea().props('value')).toBe(JSON.stringify(TEST_NEW_SETTINGS, null, 2)); - }); - }); }); diff --git a/spec/frontend/vscode_extension_marketplace/components/settings_form_spec.js b/spec/frontend/vscode_extension_marketplace/components/settings_form_spec.js new file mode 100644 index 00000000000..a0faca78763 --- /dev/null +++ b/spec/frontend/vscode_extension_marketplace/components/settings_form_spec.js @@ -0,0 +1,240 @@ +import { GlForm, GlFormFields, GlToggle, GlButton } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import SettingsForm from '~/vscode_extension_marketplace/components/settings_form.vue'; +import { PRESETS } from '../mock_data'; + +jest.mock('lodash/uniqueId', () => (x) => `${x}uniqueId`); + +const TEST_FORM_ID = 'extension-marketplace-settings-form-uniqueId'; +const TEST_SUBMIT_BUTTON_ATTRS = { + 'aria-describedby': 'extension-marketplace-settings-error-alert', +}; +const TEST_CUSTOM_VALUES = { + item_url: 'abc', + service_url: 'def', + resource_url_template: 'ghi', +}; + +describe('~/vscode_extension_marketplace/components/settings_form.vue', () => { + let wrapper; + + const findForm = () => wrapper.findComponent(GlForm); + const findFormFields = () => findForm().findComponent(GlFormFields); + const findOpenVsxToggle = () => findFormFields().findComponent(GlToggle); + const findButton = () => findForm().findComponent(GlButton); + + const createComponent = (props = {}) => { + wrapper = shallowMount(SettingsForm, { + propsData: { + presets: PRESETS, + submitButtonAttrs: TEST_SUBMIT_BUTTON_ATTRS, + ...props, + }, + stubs: { + GlFormFields, + }, + }); + }; + + beforeEach(() => { + jest.spyOn(console, 'error').mockImplementation(() => {}); + }); + + describe('default', () => { + beforeEach(() => { + createComponent(); + }); + + it('renders form', () => { + expect(findForm().attributes('id')).toBe(TEST_FORM_ID); + }); + + it('renders form fields', () => { + const expectedInputAttrs = { + readonly: true, + 'aria-description': + 'Disable Open VSX extension registry to set a custom value for this field.', + width: 'lg', + }; + + expect(findFormFields().props()).toEqual({ + formId: TEST_FORM_ID, + serverValidations: {}, + fields: { + useOpenVsx: { + label: 'Use Open VSX extension registry', + }, + presetItemUrl: { + label: 'Item URL', + inputAttrs: expectedInputAttrs, + }, + presetServiceUrl: { + label: 'Service URL', + inputAttrs: expectedInputAttrs, + }, + presetResourceUrlTemplate: { + label: 'Resource URL Template', + inputAttrs: expectedInputAttrs, + }, + }, + values: { + useOpenVsx: true, + presetItemUrl: 'https://open-vsx.org/vscode/item', + presetResourceUrlTemplate: + 'https://open-vsx.org/vscode/unpkg/{publisher}/{name}/{version}/{path}', + presetServiceUrl: 'https://open-vsx.org/vscode/gallery', + }, + }); + }); + + it('renders open vsx toggle', () => { + expect(findOpenVsxToggle().props('value')).toEqual(true); + expect(findOpenVsxToggle().attributes()).toMatchObject({ + id: 'gl-form-field-uniqueId', + label: 'Use Open VSX extension registry', + labelposition: 'hidden', + }); + }); + + it('renders save button', () => { + expect(findButton().attributes()).toMatchObject({ + type: 'submit', + variant: 'confirm', + category: 'primary', + ...TEST_SUBMIT_BUTTON_ATTRS, + }); + }); + }); + + describe('with preset=open_vsx and custom_values', () => { + beforeEach(() => { + createComponent({ + initialSettings: { + custom_values: TEST_CUSTOM_VALUES, + }, + }); + }); + + it('changes values when openvsx is toggled', async () => { + // NOTE: gl-form-fields emits `input` on mount to only include fiels created with + expect(findFormFields().props('values')).toEqual({ + useOpenVsx: true, + presetItemUrl: 'https://open-vsx.org/vscode/item', + presetServiceUrl: 'https://open-vsx.org/vscode/gallery', + presetResourceUrlTemplate: + 'https://open-vsx.org/vscode/unpkg/{publisher}/{name}/{version}/{path}', + }); + + await findOpenVsxToggle().vm.$emit('change', false); + + expect(findFormFields().props('values')).toEqual({ + useOpenVsx: false, + presetItemUrl: 'https://open-vsx.org/vscode/item', + presetServiceUrl: 'https://open-vsx.org/vscode/gallery', + presetResourceUrlTemplate: + 'https://open-vsx.org/vscode/unpkg/{publisher}/{name}/{version}/{path}', + itemUrl: 'abc', + serviceUrl: 'def', + resourceUrlTemplate: 'ghi', + }); + }); + }); + + describe('with preset=custom and custom_values', () => { + beforeEach(() => { + createComponent({ + initialSettings: { + custom_values: TEST_CUSTOM_VALUES, + preset: 'custom', + }, + }); + }); + + it('stores custom values when preset is changed back and forth', async () => { + await findFormFields().vm.$emit('input', { + useOpenVsx: false, + itemUrl: 'xyz', + serviceUrl: 'xyz', + resourceUrlTemplate: 'xyz', + }); + await findOpenVsxToggle().vm.$emit('change', true); + + expect(findFormFields().props('values')).toEqual({ + useOpenVsx: true, + itemUrl: 'xyz', + serviceUrl: 'xyz', + resourceUrlTemplate: 'xyz', + presetItemUrl: 'https://open-vsx.org/vscode/item', + presetServiceUrl: 'https://open-vsx.org/vscode/gallery', + presetResourceUrlTemplate: + 'https://open-vsx.org/vscode/unpkg/{publisher}/{name}/{version}/{path}', + }); + + await findOpenVsxToggle().vm.$emit('change', false); + + expect(findFormFields().props('values')).toEqual({ + useOpenVsx: false, + itemUrl: 'xyz', + serviceUrl: 'xyz', + resourceUrlTemplate: 'xyz', + presetItemUrl: 'https://open-vsx.org/vscode/item', + presetServiceUrl: 'https://open-vsx.org/vscode/gallery', + presetResourceUrlTemplate: + 'https://open-vsx.org/vscode/unpkg/{publisher}/{name}/{version}/{path}', + }); + }); + + it('renders customizable fields', () => { + expect(findFormFields().props('fields')).toEqual( + expect.objectContaining({ + itemUrl: { + label: 'Item URL', + inputAttrs: { + placeholder: 'https://...', + width: 'lg', + }, + validators: expect.any(Array), + }, + serviceUrl: { + label: 'Service URL', + inputAttrs: { + placeholder: 'https://...', + width: 'lg', + }, + validators: expect.any(Array), + }, + resourceUrlTemplate: { + label: 'Resource URL Template', + inputAttrs: { + placeholder: 'https://...', + width: 'lg', + }, + validators: expect.any(Array), + }, + }), + ); + }); + + it.each` + fieldName | value | expectation + ${'itemUrl'} | ${''} | ${'A valid URL is required.'} + ${'itemUrl'} | ${'abc def'} | ${'A valid URL is required.'} + ${'itemUrl'} | ${'https://example.com'} | ${''} + ${'serviceUrl'} | ${''} | ${'A valid URL is required.'} + ${'serviceUrl'} | ${'abc def'} | ${'A valid URL is required.'} + ${'serviceUrl'} | ${'https://example.com'} | ${''} + ${'resourceUrlTemplate'} | ${''} | ${'A valid URL is required.'} + ${'resourceUrlTemplate'} | ${'abc def'} | ${'A valid URL is required.'} + ${'resourceUrlTemplate'} | ${'https://example.com'} | ${''} + `( + 'validates $fieldName where $value is "$expectation"', + ({ fieldName, value, expectation }) => { + const field = findFormFields().props('fields')[fieldName]; + + const result = field.validators.reduce((msg, validator) => msg || validator(value), ''); + + expect(result).toBe(expectation); + }, + ); + }); +}); diff --git a/spec/frontend/vscode_extension_marketplace/mock_data.js b/spec/frontend/vscode_extension_marketplace/mock_data.js new file mode 100644 index 00000000000..5f0ea03fe13 --- /dev/null +++ b/spec/frontend/vscode_extension_marketplace/mock_data.js @@ -0,0 +1,11 @@ +export const PRESETS = [ + { + key: 'open_vsx', + name: 'Open VSX', + values: { + serviceUrl: 'https://open-vsx.org/vscode/gallery', + itemUrl: 'https://open-vsx.org/vscode/item', + resourceUrlTemplate: 'https://open-vsx.org/vscode/unpkg/{publisher}/{name}/{version}/{path}', + }, + }, +]; diff --git a/spec/helpers/application_settings_helper_spec.rb b/spec/helpers/application_settings_helper_spec.rb index 485728c007e..b44344926f7 100644 --- a/spec/helpers/application_settings_helper_spec.rb +++ b/spec/helpers/application_settings_helper_spec.rb @@ -462,11 +462,14 @@ RSpec.describe ApplicationSettingsHelper, feature_category: :shared do context 'with flag on' do it 'returns hash of view properties' do - expect(helper.vscode_extension_marketplace_settings_view).to eq({ + expect(helper.vscode_extension_marketplace_settings_view).to match({ title: _('VS Code Extension Marketplace'), description: _('Enable VS Code Extension Marketplace and configure the extensions registry for Web IDE.'), view_model: { - initialSettings: vscode_extension_marketplace + initialSettings: vscode_extension_marketplace, + presets: [ + hash_including("key" => "open_vsx") + ] } }) end diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index 4d90c8de039..f5397bb73c6 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -70,7 +70,8 @@ RSpec.describe ApplicationSetting, feature_category: :shared, type: :model do it { expect(setting.global_search_merge_requests_enabled).to be(true) } it { expect(setting.global_search_snippet_titles_enabled).to be(true) } it { expect(setting.global_search_users_enabled).to be(true) } - it { expect(setting.vscode_extension_marketplace).to eq({}) } + it { expect(setting.vscode_extension_marketplace).to eq({ "enabled" => false }) } + it { expect(setting.vscode_extension_marketplace_enabled?).to be(false) } it do expect(setting.sign_in_restrictions).to eq({ @@ -1776,7 +1777,7 @@ RSpec.describe ApplicationSetting, feature_category: :shared, type: :model do end end - describe 'vscode_extension_marketplace' do + describe '#vscode_extension_marketplace' do let(:invalid_custom) { { enabled: false, preset: "custom", custom_values: {} } } let(:valid_open_vsx) { { enabled: true, preset: "open_vsx" } } let(:valid_custom) do @@ -1802,6 +1803,24 @@ RSpec.describe ApplicationSetting, feature_category: :shared, type: :model do it { is_expected.not_to allow_value(invalid_custom).for(:vscode_extension_marketplace) } end + describe '#vscode_extension_marketplace_enabled' do + it 'is updated when underlying vscode_extension_marketplace changes' do + expect(setting.vscode_extension_marketplace_enabled).to be(false) + + setting.vscode_extension_marketplace = { enabled: true, preset: "open_vsx" } + + expect(setting.vscode_extension_marketplace_enabled).to be(true) + end + + it 'updates the underlying vscode_extension_marketplace when changed' do + setting.vscode_extension_marketplace = { enabled: true, preset: "open_vsx" } + + setting.vscode_extension_marketplace_enabled = false + + expect(setting.vscode_extension_marketplace).to eq({ "enabled" => false, "preset" => "open_vsx" }) + end + end + describe '#static_objects_external_storage_auth_token=', :aggregate_failures do subject { setting.static_objects_external_storage_auth_token = token } diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb index f0ef531bf83..4026efcdce6 100644 --- a/spec/requests/api/settings_spec.rb +++ b/spec/requests/api/settings_spec.rb @@ -1221,5 +1221,16 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting, featu expect(json_response['resource_usage_limits']).to eq(hash) end end + + context 'with vscode_extension_marketplace_enabled' do + it 'updates underlying vscode_extension_marketplace field' do + put api("/application/settings", admin), + params: { vscode_extension_marketplace_enabled: true } + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['vscode_extension_marketplace_enabled']).to eq(true) + expect(json_response['vscode_extension_marketplace']).to eq({ "enabled" => true }) + end + end end end diff --git a/spec/views/admin/application_settings/_extension_marketplace.html.haml_spec.rb b/spec/views/admin/application_settings/_extension_marketplace.html.haml_spec.rb index e8b32a2305d..c4379ad581d 100644 --- a/spec/views/admin/application_settings/_extension_marketplace.html.haml_spec.rb +++ b/spec/views/admin/application_settings/_extension_marketplace.html.haml_spec.rb @@ -31,7 +31,22 @@ RSpec.describe 'admin/application_settings/_extension_marketplace', feature_cate it 'renders data-view-model for vue app' do vue_app = page.at('#js-extension-marketplace-settings-app') - expected_json = { initialSettings: {} }.to_json + expected_presets = ::WebIde::ExtensionMarketplacePreset.all.map do |x| + { + key: x.key, + name: x.name, + values: { + serviceUrl: x.values[:service_url], + itemUrl: x.values[:item_url], + resourceUrlTemplate: x.values[:resource_url_template] + } + } + end + + expected_json = { + presets: expected_presets, + initialSettings: { enabled: false } + }.to_json expect(vue_app).not_to be_nil expect(vue_app['data-view-model']).to eq(expected_json)