Commit f4d67805 authored by mlunoe's avatar mlunoe Committed by Peter Leitzen

Refactor(profile preferences): integration views

Refactor the integration views of the
profile/preferences page to Vue (from HAML), so
we can achieve these goals:

https://gitlab.com/gitlab-com/www-gitlab-com/-/issues/8765
https://gitlab.com/gitlab-org/gitlab/-/issues/259621
https://gitlab.com/gitlab-org/gitlab/-/issues/250846
parent 3fb22f44
import Vue from 'vue';
import initUserInternalRegexPlaceholder from '../account_and_limits';
import IntegrationHelpText from '~/vue_shared/components/integrations_help_text.vue';
document.addEventListener('DOMContentLoaded', initUserInternalRegexPlaceholder());
document.addEventListener('DOMContentLoaded', () => {
initUserInternalRegexPlaceholder();
const gitpodSettingEl = document.querySelector('#js-gitpod-settings-help-text');
if (!gitpodSettingEl) {
return;
}
// eslint-disable-next-line no-new
new Vue({
el: gitpodSettingEl,
name: 'GitpodSettings',
components: {
IntegrationHelpText,
},
});
});
import initProfilePreferences from '~/profile/preferences/profile_preferences_bundle';
document.addEventListener('DOMContentLoaded', initProfilePreferences);
<script>
import { GlFormText, GlIcon, GlLink } from '@gitlab/ui';
import IntegrationHelpText from '~/vue_shared/components/integrations_help_text.vue';
export default {
name: 'IntegrationView',
components: {
GlFormText,
GlIcon,
GlLink,
IntegrationHelpText,
},
inject: ['userFields'],
props: {
helpLink: {
type: String,
required: true,
},
message: {
type: String,
required: true,
},
messageUrl: {
type: String,
required: true,
},
config: {
type: Object,
required: true,
},
},
data() {
return {
isEnabled: this.userFields[this.config.formName],
};
},
computed: {
formName() {
return `user[${this.config.formName}]`;
},
formId() {
return `user_${this.config.formName}`;
},
},
};
</script>
<template>
<div>
<label class="label-bold">
{{ config.title }}
</label>
<gl-link class="has-tooltip" title="More information" :href="helpLink">
<gl-icon name="question-o" class="vertical-align-middle" />
</gl-link>
<div class="form-group form-check" data-testid="profile-preferences-integration-form-group">
<!-- Necessary for Rails to receive the value when not checked -->
<input
:name="formName"
type="hidden"
value="0"
data-testid="profile-preferences-integration-hidden-field"
/>
<input
:id="formId"
v-model="isEnabled"
type="checkbox"
class="form-check-input"
:name="formName"
value="1"
data-testid="profile-preferences-integration-checkbox"
/>
<label class="form-check-label" :for="formId">
{{ config.label }}
</label>
<gl-form-text tag="div">
<integration-help-text :message="message" :message-url="messageUrl" />
</gl-form-text>
</div>
</div>
</template>
<script>
import { s__ } from '~/locale';
import IntegrationView from './integration_view.vue';
const INTEGRATION_VIEW_CONFIGS = {
sourcegraph: {
title: s__('ProfilePreferences|Sourcegraph'),
label: s__('ProfilePreferences|Enable integrated code intelligence on code views'),
formName: 'sourcegraph_enabled',
},
gitpod: {
title: s__('ProfilePreferences|Gitpod'),
label: s__('ProfilePreferences|Enable Gitpod integration'),
formName: 'gitpod_enabled',
},
};
export default {
name: 'ProfilePreferences',
components: {
IntegrationView,
},
inject: {
integrationViews: {
default: [],
},
},
integrationViewConfigs: INTEGRATION_VIEW_CONFIGS,
};
</script>
<template>
<div class="row gl-mt-3 js-preferences-form">
<div v-if="integrationViews.length" class="col-sm-12">
<hr data-testid="profile-preferences-integrations-rule" />
</div>
<div v-if="integrationViews.length" class="col-lg-4 profile-settings-sidebar">
<h4 class="gl-mt-0" data-testid="profile-preferences-integrations-heading">
{{ s__('ProfilePreferences|Integrations') }}
</h4>
<p>
{{ s__('ProfilePreferences|Customize integrations with third party services.') }}
</p>
</div>
<div v-if="integrationViews.length" class="col-lg-8">
<integration-view
v-for="view in integrationViews"
:key="view.name"
:help-link="view.help_link"
:message="view.message"
:message-url="view.message_url"
:config="$options.integrationViewConfigs[view.name]"
/>
</div>
</div>
</template>
import Vue from 'vue';
import ProfilePreferences from './components/profile_preferences.vue';
export default () => {
const el = document.querySelector('#js-profile-preferences-app');
const shouldParse = ['integrationViews', 'userFields'];
const provide = Object.keys(el.dataset).reduce((memo, key) => {
let value = el.dataset[key];
if (shouldParse.includes(key)) {
value = JSON.parse(value);
}
return { ...memo, [key]: value };
}, {});
return new Vue({
el,
name: 'ProfilePreferencesApp',
provide,
render: createElement => createElement(ProfilePreferences),
});
};
<script>
import { GlIcon, GlLink, GlSprintf } from '@gitlab/ui';
export default {
name: 'IntegrationsHelpText',
components: {
GlIcon,
GlLink,
GlSprintf,
},
props: {
message: {
type: String,
required: true,
},
messageUrl: {
type: String,
required: true,
},
},
};
</script>
<template>
<span>
<gl-sprintf :message="message">
<template #link="{ content }">
<gl-link :href="messageUrl" target="_blank">
{{ content }}
<gl-icon name="external-link" class="gl-vertical-align-middle" :size="12" />
</gl-link>
</template>
</gl-sprintf>
</span>
</template>
......@@ -2,9 +2,6 @@
module GitpodHelper
def gitpod_enable_description
link_start = '<a href="https://gitpod.io/" target="_blank" rel="noopener noreferrer">'.html_safe
link_end = "#{sprite_icon('external-link', size: 12, css_class: 'ml-1 vertical-align-center')}</a>".html_safe
s_('Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab.').html_safe % { link_start: link_start, link_end: link_end }
s_('Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab.')
end
end
......@@ -82,8 +82,8 @@ module PreferencesHelper
def integration_views
[].tap do |views|
views << 'gitpod' if Gitlab::Gitpod.feature_and_settings_enabled?
views << 'sourcegraph' if Gitlab::Sourcegraph.feature_available? && Gitlab::CurrentSettings.sourcegraph_enabled
views << { name: 'gitpod', message: gitpod_enable_description, message_url: 'https://gitpod.io/', help_link: help_page_path('integration/gitpod.md') } if Gitlab::Gitpod.feature_and_settings_enabled?
views << { name: 'sourcegraph', message: sourcegraph_url_message, message_url: Gitlab::CurrentSettings.sourcegraph_url, help_link: help_page_path('user/profile/preferences.md', anchor: 'sourcegraph') } if Gitlab::Sourcegraph.feature_available? && Gitlab::CurrentSettings.sourcegraph_enabled
end
end
......
......@@ -2,20 +2,14 @@
module SourcegraphHelper
def sourcegraph_url_message
link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: Gitlab::CurrentSettings.sourcegraph_url }
link_end = "#{sprite_icon('external-link', size: 12, css_class: 'ml-1 vertical-align-center')}</a>".html_safe
message =
if Gitlab::CurrentSettings.sourcegraph_url_is_com?
s_('SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}.').html_safe
s_('SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}.').html_safe
else
s_('SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}.').html_safe
end
message % { link_start: link_start, link_end: link_end }
s_('SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}.').html_safe
end
def sourcegraph_experimental_message
experimental_message =
if Gitlab::Sourcegraph.feature_conditional?
s_("SourcegraphPreferences|This feature is experimental and currently limited to certain projects.")
elsif Gitlab::CurrentSettings.sourcegraph_public_only
......@@ -23,5 +17,7 @@ module SourcegraphHelper
else
s_("SourcegraphPreferences|This feature is experimental.")
end
"#{message} #{experimental_message}"
end
end
......@@ -8,7 +8,7 @@
%button.btn.btn-default.js-settings-toggle{ type: 'button' }
= expanded ? _('Collapse') : _('Expand')
%p
= gitpod_enable_description
%integration-help-text{ "id" => "js-gitpod-settings-help-text", "message" => gitpod_enable_description, "message-url" => "https://gitpod.io/" }
= link_to sprite_icon('question-o'), help_page_path('integration/gitpod.md'), target: '_blank', class: 'has-tooltip', title: _('More information')
......
%label.label-bold#gitpod
= s_('Gitpod')
= link_to sprite_icon('question-o'), help_page_path('integration/gitpod.md'), target: '_blank', class: 'has-tooltip', title: _('More information')
.form-group.form-check
= f.check_box :gitpod_enabled, class: 'form-check-input'
= f.label :gitpod_enabled, class: 'form-check-label' do
= s_('Gitpod|Enable Gitpod integration').html_safe
.form-text.text-muted
= gitpod_enable_description
- views = integration_views
- return unless views.any?
.col-sm-12
%hr
.col-lg-4.profile-settings-sidebar#integrations
%h4.gl-mt-0
= s_('Preferences|Integrations')
%p
= s_('Preferences|Customize integrations with third party services.')
= succeed '.' do
= link_to _('Learn more'), help_page_path('user/profile/preferences.md', anchor: 'integrations'), target: '_blank'
.col-lg-8
- views.each do |view|
= render view, f: f
%label.label-bold
= s_('Preferences|Sourcegraph')
= link_to sprite_icon('question-o'), help_page_path('user/profile/preferences.md', anchor: 'sourcegraph'), target: '_blank', class: 'has-tooltip', title: _('More information')
.form-group.form-check
= f.check_box :sourcegraph_enabled, class: 'form-check-input'
= f.label :sourcegraph_enabled, class: 'form-check-label' do
= s_('Preferences|Enable integrated code intelligence on code views').html_safe
.form-text.text-muted
= sourcegraph_url_message
= sourcegraph_experimental_message
- page_title _('Preferences')
- @content_class = "limit-container-width" unless fluid_layout
- user_fields = { gitpod_enabled: @user.gitpod_enabled, sourcegraph_enabled: @user.sourcegraph_enabled }
- user_theme_id = Gitlab::Themes.for_user(@user).id
- data_attributes = { integration_views: integration_views.to_json, user_fields: user_fields.to_json }
- Gitlab::Themes.each do |theme|
= stylesheet_link_tag "themes/#{theme.css_filename}" if theme.css_filename
= form_for @user, url: profile_preferences_path, remote: true, method: :put, html: { class: 'row gl-mt-3 js-preferences-form' } do |f|
= form_for @user, url: profile_preferences_path, remote: true, method: :put do |f|
.row.gl-mt-3.js-preferences-form
.col-lg-4.application-theme#navigation-theme
%h4.gl-mt-0
= s_('Preferences|Navigation theme')
......@@ -15,7 +19,7 @@
- Gitlab::Themes.each do |theme|
%label.col-6.col-sm-4.col-md-3.gl-mb-5.gl-text-center
.preview{ class: theme.css_class }
= f.radio_button :theme_id, theme.id, checked: Gitlab::Themes.for_user(@user).id == theme.id
= f.radio_button :theme_id, theme.id, checked: user_theme_id == theme.id
= theme.name
.col-sm-12
......@@ -138,8 +142,9 @@
.form-text.text-muted
= s_('Preferences|For example: 30 mins ago.')
= render 'integrations', f: f
#js-profile-preferences-app{ data: data_attributes, user_fields: user_fields.to_json }
.row.gl-mt-3.js-preferences-form
.col-lg-4.profile-settings-sidebar
.col-lg-8
.form-group
......
......@@ -10110,7 +10110,7 @@ msgstr ""
msgid "Enable"
msgstr ""
msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
......@@ -20310,18 +20310,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
msgid "Preferences|Customize integrations with third party services."
msgstr ""
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
msgid "Preferences|Enable integrated code intelligence on code views"
msgstr ""
msgid "Preferences|For example: 30 mins ago."
msgstr ""
......@@ -20331,9 +20325,6 @@ msgstr ""
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
msgid "Preferences|Integrations"
msgstr ""
msgid "Preferences|Layout width"
msgstr ""
......@@ -20355,9 +20346,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
msgid "Preferences|Sourcegraph"
msgstr ""
msgid "Preferences|Syntax highlighting theme"
msgstr ""
......@@ -20544,6 +20532,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
msgid "ProfilePreferences|Customize integrations with third party services."
msgstr ""
msgid "ProfilePreferences|Enable Gitpod integration"
msgstr ""
msgid "ProfilePreferences|Enable integrated code intelligence on code views"
msgstr ""
msgid "ProfilePreferences|Gitpod"
msgstr ""
msgid "ProfilePreferences|Integrations"
msgstr ""
msgid "ProfilePreferences|Sourcegraph"
msgstr ""
msgid "ProfileSession|on"
msgstr ""
......@@ -25590,10 +25596,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`IntegrationView component should render IntegrationView properly 1`] = `
<div
name="sourcegraph"
>
<label
class="label-bold"
>
Foo
</label>
<gl-link-stub
class="has-tooltip"
href="http://foo.com/help"
title="More information"
>
<gl-icon-stub
class="vertical-align-middle"
name="question-o"
size="16"
/>
</gl-link-stub>
<div
class="form-group form-check"
data-testid="profile-preferences-integration-form-group"
>
<input
data-testid="profile-preferences-integration-hidden-field"
name="user[foo_enabled]"
type="hidden"
value="0"
/>
<input
class="form-check-input"
data-testid="profile-preferences-integration-checkbox"
id="user_foo_enabled"
name="user[foo_enabled]"
type="checkbox"
value="1"
/>
<label
class="form-check-label"
for="user_foo_enabled"
>
Enable foo
</label>
<gl-form-text-stub
tag="div"
textvariant="muted"
>
<integration-help-text-stub
message="Click %{linkStart}Foo%{linkEnd}!"
messageurl="http://foo.com"
/>
</gl-form-text-stub>
</div>
</div>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ProfilePreferences component should render ProfilePreferences properly 1`] = `
<div
class="row gl-mt-3 js-preferences-form"
>
<div
class="col-sm-12"
>
<hr
data-testid="profile-preferences-integrations-rule"
/>
</div>
<div
class="col-lg-4 profile-settings-sidebar"
>
<h4
class="gl-mt-0"
data-testid="profile-preferences-integrations-heading"
>
Integrations
</h4>
<p>
Customize integrations with third party services.
</p>
</div>
<div
class="col-lg-8"
>
<integration-view-stub
config="[object Object]"
helplink="http://foo.com/help"
message="Click %{linkStart}Foo%{linkEnd}!"
messageurl="http://foo.com"
/>
<integration-view-stub
config="[object Object]"
helplink="http://bar.com/help"
message="Click %{linkStart}Bar%{linkEnd}!"
messageurl="http://bar.com"
/>
</div>
</div>
`;
import { shallowMount } from '@vue/test-utils';
import { GlFormText } from '@gitlab/ui';
import IntegrationView from '~/profile/preferences/components/integration_view.vue';
import IntegrationHelpText from '~/vue_shared/components/integrations_help_text.vue';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { integrationViews, userFields } from '../mock_data';
const viewProps = convertObjectPropsToCamelCase(integrationViews[0]);
describe('IntegrationView component', () => {
let wrapper;
const defaultProps = {
config: {
title: 'Foo',
label: 'Enable foo',
formName: 'foo_enabled',
},
...viewProps,
};
function createComponent(options = {}) {
const { props = {}, provide = {} } = options;
return shallowMount(IntegrationView, {
provide: {
userFields,
...provide,
},
propsData: {
...defaultProps,
...props,
},
});
}
function findCheckbox() {
return wrapper.find('[data-testid="profile-preferences-integration-checkbox"]');
}
function findFormGroup() {
return wrapper.find('[data-testid="profile-preferences-integration-form-group"]');
}
function findHiddenField() {
return wrapper.find('[data-testid="profile-preferences-integration-hidden-field"]');
}
function findFormGroupLabel() {
return wrapper.find('[data-testid="profile-preferences-integration-form-group"] label');
}
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('should render the title correctly', () => {
wrapper = createComponent();
expect(wrapper.find('label.label-bold').text()).toBe('Foo');
});
it('should render the form correctly', () => {
wrapper = createComponent();
expect(findFormGroup().exists()).toBe(true);
expect(findHiddenField().exists()).toBe(true);
expect(findCheckbox().exists()).toBe(true);
expect(findCheckbox().attributes('id')).toBe('user_foo_enabled');
expect(findCheckbox().attributes('name')).toBe('user[foo_enabled]');
});
it('should have the checkbox value to be set to 1', () => {
wrapper = createComponent();
expect(findCheckbox().attributes('value')).toBe('1');
});
it('should have the hidden value to be set to 0', () => {
wrapper = createComponent();
expect(findHiddenField().attributes('value')).toBe('0');
});
it('should set the checkbox value to be true', () => {
wrapper = createComponent();
expect(findCheckbox().element.checked).toBe(true);
});
it('should set the checkbox value to be false when false is provided', () => {
wrapper = createComponent({
provide: {
userFields: {
foo_enabled: false,
},
},
});
expect(findCheckbox().element.checked).toBe(false);
});
it('should set the checkbox value to be false when not provided', () => {
wrapper = createComponent({ provide: { userFields: {} } });
expect(findCheckbox().element.checked).toBe(false);
});
it('should render the help text', () => {
wrapper = createComponent();
expect(wrapper.find(GlFormText).exists()).toBe(true);
expect(wrapper.find(IntegrationHelpText).exists()).toBe(true);
});
it('should render the label correctly', () => {
wrapper = createComponent();
expect(findFormGroupLabel().text()).toBe('Enable foo');
});
it('should render IntegrationView properly', () => {
wrapper = createComponent();
expect(wrapper.element).toMatchSnapshot();
});
});
import { shallowMount } from '@vue/test-utils';
import ProfilePreferences from '~/profile/preferences/components/profile_preferences.vue';
import IntegrationView from '~/profile/preferences/components/integration_view.vue';
import { integrationViews, userFields } from '../mock_data';
describe('ProfilePreferences component', () => {
let wrapper;
const defaultProvide = {
integrationViews: [],
userFields,
};
function createComponent(options = {}) {
const { props = {}, provide = {} } = options;
return shallowMount(ProfilePreferences, {
provide: {
...defaultProvide,
...provide,
},
propsData: props,
});
}
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('should not render Integrations section', () => {
wrapper = createComponent();
const views = wrapper.findAll(IntegrationView);
const divider = wrapper.find('[data-testid="profile-preferences-integrations-rule"]');
const heading = wrapper.find('[data-testid="profile-preferences-integrations-heading"]');
expect(divider.exists()).toBe(false);
expect(heading.exists()).toBe(false);
expect(views).toHaveLength(0);
});
it('should render Integration section', () => {
wrapper = createComponent({ provide: { integrationViews } });
const divider = wrapper.find('[data-testid="profile-preferences-integrations-rule"]');
const heading = wrapper.find('[data-testid="profile-preferences-integrations-heading"]');
const views = wrapper.findAll(IntegrationView);
expect(divider.exists()).toBe(true);
expect(heading.exists()).toBe(true);
expect(views).toHaveLength(integrationViews.length);
});
it('should render ProfilePreferences properly', () => {
wrapper = createComponent({ provide: { integrationViews } });
expect(wrapper.element).toMatchSnapshot();
});
});
export const integrationViews = [
{
name: 'sourcegraph',
help_link: 'http://foo.com/help',
message: 'Click %{linkStart}Foo%{linkEnd}!',
message_url: 'http://foo.com',
},
{
name: 'gitpod',
help_link: 'http://bar.com/help',
message: 'Click %{linkStart}Bar%{linkEnd}!',
message_url: 'http://bar.com',
},
];
export const userFields = {
foo_enabled: true,
};
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`IntegrationHelpText component should not render the link when start and end is not provided 1`] = `
<span>
Click nowhere!
</span>
`;
exports[`IntegrationHelpText component should render the help text 1`] = `
<span>
Click
<gl-link-stub
href="http://bar.com"
target="_blank"
>
Bar
<gl-icon-stub
class="gl-vertical-align-middle"
name="external-link"
size="12"
/>
</gl-link-stub>
!
</span>
`;
import { shallowMount } from '@vue/test-utils';
import { GlIcon, GlLink, GlSprintf } from '@gitlab/ui';
import IntegrationHelpText from '~/vue_shared/components/integrations_help_text.vue';
describe('IntegrationHelpText component', () => {
let wrapper;
const defaultProps = {
message: 'Click %{linkStart}Bar%{linkEnd}!',
messageUrl: 'http://bar.com',
};
function createComponent(props = {}) {
return shallowMount(IntegrationHelpText, {
propsData: {
...defaultProps,
...props,
},
stubs: {
GlSprintf,
},
});
}
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('should use the gl components', () => {
wrapper = createComponent();
expect(wrapper.find(GlSprintf).exists()).toBe(true);
expect(wrapper.find(GlIcon).exists()).toBe(true);
expect(wrapper.find(GlLink).exists()).toBe(true);
});
it('should render the help text', () => {
wrapper = createComponent();
expect(wrapper.element).toMatchSnapshot();
});
it('should not use the gl-link and gl-icon components', () => {
wrapper = createComponent({ message: 'Click nowhere!' });
expect(wrapper.find(GlSprintf).exists()).toBe(true);
expect(wrapper.find(GlIcon).exists()).toBe(false);
expect(wrapper.find(GlLink).exists()).toBe(false);
});
it('should not render the link when start and end is not provided', () => {
wrapper = createComponent({ message: 'Click nowhere!' });
expect(wrapper.element).toMatchSnapshot();
});
});
......@@ -5,60 +5,43 @@ require 'spec_helper'
RSpec.describe SourcegraphHelper do
describe '#sourcegraph_url_message' do
let(:sourcegraph_url) { 'http://sourcegraph.example.com' }
let(:feature_conditional) { false }
let(:public_only) { false }
let(:is_com) { true }
before do
allow(Gitlab::CurrentSettings).to receive(:sourcegraph_url).and_return(sourcegraph_url)
allow(Gitlab::CurrentSettings).to receive(:sourcegraph_url_is_com?).and_return(is_com)
allow(Gitlab::CurrentSettings).to receive(:sourcegraph_public_only).and_return(public_only)
allow(Gitlab::Sourcegraph).to receive(:feature_conditional?).and_return(feature_conditional)
end
subject { helper.sourcegraph_url_message }
context 'with .com sourcegraph url' do
let(:is_com) { true }
it { is_expected.to have_text('Uses Sourcegraph.com') }
it { is_expected.to have_link('Sourcegraph.com', href: sourcegraph_url) }
it { is_expected.to have_text('Uses %{linkStart}Sourcegraph.com%{linkEnd}. This feature is experimental.') }
end
context 'with custom sourcegraph url' do
let(:is_com) { false }
it { is_expected.to have_text('Uses a custom Sourcegraph instance') }
it { is_expected.to have_link('Sourcegraph instance', href: sourcegraph_url) }
context 'with unsafe url' do
let(:sourcegraph_url) { '\" onload=\"alert(1);\"' }
it { is_expected.to have_link('Sourcegraph instance', href: sourcegraph_url) }
end
end
end
describe '#sourcegraph_experimental_message' do
let(:feature_conditional) { false }
let(:public_only) { false }
before do
allow(Gitlab::CurrentSettings).to receive(:sourcegraph_public_only).and_return(public_only)
allow(Gitlab::Sourcegraph).to receive(:feature_conditional?).and_return(feature_conditional)
it { is_expected.to have_text('Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}. This feature is experimental.') }
end
subject { helper.sourcegraph_experimental_message }
context 'when not limited by feature or public only' do
it { is_expected.to eq "This feature is experimental." }
it { is_expected.to eq 'Uses %{linkStart}Sourcegraph.com%{linkEnd}. This feature is experimental.' }
end
context 'when limited by feature' do
let(:feature_conditional) { true }
it { is_expected.to eq "This feature is experimental and currently limited to certain projects." }
it { is_expected.to eq 'Uses %{linkStart}Sourcegraph.com%{linkEnd}. This feature is experimental and currently limited to certain projects.' }
end
context 'when limited by public only' do
let(:public_only) { true }
it { is_expected.to eq "This feature is experimental and limited to public projects." }
it { is_expected.to eq 'Uses %{linkStart}Sourcegraph.com%{linkEnd}. This feature is experimental and limited to public projects.' }
end
end
end
......@@ -68,61 +68,4 @@ RSpec.describe 'profiles/preferences/show' do
expect(rendered).to have_css('#localization')
end
end
context 'sourcegraph' do
def have_sourcegraph_field(*args)
have_field('user_sourcegraph_enabled', *args)
end
def have_integrations_section
have_css('#integrations.profile-settings-sidebar', text: 'Integrations')
end
before do
stub_feature_flags(sourcegraph: sourcegraph_feature)
stub_application_setting(sourcegraph_enabled: sourcegraph_enabled)
end
context 'when not fully enabled' do
where(:feature, :admin_enabled) do
false | false
false | true
true | false
end
with_them do
let(:sourcegraph_feature) { feature }
let(:sourcegraph_enabled) { admin_enabled }
before do
render
end
it 'does not display sourcegraph field' do
expect(rendered).not_to have_sourcegraph_field
end
it 'does not display Integration Settings' do
expect(rendered).not_to have_integrations_section
end
end
end
context 'when fully enabled' do
let(:sourcegraph_feature) { true }
let(:sourcegraph_enabled) { true }
before do
render
end
it 'displays the sourcegraph field' do
expect(rendered).to have_sourcegraph_field
end
it 'displays the integrations section' do
expect(rendered).to have_integrations_section
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment