Commit 5adb5a11 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents ae2088bc e51a3eae
<script> <script>
import { GlLink, GlLoadingIcon, GlPagination, GlTable } from '@gitlab/ui'; import { GlLink, GlLoadingIcon, GlPagination, GlTable, GlAlert } from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import { DEFAULT_PER_PAGE } from '~/api'; import { DEFAULT_PER_PAGE } from '~/api';
import createFlash from '~/flash';
import { fetchOverrides } from '~/integrations/overrides/api'; import { fetchOverrides } from '~/integrations/overrides/api';
import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils'; import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils';
import { truncateNamespace } from '~/lib/utils/text_utility'; import { truncateNamespace } from '~/lib/utils/text_utility';
...@@ -16,6 +16,7 @@ export default { ...@@ -16,6 +16,7 @@ export default {
GlLoadingIcon, GlLoadingIcon,
GlPagination, GlPagination,
GlTable, GlTable,
GlAlert,
ProjectAvatar, ProjectAvatar,
}, },
props: { props: {
...@@ -36,6 +37,7 @@ export default { ...@@ -36,6 +37,7 @@ export default {
overrides: [], overrides: [],
page: 1, page: 1,
totalItems: 0, totalItems: 0,
errorMessage: null,
}; };
}, },
computed: { computed: {
...@@ -49,6 +51,7 @@ export default { ...@@ -49,6 +51,7 @@ export default {
methods: { methods: {
loadOverrides(page = this.page) { loadOverrides(page = this.page) {
this.isLoading = true; this.isLoading = true;
this.errorMessage = null;
fetchOverrides(this.overridesPath, { fetchOverrides(this.overridesPath, {
page, page,
...@@ -61,11 +64,9 @@ export default { ...@@ -61,11 +64,9 @@ export default {
this.overrides = data; this.overrides = data;
}) })
.catch((error) => { .catch((error) => {
createFlash({ this.errorMessage = this.$options.i18n.defaultErrorMessage;
message: this.$options.i18n.defaultErrorMessage,
error, Sentry.captureException(error);
captureError: true,
});
}) })
.finally(() => { .finally(() => {
this.isLoading = false; this.isLoading = false;
...@@ -85,7 +86,11 @@ export default { ...@@ -85,7 +86,11 @@ export default {
<template> <template>
<div> <div>
<gl-alert v-if="errorMessage" variant="danger" :dismissible="false">
{{ errorMessage }}
</gl-alert>
<gl-table <gl-table
v-else
:items="overrides" :items="overrides"
:fields="$options.fields" :fields="$options.fields"
:busy="isLoading" :busy="isLoading"
......
...@@ -206,6 +206,12 @@ ...@@ -206,6 +206,12 @@
padding: 0; padding: 0;
height: 100%; height: 100%;
&.with-system-header {
.login-page-broadcast {
margin-top: $system-header-height + $header-height;
}
}
// Fixes footer container to bottom of viewport // Fixes footer container to bottom of viewport
body { body {
// offset height of fixed header + 1 to avoid scroll // offset height of fixed header + 1 to avoid scroll
......
...@@ -2,10 +2,6 @@ ...@@ -2,10 +2,6 @@
- root_group = group.root_ancestor - root_group = group.root_ancestor
- return unless show_trial_status_widget?(root_group) - return unless show_trial_status_widget?(root_group)
-# For the current Growth::Conversion experiment
- record_experiment_group(:show_trial_status_in_sidebar, root_group)
- return unless experiment_enabled?(:show_trial_status_in_sidebar, subject: root_group)
= nav_link do = nav_link do
#js-trial-status-widget{ data: trial_status_widget_data_attrs(root_group) } #js-trial-status-widget{ data: trial_status_widget_data_attrs(root_group) }
#js-trial-status-popover{ data: trial_status_popover_data_attrs(root_group) } #js-trial-status-popover{ data: trial_status_popover_data_attrs(root_group) }
---
name: show_trial_status_in_sidebar_experiment_percentage
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50090
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/281019
milestone: '13.8'
type: experiment
group: group::conversion
default_enabled: false
...@@ -15,12 +15,17 @@ RSpec.describe 'Billing plan pages', :feature, :js do ...@@ -15,12 +15,17 @@ RSpec.describe 'Billing plan pages', :feature, :js do
let(:plans_data) { billing_plans_data } let(:plans_data) { billing_plans_data }
before do before do
stub_application_setting(check_namespace_plan: true)
stub_feature_flags(show_billing_eoa_banner: true) stub_feature_flags(show_billing_eoa_banner: true)
stub_feature_flags(hide_deprecated_billing_plans: false) stub_feature_flags(hide_deprecated_billing_plans: false)
stub_billing_plans(nil)
stub_billing_plans(namespace.id, plan.name, plans_data.to_json) stub_billing_plans(namespace.id, plan.name, plans_data.to_json)
stub_eoa_eligibility_request(namespace.id) stub_eoa_eligibility_request(namespace.id)
stub_application_setting(check_namespace_plan: true)
allow(Gitlab).to receive(:com?) { true } allow(Gitlab).to receive(:com?) { true }
sign_in(user) sign_in(user)
end end
...@@ -412,8 +417,11 @@ RSpec.describe 'Billing plan pages', :feature, :js do ...@@ -412,8 +417,11 @@ RSpec.describe 'Billing plan pages', :feature, :js do
let(:plan) { free_plan } let(:plan) { free_plan }
let!(:subscription) do let!(:subscription) do
create(:gitlab_subscription, namespace: namespace, hosted_plan: premium_plan, create(:gitlab_subscription, :active_trial,
trial: true, trial_ends_on: Date.current.tomorrow, seats: 15) namespace: namespace,
hosted_plan: premium_plan,
seats: 15
)
end end
before do before do
......
...@@ -23,6 +23,7 @@ RSpec.describe 'Billings > Extend / Reactivate Trial', :js do ...@@ -23,6 +23,7 @@ RSpec.describe 'Billings > Extend / Reactivate Trial', :js do
allow(Gitlab).to receive(:com?).and_return(true) allow(Gitlab).to receive(:com?).and_return(true)
stub_ee_application_setting(should_check_namespace_plan: true) stub_ee_application_setting(should_check_namespace_plan: true)
stub_feature_flags(allow_extend_reactivate_trial: true) stub_feature_flags(allow_extend_reactivate_trial: true)
stub_billing_plans(nil)
stub_full_request("#{EE::SUBSCRIPTIONS_URL}/gitlab_plans?plan=#{plan.name}&namespace_id=#{group.id}") stub_full_request("#{EE::SUBSCRIPTIONS_URL}/gitlab_plans?plan=#{plan.name}&namespace_id=#{group.id}")
.to_return(status: 200, body: plans_data.to_json) .to_return(status: 200, body: plans_data.to_json)
stub_full_request("#{EE::SUBSCRIPTIONS_URL}/trials/extend_reactivate_trial", method: :put) stub_full_request("#{EE::SUBSCRIPTIONS_URL}/trials/extend_reactivate_trial", method: :put)
......
...@@ -3,37 +3,40 @@ ...@@ -3,37 +3,40 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Show trial banner', :js do RSpec.describe 'Show trial banner', :js do
include StubRequests
include SubscriptionPortalHelpers include SubscriptionPortalHelpers
let!(:user) { create(:user) } let_it_be(:user) { create(:user) }
let!(:group) { create(:group) } let_it_be(:group) { create(:group) }
let!(:ultimate_plan) { create(:ultimate_plan) }
let(:ultimate_plan) { create(:ultimate_plan) }
before do before do
stub_application_setting(check_namespace_plan: true) stub_application_setting(check_namespace_plan: true)
allow(Gitlab).to receive(:com?).and_return(true).at_least(:once) allow(Gitlab).to receive(:com?).and_return(true).at_least(:once)
stub_billing_plans(namespace_id) stub_billing_plans(nil)
group.add_owner(user)
create(:gitlab_subscription, namespace: user.namespace, hosted_plan: ultimate_plan, trial: true, trial_ends_on: Date.current + 1.month)
create(:gitlab_subscription, namespace: group, hosted_plan: ultimate_plan, trial: true, trial_ends_on: Date.current + 1.month)
gitlab_sign_in(user) sign_in(user)
end end
context "when user's trial is active" do context "when user's trial is active" do
let(:namespace_id) { user.namespace_id } before do
create(:gitlab_subscription, :active_trial, namespace: user.namespace, hosted_plan: ultimate_plan)
stub_billing_plans(user.namespace_id)
end
it 'renders congratulations banner for user in profile billing page' do it 'renders congratulations banner for user in profile billing page' do
visit profile_billings_path + '?trial=true' visit profile_billings_path(trial: true)
expect(page).to have_content('Congratulations, your free trial is activated.') expect(page).to have_content('Congratulations, your free trial is activated.')
end end
end end
context "when group's trial is active" do context "when group's trial is active" do
let(:namespace_id) { group.id } before do
group.add_owner(user)
create(:gitlab_subscription, :active_trial, namespace: group, hosted_plan: ultimate_plan)
stub_billing_plans(group.id)
end
it 'renders congratulations banner for group in group details page' do it 'renders congratulations banner for group in group details page' do
visit group_path(group, trial: true) visit group_path(group, trial: true)
......
...@@ -3,75 +3,37 @@ ...@@ -3,75 +3,37 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'layouts/nav/sidebar/_group' do RSpec.describe 'layouts/nav/sidebar/_group' do
let_it_be_with_refind(:group) { create(:group) }
let_it_be(:user) { create(:user) }
before do before do
assign(:group, group) assign(:group, group)
allow(view).to receive(:show_trial_status_widget?).and_return(false) allow(view).to receive(:show_trial_status_widget?).and_return(false)
end end
let(:group) { create(:group) }
let(:user) { create(:user) }
describe 'trial status widget', :aggregate_failures do describe 'trial status widget', :aggregate_failures do
using RSpec::Parameterized::TableSyntax
let(:experiment_key) { :show_trial_status_in_sidebar }
let(:show_widget) { false }
let(:experiment_enabled) { false }
before do
allow(view).to receive(:show_trial_status_widget?).and_return(show_widget)
allow(view).to receive(:experiment_enabled?).with(experiment_key, subject: group).and_return(experiment_enabled)
allow(view).to receive(:record_experiment_group)
allow(view).to receive(:trial_status_widget_data_attrs)
allow(view).to receive(:trial_status_popover_data_attrs)
render
end
subject do subject do
render render
rendered rendered
end end
shared_examples 'does not render the widget & popover' do context 'when the widget should not be shown' do
it 'does not render' do it 'does not render the widget & popover' do
is_expected.not_to have_selector '#js-trial-status-widget' is_expected.not_to have_selector '#js-trial-status-widget'
is_expected.not_to have_selector '#js-trial-status-popover' is_expected.not_to have_selector '#js-trial-status-popover'
end end
end end
shared_examples 'renders the widget & popover' do context 'when the widget should be shown' do
it 'renders both the widget & popover component initialization elements' do before do
is_expected.to have_selector '#js-trial-status-widget' allow(view).to receive(:show_trial_status_widget?).and_return(true)
is_expected.to have_selector '#js-trial-status-popover' allow(view).to receive(:trial_status_widget_data_attrs).and_return({ foo: 'bar' })
end allow(view).to receive(:trial_status_popover_data_attrs).and_return({ baz: 'quux' })
end
shared_examples 'does record experiment subject' do
it 'records the group as an experiment subject' do
expect(view).to receive(:record_experiment_group).with(experiment_key, group)
subject
end
end
shared_examples 'does not record experiment subject' do
it 'does not record the group as an experiment subject' do
expect(view).not_to receive(:record_experiment_group)
subject
end
end
where :show_widget, :experiment_enabled, :examples_to_include do
true | true | ['does record experiment subject', 'renders the widget & popover']
true | false | ['does record experiment subject', 'does not render the widget & popover']
false | true | ['does not record experiment subject', 'does not render the widget & popover']
false | false | ['does not record experiment subject', 'does not render the widget & popover']
end end
with_them do it 'renders both the widget & popover component initialization elements' do
params[:examples_to_include].each do |example_set| is_expected.to have_selector '#js-trial-status-widget[data-foo="bar"]'
include_examples(example_set) is_expected.to have_selector '#js-trial-status-popover[data-baz="quux"]'
end end
end end
end end
......
...@@ -40,10 +40,6 @@ module Gitlab ...@@ -40,10 +40,6 @@ module Gitlab
}, },
invite_members_new_dropdown: { invite_members_new_dropdown: {
tracking_category: 'Growth::Expansion::Experiment::InviteMembersNewDropdown' tracking_category: 'Growth::Expansion::Experiment::InviteMembersNewDropdown'
},
show_trial_status_in_sidebar: {
tracking_category: 'Growth::Conversion::Experiment::ShowTrialStatusInSidebar',
rollout_strategy: :group
} }
}.freeze }.freeze
......
...@@ -46,21 +46,14 @@ class GithubImport ...@@ -46,21 +46,14 @@ class GithubImport
def import! def import!
@project.import_state.force_start @project.import_state.force_start
import_success = false
timings = Benchmark.measure do timings = Benchmark.measure do
import_success = Gitlab::GithubImport::SequentialImporter Gitlab::GithubImport::SequentialImporter
.new(@project, token: @options[:token]) .new(@project, token: @options[:token])
.execute .execute
end end
if import_success
@project.after_import @project.after_import
puts "Import finished. Timings: #{timings}".color(:green) puts "Import finished. Timings: #{timings}".color(:green)
else
puts "Import was not successful. Errors were as follows:"
puts @project.import_state.last_error
end
end end
def new_project def new_project
......
import { GlTable, GlLink, GlPagination } from '@gitlab/ui'; import { GlTable, GlLink, GlPagination, GlAlert } from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import { shallowMount, mount } from '@vue/test-utils'; import { shallowMount, mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import { DEFAULT_PER_PAGE } from '~/api'; import { DEFAULT_PER_PAGE } from '~/api';
import createFlash from '~/flash';
import IntegrationOverrides from '~/integrations/overrides/components/integration_overrides.vue'; import IntegrationOverrides from '~/integrations/overrides/components/integration_overrides.vue';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import httpStatus from '~/lib/utils/http_status'; import httpStatus from '~/lib/utils/http_status';
import ProjectAvatar from '~/vue_shared/components/project_avatar.vue'; import ProjectAvatar from '~/vue_shared/components/project_avatar.vue';
jest.mock('~/flash');
const mockOverrides = Array(DEFAULT_PER_PAGE * 3) const mockOverrides = Array(DEFAULT_PER_PAGE * 3)
.fill(1) .fill(1)
.map((_, index) => ({ .map((_, index) => ({
...@@ -62,6 +60,7 @@ describe('IntegrationOverrides', () => { ...@@ -62,6 +60,7 @@ describe('IntegrationOverrides', () => {
text: link.text(), text: link.text(),
}; };
}); });
const findAlert = () => wrapper.findComponent(GlAlert);
describe('while loading', () => { describe('while loading', () => {
it('sets GlTable `busy` attribute to `true`', () => { it('sets GlTable `busy` attribute to `true`', () => {
...@@ -104,18 +103,26 @@ describe('IntegrationOverrides', () => { ...@@ -104,18 +103,26 @@ describe('IntegrationOverrides', () => {
describe('when request fails', () => { describe('when request fails', () => {
beforeEach(async () => { beforeEach(async () => {
jest.spyOn(Sentry, 'captureException');
mockAxios.onGet(defaultProps.overridesPath).reply(httpStatus.INTERNAL_SERVER_ERROR); mockAxios.onGet(defaultProps.overridesPath).reply(httpStatus.INTERNAL_SERVER_ERROR);
createComponent(); createComponent();
await waitForPromises(); await waitForPromises();
}); });
it('calls createFlash', () => { it('displays error alert', () => {
expect(createFlash).toHaveBeenCalledTimes(1); const alert = findAlert();
expect(createFlash).toHaveBeenCalledWith({ expect(alert.exists()).toBe(true);
message: IntegrationOverrides.i18n.defaultErrorMessage, expect(alert.text()).toBe(IntegrationOverrides.i18n.defaultErrorMessage);
captureError: true, });
error: expect.any(Error),
it('hides overrides table', () => {
const table = findGlTable();
expect(table.exists()).toBe(false);
}); });
it('captures exception in Sentry', () => {
expect(Sentry.captureException).toHaveBeenCalledWith(expect.any(Error));
}); });
}); });
......
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