Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-06-06 00:09:04 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-06-06 00:09:04 +0300
commit96e23b2017cbe56969771960f6c274c5d3599397 (patch)
treeb8b17da1ab080dd41fc64fc0262de2cf16754559 /spec
parent2f1a81fd16ff9968d6b986f8a407d963bc2218f9 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/projects/environments_controller_spec.rb405
-rw-r--r--spec/controllers/projects/settings/operations_controller_spec.rb48
-rw-r--r--spec/features/markdown/markdown_spec.rb8
-rw-r--r--spec/features/merge_request/user_comments_on_whitespace_hidden_diff_spec.rb39
-rw-r--r--spec/features/project_group_variables_spec.rb140
-rw-r--r--spec/features/projects/releases/user_views_releases_spec.rb15
-rw-r--r--spec/frontend/behaviors/markdown/utils_spec.js18
-rw-r--r--spec/frontend/ci/ci_variable_list/components/ci_variable_shared_spec.js1
-rw-r--r--spec/frontend/ci/ci_variable_list/components/ci_variable_table_spec.js76
-rw-r--r--spec/frontend/ci/ci_variable_list/mocks.js39
-rw-r--r--spec/frontend/ci/inherited_ci_variables/components/inherited_ci_variables_app_spec.js114
-rw-r--r--spec/frontend/ci/inherited_ci_variables/mocks.js44
-rw-r--r--spec/frontend/design_management/components/design_description/description_form_spec.js299
-rw-r--r--spec/frontend/design_management/components/design_sidebar_spec.js8
-rw-r--r--spec/frontend/design_management/mock_data/apollo_mock.js62
-rw-r--r--spec/frontend/design_management/mock_data/design.js2
-rw-r--r--spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap12
-rw-r--r--spec/frontend/design_management/pages/design/index_spec.js6
-rw-r--r--spec/frontend/design_management/pages/index_spec.js2
-rw-r--r--spec/frontend/design_management/utils/design_management_utils_spec.js2
-rw-r--r--spec/lib/banzai/filter/inline_alert_metrics_filter_spec.rb2
-rw-r--r--spec/lib/banzai/filter/inline_cluster_metrics_filter_spec.rb2
-rw-r--r--spec/lib/banzai/filter/inline_metrics_filter_spec.rb2
-rw-r--r--spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb2
-rw-r--r--spec/lib/banzai/filter/markdown_filter_spec.rb57
-rw-r--r--spec/lib/banzai/filter/truncate_visible_filter_spec.rb2
-rw-r--r--spec/lib/banzai/pipeline/full_pipeline_spec.rb8
-rw-r--r--spec/lib/gitlab/auth/ldap/auth_hash_spec.rb12
-rw-r--r--spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb8
-rw-r--r--spec/lib/gitlab/markdown_cache/redis/extension_spec.rb8
-rw-r--r--spec/migrations/20230412141541_reschedule_links_avoiding_duplication_spec.rb8
-rw-r--r--spec/migrations/20230522111534_reschedule_migration_for_links_from_metadata_spec.rb31
-rw-r--r--spec/models/concerns/cache_markdown_field_spec.rb10
-rw-r--r--spec/models/diff_discussion_spec.rb4
-rw-r--r--spec/models/release_highlight_spec.rb4
-rw-r--r--spec/requests/api/graphql/ci/runner_spec.rb2
-rw-r--r--spec/requests/projects/metrics_dashboard_spec.rb171
-rw-r--r--spec/routing/project_routing_spec.rb67
-rw-r--r--spec/services/projects/operations/update_service_spec.rb111
-rw-r--r--spec/support/helpers/markdown_helpers.rb7
-rw-r--r--spec/support/matchers/sourcepos_matchers.rb14
41 files changed, 961 insertions, 911 deletions
diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb
index 22804339fef..5f03d721fe7 100644
--- a/spec/controllers/projects/environments_controller_spec.rb
+++ b/spec/controllers/projects/environments_controller_spec.rb
@@ -3,7 +3,6 @@
require 'spec_helper'
RSpec.describe Projects::EnvironmentsController, feature_category: :continuous_delivery do
- include MetricsDashboardHelpers
include KubernetesHelpers
let_it_be(:project) { create(:project, :repository) }
@@ -15,7 +14,6 @@ RSpec.describe Projects::EnvironmentsController, feature_category: :continuous_d
let!(:environment) { create(:environment, name: 'production', project: project) }
before do
- stub_feature_flags(remove_monitor_metrics: false)
sign_in(user)
end
@@ -538,405 +536,6 @@ RSpec.describe Projects::EnvironmentsController, feature_category: :continuous_d
end
end
- describe 'GET #metrics_redirect' do
- it 'redirects to metrics dashboard page' do
- get :metrics_redirect, params: { namespace_id: project.namespace, project_id: project }
-
- expect(response).to redirect_to(project_metrics_dashboard_path(project))
- end
-
- context 'when metrics dashboard feature is unavailable' do
- before do
- stub_feature_flags(remove_monitor_metrics: true)
- end
-
- it 'returns 404 not found' do
- get :metrics_redirect, params: { namespace_id: project.namespace, project_id: project }
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
- end
-
- describe 'GET #metrics' do
- before do
- allow(controller).to receive(:environment).and_return(environment)
- end
-
- context 'when environment has no metrics' do
- it 'redirects to metrics dashboard page' do
- expect(environment).not_to receive(:metrics)
-
- get :metrics, params: environment_params
-
- expect(response).to redirect_to(project_metrics_dashboard_path(project, environment: environment))
- end
-
- context 'when requesting metrics as JSON' do
- it 'returns a metrics JSON document' do
- expect(environment).to receive(:metrics).and_return(nil)
-
- get :metrics, params: environment_params(format: :json)
-
- expect(response).to have_gitlab_http_status(:no_content)
- expect(json_response).to eq({})
- end
- end
- end
-
- context 'when environment has some metrics' do
- before do
- expect(environment).to receive(:metrics).and_return({
- success: true,
- metrics: {},
- last_update: 42
- })
- end
-
- it 'returns a metrics JSON document' do
- get :metrics, params: environment_params(format: :json)
-
- expect(response).to be_ok
- expect(json_response['success']).to be(true)
- expect(json_response['metrics']).to eq({})
- expect(json_response['last_update']).to eq(42)
- end
- end
-
- context 'permissions' do
- before do
- allow(controller).to receive(:can?).and_return true
- end
-
- it 'checks :metrics_dashboard ability' do
- expect(controller).to receive(:can?).with(anything, :metrics_dashboard, anything)
-
- get :metrics, params: environment_params
- end
- end
-
- context 'with anonymous user and public dashboard visibility' do
- let(:project) { create(:project, :public) }
- let(:user) { create(:user) }
-
- it 'redirects to metrics dashboard page' do
- project.project_feature.update!(metrics_dashboard_access_level: ProjectFeature::ENABLED)
-
- get :metrics, params: environment_params
-
- expect(response).to redirect_to(project_metrics_dashboard_path(project, environment: environment))
- end
- end
-
- context 'when metrics dashboard feature is unavailable' do
- before do
- stub_feature_flags(remove_monitor_metrics: true)
- end
-
- it 'returns 404 not found' do
- expect(environment).not_to receive(:metrics)
-
- get :metrics, params: environment_params
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
- end
-
- describe 'GET #additional_metrics' do
- let(:window_params) { { start: '1554702993.5398998', end: '1554717396.996232' } }
-
- before do
- allow(controller).to receive(:environment).and_return(environment)
- end
-
- context 'when environment has no metrics' do
- before do
- expect(environment).to receive(:additional_metrics).and_return(nil)
- end
-
- context 'when requesting metrics as JSON' do
- it 'returns a metrics JSON document' do
- additional_metrics(window_params)
-
- expect(response).to have_gitlab_http_status(:no_content)
- expect(json_response).to eq({})
- end
- end
- end
-
- context 'when environment has some metrics' do
- before do
- expect(environment)
- .to receive(:additional_metrics)
- .and_return({
- success: true,
- data: {},
- last_update: 42
- })
- end
-
- it 'returns a metrics JSON document' do
- additional_metrics(window_params)
-
- expect(response).to be_ok
- expect(json_response['success']).to be(true)
- expect(json_response['data']).to eq({})
- expect(json_response['last_update']).to eq(42)
- end
- end
-
- context 'when time params are missing' do
- it 'raises an error when window params are missing' do
- expect { additional_metrics }
- .to raise_error(ActionController::ParameterMissing)
- end
- end
-
- context 'when only one time param is provided' do
- it 'raises an error when start is missing' do
- expect { additional_metrics(end: '1552647300.651094') }
- .to raise_error(ActionController::ParameterMissing)
- end
-
- it 'raises an error when end is missing' do
- expect { additional_metrics(start: '1552647300.651094') }
- .to raise_error(ActionController::ParameterMissing)
- end
- end
-
- context 'permissions' do
- before do
- allow(controller).to receive(:can?).and_return true
- end
-
- it 'checks :metrics_dashboard ability' do
- expect(controller).to receive(:can?).with(anything, :metrics_dashboard, anything)
-
- get :metrics, params: environment_params
- end
- end
-
- context 'with anonymous user and public dashboard visibility' do
- let(:project) { create(:project, :public) }
- let(:user) { create(:user) }
-
- it 'does not fail' do
- allow(environment)
- .to receive(:additional_metrics)
- .and_return({
- success: true,
- data: {},
- last_update: 42
- })
- project.project_feature.update!(metrics_dashboard_access_level: ProjectFeature::ENABLED)
-
- additional_metrics(window_params)
-
- expect(response).to have_gitlab_http_status(:ok)
- end
- end
-
- context 'when metrics dashboard feature is unavailable' do
- before do
- stub_feature_flags(remove_monitor_metrics: true)
- end
-
- it 'returns 404 not found' do
- additional_metrics(window_params)
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
- end
-
- describe 'GET #metrics_dashboard' do
- let(:metrics_dashboard_req_params) { environment_params(dashboard_params) }
-
- shared_examples_for '200 response' do
- it_behaves_like 'GET #metrics_dashboard correctly formatted response' do
- let(:expected_keys) { %w(dashboard status metrics_data) }
- let(:status_code) { :ok }
- end
- end
-
- shared_examples_for 'error response' do |status_code|
- it_behaves_like 'GET #metrics_dashboard correctly formatted response' do
- let(:expected_keys) { %w(message status) }
- let(:status_code) { status_code }
- end
- end
-
- shared_examples_for 'includes all dashboards' do
- it 'includes info for all findable dashboard' do
- get :metrics_dashboard, params: environment_params(dashboard_params)
-
- expect(json_response).to have_key('all_dashboards')
- expect(json_response['all_dashboards']).to be_an_instance_of(Array)
- expect(json_response['all_dashboards']).to all(include('path', 'default', 'display_name'))
- end
- end
-
- shared_examples_for 'the default dashboard' do
- it_behaves_like 'includes all dashboards'
- it_behaves_like 'GET #metrics_dashboard for dashboard', 'Environment metrics'
- end
-
- shared_examples_for 'the specified dashboard' do |expected_dashboard|
- it_behaves_like 'includes all dashboards'
-
- it_behaves_like 'GET #metrics_dashboard for dashboard', expected_dashboard
-
- context 'when the dashboard cannot not be processed' do
- before do
- allow(YAML).to receive(:safe_load).and_return({})
- end
-
- it_behaves_like 'error response', :unprocessable_entity
- end
- end
-
- shared_examples_for 'specified dashboard embed' do |expected_titles|
- it_behaves_like '200 response'
-
- it 'contains only the specified charts' do
- get :metrics_dashboard, params: environment_params(dashboard_params)
-
- dashboard = json_response['dashboard']
- panel_group = dashboard['panel_groups'].first
- titles = panel_group['panels'].map { |panel| panel['title'] }
-
- expect(dashboard['dashboard']).to be_nil
- expect(dashboard['panel_groups'].length).to eq 1
- expect(panel_group['group']).to be_nil
- expect(titles).to eq expected_titles
- end
- end
-
- shared_examples_for 'the default dynamic dashboard' do
- it_behaves_like 'specified dashboard embed', ['Memory Usage (Total)', 'Core Usage (Total)']
- end
-
- shared_examples_for 'dashboard can be specified' do
- context 'when dashboard is specified' do
- let(:dashboard_path) { '.gitlab/dashboards/test.yml' }
- let(:dashboard_params) { { format: :json, dashboard: dashboard_path } }
-
- it_behaves_like 'error response', :not_found
-
- context 'when the project dashboard is available' do
- let(:dashboard_yml) { fixture_file('lib/gitlab/metrics/dashboard/sample_dashboard.yml') }
- let(:project) { project_with_dashboard(dashboard_path, dashboard_yml) }
- let(:environment) { create(:environment, name: 'production', project: project) }
-
- before do
- project.add_maintainer(user)
- end
-
- it_behaves_like 'the specified dashboard', 'Test Dashboard'
- end
-
- context 'when the specified dashboard is the default dashboard' do
- let(:dashboard_path) { system_dashboard_path }
-
- it_behaves_like 'the default dashboard'
- end
- end
- end
-
- shared_examples_for 'dashboard can be embedded' do
- context 'when the embedded flag is included' do
- let(:dashboard_params) { { format: :json, embedded: true } }
-
- it_behaves_like 'the default dynamic dashboard'
-
- context 'when incomplete dashboard params are provided' do
- let(:dashboard_params) { { format: :json, embedded: true, title: 'Title' } }
-
- # The title param should be ignored.
- it_behaves_like 'the default dynamic dashboard'
- end
-
- context 'when invalid params are provided' do
- let(:dashboard_params) { { format: :json, embedded: true, metric_id: 16 } }
-
- # The superfluous param should be ignored.
- it_behaves_like 'the default dynamic dashboard'
- end
-
- context 'when the dashboard is correctly specified' do
- let(:dashboard_params) do
- {
- format: :json,
- embedded: true,
- dashboard: system_dashboard_path,
- group: business_metric_title,
- title: 'title',
- y_label: 'y_label'
- }
- end
-
- it_behaves_like 'error response', :not_found
-
- context 'and exists' do
- let!(:metric) { create(:prometheus_metric, project: project) }
-
- it_behaves_like 'specified dashboard embed', ['title']
- end
- end
- end
- end
-
- shared_examples_for 'dashboard cannot be specified' do
- context 'when dashboard is specified' do
- let(:dashboard_params) { { format: :json, dashboard: '.gitlab/dashboards/test.yml' } }
-
- it_behaves_like 'the default dashboard'
- end
- end
-
- let(:dashboard_params) { { format: :json } }
-
- it_behaves_like 'the default dashboard'
- it_behaves_like 'dashboard can be specified'
- it_behaves_like 'dashboard can be embedded'
-
- context 'with anonymous user and public dashboard visibility' do
- let(:project) { create(:project, :public) }
- let(:user) { create(:user) }
-
- before do
- project.project_feature.update!(metrics_dashboard_access_level: ProjectFeature::ENABLED)
- end
-
- it_behaves_like 'the default dashboard'
- end
-
- context 'permissions' do
- before do
- allow(controller).to receive(:can?).and_return true
- end
-
- it 'checks :metrics_dashboard ability' do
- expect(controller).to receive(:can?).with(anything, :metrics_dashboard, anything)
-
- get :metrics, params: environment_params
- end
-
- context 'when metrics dashboard feature is unavailable' do
- before do
- stub_feature_flags(remove_monitor_metrics: true)
- end
-
- it 'returns 404 not found' do
- get :metrics_dashboard, params: environment_params(dashboard_params)
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
- end
- end
-
describe 'GET #search' do
before do
create(:environment, name: 'staging', project: project)
@@ -1053,8 +652,4 @@ RSpec.describe Projects::EnvironmentsController, feature_category: :continuous_d
def environment_params(opts = {})
opts.reverse_merge(namespace_id: project.namespace, project_id: project, id: environment.id)
end
-
- def additional_metrics(opts = {})
- get :additional_metrics, params: environment_params(format: :json, **opts)
- end
end
diff --git a/spec/controllers/projects/settings/operations_controller_spec.rb b/spec/controllers/projects/settings/operations_controller_spec.rb
index 04dbd9ab671..770c1991144 100644
--- a/spec/controllers/projects/settings/operations_controller_spec.rb
+++ b/spec/controllers/projects/settings/operations_controller_spec.rb
@@ -11,8 +11,6 @@ RSpec.describe Projects::Settings::OperationsController, feature_category: :inci
end
before do
- stub_feature_flags(remove_monitor_metrics: false)
-
sign_in(user)
end
@@ -67,20 +65,6 @@ RSpec.describe Projects::Settings::OperationsController, feature_category: :inci
end
end
- shared_examples 'PATCHable without metrics dashboard' do
- context 'when metrics dashboard feature is unavailable' do
- before do
- stub_feature_flags(remove_monitor_metrics: true)
- end
-
- include_examples 'PATCHable' do
- let(:permitted_params) do
- ActionController::Parameters.new({}).permit!
- end
- end
- end
- end
-
describe 'GET #show' do
it 'renders show template' do
get :show, params: project_params(project)
@@ -339,38 +323,6 @@ RSpec.describe Projects::Settings::OperationsController, feature_category: :inci
end
end
- context 'metrics dashboard setting', feature_category: :metrics do
- describe 'PATCH #update' do
- let(:params) do
- {
- metrics_setting_attributes: {
- external_dashboard_url: 'https://gitlab.com'
- }
- }
- end
-
- include_examples 'PATCHable'
- include_examples 'PATCHable without metrics dashboard'
- end
- end
-
- context 'grafana integration', feature_category: :metrics do
- describe 'PATCH #update' do
- let(:params) do
- {
- grafana_integration_attributes: {
- grafana_url: 'https://grafana.gitlab.com',
- token: 'eyJrIjoicDRlRTREdjhhOEZ5WjZPWXUzazJOSW0zZHJUejVOd3IiLCJuIjoiVGVzdCBLZXkiLCJpZCI6MX0=',
- enabled: 'true'
- }
- }
- end
-
- include_examples 'PATCHable'
- include_examples 'PATCHable without metrics dashboard'
- end
- end
-
context 'prometheus integration' do
describe 'POST #reset_alerting_token' do
context 'with existing alerting setting' do
diff --git a/spec/features/markdown/markdown_spec.rb b/spec/features/markdown/markdown_spec.rb
index a31ad5a868e..9bb0220004a 100644
--- a/spec/features/markdown/markdown_spec.rb
+++ b/spec/features/markdown/markdown_spec.rb
@@ -59,7 +59,7 @@ RSpec.describe 'GitLab Markdown', :aggregate_failures, feature_category: :team_p
aggregate_failures 'allows Markdown in tables' do
expect(doc.at_css('td:contains("Baz")').children.to_html)
- .to eq '<strong>Baz</strong>'
+ .to eq_no_sourcepos '<strong>Baz</strong>'
end
aggregate_failures 'parses fenced code blocks' do
@@ -167,13 +167,13 @@ RSpec.describe 'GitLab Markdown', :aggregate_failures, feature_category: :team_p
it 'allows markup inside link elements' do
aggregate_failures do
expect(doc.at_css('a[href="#link-emphasis"]').to_html)
- .to eq %{<a href="#link-emphasis"><em>text</em></a>}
+ .to eq_no_sourcepos %{<a href="#link-emphasis"><em>text</em></a>}
expect(doc.at_css('a[href="#link-strong"]').to_html)
- .to eq %{<a href="#link-strong"><strong>text</strong></a>}
+ .to eq_no_sourcepos %{<a href="#link-strong"><strong>text</strong></a>}
expect(doc.at_css('a[href="#link-code"]').to_html)
- .to eq %{<a href="#link-code"><code>text</code></a>}
+ .to eq_no_sourcepos %{<a href="#link-code"><code>text</code></a>}
end
end
end
diff --git a/spec/features/merge_request/user_comments_on_whitespace_hidden_diff_spec.rb b/spec/features/merge_request/user_comments_on_whitespace_hidden_diff_spec.rb
index 08976d3d2e2..c13fe8d1e45 100644
--- a/spec/features/merge_request/user_comments_on_whitespace_hidden_diff_spec.rb
+++ b/spec/features/merge_request/user_comments_on_whitespace_hidden_diff_spec.rb
@@ -27,22 +27,35 @@ RSpec.describe 'User comments on a diff with whitespace changes', :js, feature_c
wait_for_requests
end
- it 'allows commenting on line combinations that are not present in the real diff' do
- # Comment on line combination old: 19, new 20
- # This line combination does not exist when whitespace is shown
- click_diff_line(
- find_by_scrolling('div[data-path="files/ruby/popen.rb"] .left-side a[data-linenumber="19"]').find(:xpath,
- '../..'), 'left')
-
- page.within('.js-discussion-note-form') do
- fill_in(:note_note, with: 'Comment on diff with whitespace')
- click_button('Add comment now')
+ context 'when commenting on line combinations that are not present in the real diff' do
+ before do
+ # Comment on line combination old: 19, new 20
+ # This line combination does not exist when whitespace is shown
+ click_diff_line(
+ find_by_scrolling('div[data-path="files/ruby/popen.rb"] .left-side a[data-linenumber="19"]').find(:xpath,
+ '../..'), 'left')
+ page.within('.js-discussion-note-form') do
+ fill_in(:note_note, with: 'Comment on diff with whitespace')
+ click_button('Add comment now')
+ end
+
+ wait_for_requests
end
- wait_for_requests
+ it 'shows the comments in the diff' do
+ page.within('.notes_holder') do
+ expect(page).to have_content('Comment on diff with whitespace')
+ end
+ end
- page.within('.notes_holder') do
- expect(page).to have_content('Comment on diff with whitespace')
+ it 'allows replies to comments in the diff' do
+ click_button('Reply to comment')
+ fill_in(:note_note, with: 'reply to whitespace comment')
+ click_button('Add comment now')
+ wait_for_requests
+ page.within('.notes_holder') do
+ expect(page).to have_content('reply to whitespace comment')
+ end
end
end
end
diff --git a/spec/features/project_group_variables_spec.rb b/spec/features/project_group_variables_spec.rb
index 966c05bb4cb..001b3ff2e41 100644
--- a/spec/features/project_group_variables_spec.rb
+++ b/spec/features/project_group_variables_spec.rb
@@ -26,39 +26,131 @@ RSpec.describe 'Project group variables', :js, feature_category: :secrets_manage
group.add_owner(user)
end
- it 'project in group shows inherited vars from ancestor group' do
- visit project_path
- expect(page).to have_content(key1)
- expect(page).to have_content(group.name)
+ shared_examples 'renders the haml column headers' do
+ it "shows inherited CI variables table with correct columns" do
+ page.within('.inherited-ci-variable-table') do
+ columns = find_all('th')
+
+ expect(columns[0].text).to eq('Key')
+ expect(columns[1].text).to eq('Environments')
+ expect(columns[2].text).to eq('Group')
+ end
+ end
end
- it 'project in subgroup shows inherited vars from all ancestor groups' do
- visit project2_path
- expect(page).to have_content(key1)
- expect(page).to have_content(key2)
- expect(page).to have_content(group.name)
- expect(page).to have_content(subgroup.name)
+ shared_examples 'renders the vue app column headers' do
+ it "shows inherited CI variables table with correct columns" do
+ page.within('[data-testid="inherited-ci-variable-table"]') do
+ # Wait for vue app to load
+ wait_for_requests
+
+ columns = find_all('[role=columnheader]')
+
+ expect(columns[0].text).to eq('Type')
+ expect(columns[1].text).to eq('Key')
+ expect(columns[2].text).to eq('Options')
+ expect(columns[3].text).to eq('Environments')
+ expect(columns[4].text).to eq('Group')
+ end
+ end
end
- it 'project in nested subgroup shows inherited vars from all ancestor groups' do
- visit project3_path
- expect(page).to have_content(key1)
- expect(page).to have_content(key2)
- expect(page).to have_content(key3)
- expect(page).to have_content(group.name)
- expect(page).to have_content(subgroup.name)
- expect(page).to have_content(subgroup_nested.name)
+ context 'when feature flag ci_vueify_inherited_group_variables is disabled' do
+ before do
+ stub_feature_flags(ci_vueify_inherited_group_variables: false)
+ end
+
+ describe 'project in group' do
+ before do
+ visit project_path
+ end
+
+ it_behaves_like 'renders the haml column headers'
+
+ it 'shows inherited variable info from ancestor group' do
+ visit project_path
+
+ expect(page).to have_content(key1)
+ expect(page).to have_content(group.name)
+ end
+ end
+
+ describe 'project in subgroup' do
+ before do
+ visit project2_path
+ end
+
+ it_behaves_like 'renders the haml column headers'
+
+ it 'shows inherited variable info from all ancestor groups' do
+ visit project2_path
+
+ expect(page).to have_content(key1)
+ expect(page).to have_content(key2)
+ expect(page).to have_content(group.name)
+ expect(page).to have_content(subgroup.name)
+ end
+ end
+
+ describe 'project in nested subgroup' do
+ before do
+ visit project3_path
+ end
+
+ it_behaves_like 'renders the haml column headers'
+
+ it 'shows inherited variable info from all ancestor groups' do
+ visit project3_path
+
+ expect(page).to have_content(key1)
+ expect(page).to have_content(key2)
+ expect(page).to have_content(key3)
+ expect(page).to have_content(group.name)
+ expect(page).to have_content(subgroup.name)
+ expect(page).to have_content(subgroup_nested.name)
+ end
+ end
+
+ it 'project origin keys link to ancestor groups ci_cd settings' do
+ visit project_path
+
+ find('.group-origin-link').click
+
+ wait_for_requests
+
+ page.within('[data-testid="ci-variable-table"]') do
+ expect(find('.js-ci-variable-row:nth-child(1) [data-label="Key"]').text).to eq(key1)
+ end
+ end
end
- it 'project origin keys link to ancestor groups ci_cd settings' do
- visit project_path
+ context 'when feature flag ci_vueify_inherited_group_variables is enabled' do
+ before do
+ stub_feature_flags(ci_vueify_inherited_group_variables: true)
+ end
+
+ describe 'project in group' do
+ before do
+ visit project_path
+ end
- find('.group-origin-link').click
+ it_behaves_like 'renders the vue app column headers'
+ end
+
+ describe 'project in subgroup' do
+ before do
+ visit project2_path
+ end
+
+ it_behaves_like 'renders the vue app column headers'
+ end
- wait_for_requests
+ describe 'project in nested subgroup' do
+ before do
+ visit project3_path
+ end
- page.within('[data-testid="ci-variable-table"]') do
- expect(find('.js-ci-variable-row:nth-child(1) [data-label="Key"]').text).to eq(key1)
+ it_behaves_like 'renders the vue app column headers'
end
end
end
diff --git a/spec/features/projects/releases/user_views_releases_spec.rb b/spec/features/projects/releases/user_views_releases_spec.rb
index 0a4075be02f..d0111d0b9c8 100644
--- a/spec/features/projects/releases/user_views_releases_spec.rb
+++ b/spec/features/projects/releases/user_views_releases_spec.rb
@@ -24,6 +24,17 @@ RSpec.describe 'User views releases', :js, feature_category: :continuous_deliver
stub_default_url_options(host: 'localhost')
end
+ shared_examples 'when the project does not have releases' do
+ before do
+ project.releases.delete_all
+ visit project_releases_path(project)
+ end
+
+ it 'sees an empty state' do
+ expect(page).to have_selector('[data-testid="gl-empty-state-content"]')
+ end
+ end
+
context('when the user is a maintainer') do
before do
sign_in(maintainer)
@@ -110,6 +121,8 @@ RSpec.describe 'User views releases', :js, feature_category: :continuous_deliver
it_behaves_like 'releases sort order'
end
end
+
+ it_behaves_like 'when the project does not have releases'
end
context('when the user is a guest') do
@@ -130,5 +143,7 @@ RSpec.describe 'User views releases', :js, feature_category: :continuous_deliver
expect(page).not_to have_content(release_v3.commit.short_id)
end
end
+
+ it_behaves_like 'when the project does not have releases'
end
end
diff --git a/spec/frontend/behaviors/markdown/utils_spec.js b/spec/frontend/behaviors/markdown/utils_spec.js
new file mode 100644
index 00000000000..f4e7ca621d9
--- /dev/null
+++ b/spec/frontend/behaviors/markdown/utils_spec.js
@@ -0,0 +1,18 @@
+import { toggleMarkCheckboxes } from '~/behaviors/markdown/utils';
+
+describe('toggleMarkCheckboxes', () => {
+ const rawMarkdown = `- [x] todo 1\n- [ ] todo 2`;
+
+ it.each`
+ assertionName | sourcepos | checkboxChecked | expectedMarkdown
+ ${'marks'} | ${'2:1-2:12'} | ${true} | ${'- [x] todo 1\n- [x] todo 2'}
+ ${'unmarks'} | ${'1:1-1:12'} | ${false} | ${'- [ ] todo 1\n- [ ] todo 2'}
+ `(
+ '$assertionName the checkbox at correct position',
+ ({ sourcepos, checkboxChecked, expectedMarkdown }) => {
+ expect(toggleMarkCheckboxes({ rawMarkdown, sourcepos, checkboxChecked })).toEqual(
+ expectedMarkdown,
+ );
+ },
+ );
+});
diff --git a/spec/frontend/ci/ci_variable_list/components/ci_variable_shared_spec.js b/spec/frontend/ci/ci_variable_list/components/ci_variable_shared_spec.js
index a25d325f7a1..f7b90c3da30 100644
--- a/spec/frontend/ci/ci_variable_list/components/ci_variable_shared_spec.js
+++ b/spec/frontend/ci/ci_variable_list/components/ci_variable_shared_spec.js
@@ -46,6 +46,7 @@ Vue.use(VueApollo);
const mockProvide = {
endpoint: '/variables',
isGroup: false,
+ isInheritedGroupVars: false,
isProject: false,
};
diff --git a/spec/frontend/ci/ci_variable_list/components/ci_variable_table_spec.js b/spec/frontend/ci/ci_variable_list/components/ci_variable_table_spec.js
index 0b28cb06cec..e953fbb7031 100644
--- a/spec/frontend/ci/ci_variable_list/components/ci_variable_table_spec.js
+++ b/spec/frontend/ci/ci_variable_list/components/ci_variable_table_spec.js
@@ -1,9 +1,9 @@
-import { GlAlert } from '@gitlab/ui';
+import { GlAlert, GlKeysetPagination } from '@gitlab/ui';
import { sprintf } from '~/locale';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import CiVariableTable from '~/ci/ci_variable_list/components/ci_variable_table.vue';
import { EXCEEDS_VARIABLE_LIMIT_TEXT, projectString } from '~/ci/ci_variable_list/constants';
-import { mockVariables } from '../mocks';
+import { mockInheritedVariables, mockVariables } from '../mocks';
describe('Ci variable table', () => {
let wrapper;
@@ -29,6 +29,7 @@ describe('Ci variable table', () => {
glFeatures: {
ciVariablesPages: false,
},
+ isInheritedGroupVars: false,
...provide,
},
});
@@ -43,6 +44,10 @@ describe('Ci variable table', () => {
const findRevealedValues = () => wrapper.findAllByTestId('revealedValue');
const findOptionsValues = (rowIndex) =>
wrapper.findAllByTestId('ci-variable-table-row-options').at(rowIndex).text();
+ const findTableColumnText = (index) => wrapper.findAll('th').at(index).text();
+ const findGroupCiCdSettingsLink = (rowIndex) =>
+ wrapper.findAllByTestId('ci-variable-table-row-cicd-path').at(rowIndex).attributes('href');
+ const findKeysetPagination = () => wrapper.findComponent(GlKeysetPagination);
const generateExceedsVariableLimitText = (entity, currentVariableCount, maxVariableLimit) => {
return sprintf(EXCEEDS_VARIABLE_LIMIT_TEXT, { entity, currentVariableCount, maxVariableLimit });
@@ -69,17 +74,26 @@ describe('Ci variable table', () => {
});
});
- describe('When table has variables', () => {
+ describe('When table has CI variables', () => {
beforeEach(() => {
createComponent({ provide });
});
- it('does not display the empty message', () => {
- expect(findEmptyVariablesPlaceholder().exists()).toBe(false);
+ // last column is for the edit button, which has no text
+ it.each`
+ index | text
+ ${0} | ${'Type'}
+ ${1} | ${'Key (Click to sort descending)'}
+ ${2} | ${'Value'}
+ ${3} | ${'Options'}
+ ${4} | ${'Environments'}
+ ${5} | ${''}
+ `('renders the $text column', ({ index, text }) => {
+ expect(findTableColumnText(index)).toEqual(text);
});
- it('displays the reveal button', () => {
- expect(findRevealButton().exists()).toBe(true);
+ it('does not display the empty message', () => {
+ expect(findEmptyVariablesPlaceholder().exists()).toBe(false);
});
it('displays the correct amount of variables', () => {
@@ -91,11 +105,59 @@ describe('Ci variable table', () => {
expect(findOptionsValues(1)).toBe('Masked');
});
+ it('renders action buttons', () => {
+ expect(findRevealButton().exists()).toBe(true);
+ expect(findAddButton().exists()).toBe(true);
+ expect(findEditButton().exists()).toBe(true);
+ });
+
it('enables the Add Variable button', () => {
expect(findAddButton().props('disabled')).toBe(false);
});
});
+ describe('When table has inherited CI variables', () => {
+ beforeEach(() => {
+ createComponent({
+ props: { variables: mockInheritedVariables },
+ provide: { isInheritedGroupVars: true, ...provide },
+ });
+ });
+
+ it.each`
+ index | text
+ ${0} | ${'Type'}
+ ${1} | ${'Key'}
+ ${2} | ${'Options'}
+ ${3} | ${'Environments'}
+ ${4} | ${'Group'}
+ `('renders the $text column', ({ index, text }) => {
+ expect(findTableColumnText(index)).toEqual(text);
+ });
+
+ it('does not render action buttons', () => {
+ expect(findRevealButton().exists()).toBe(false);
+ expect(findAddButton().exists()).toBe(false);
+ expect(findEditButton().exists()).toBe(false);
+ expect(findKeysetPagination().exists()).toBe(false);
+ });
+
+ it('displays the correct amount of variables', () => {
+ expect(wrapper.findAll('.js-ci-variable-row')).toHaveLength(mockInheritedVariables.length);
+ });
+
+ it('displays the correct variable options', () => {
+ expect(findOptionsValues(0)).toBe('Protected, Masked, Expanded');
+ expect(findOptionsValues(1)).toBe('');
+ expect(findOptionsValues(2)).toBe('Protected');
+ });
+
+ it('displays link to the group settings', () => {
+ expect(findGroupCiCdSettingsLink(0)).toBe(mockInheritedVariables[0].groupCiCdSettingsPath);
+ expect(findGroupCiCdSettingsLink(1)).toBe(mockInheritedVariables[1].groupCiCdSettingsPath);
+ });
+ });
+
describe('When variables have exceeded the max limit', () => {
beforeEach(() => {
createComponent({
diff --git a/spec/frontend/ci/ci_variable_list/mocks.js b/spec/frontend/ci/ci_variable_list/mocks.js
index f9450803308..9c9c99ad5ea 100644
--- a/spec/frontend/ci/ci_variable_list/mocks.js
+++ b/spec/frontend/ci/ci_variable_list/mocks.js
@@ -51,6 +51,45 @@ export const mockVariables = (kind) => {
];
};
+export const mockInheritedVariables = [
+ {
+ id: 'gid://gitlab/Ci::GroupVariable/120',
+ key: 'INHERITED_VAR_1',
+ variableType: 'ENV_VAR',
+ environmentScope: '*',
+ masked: true,
+ protected: true,
+ raw: false,
+ groupName: 'group-name',
+ groupCiCdSettingsPath: '/groups/group-name/-/settings/ci_cd',
+ __typename: 'InheritedCiVariable',
+ },
+ {
+ id: 'gid://gitlab/Ci::GroupVariable/121',
+ key: 'INHERITED_VAR_2',
+ variableType: 'ENV_VAR',
+ environmentScope: 'staging',
+ masked: false,
+ protected: false,
+ raw: true,
+ groupName: 'subgroup-name',
+ groupCiCdSettingsPath: '/groups/group-name/subgroup-name/-/settings/ci_cd',
+ __typename: 'InheritedCiVariable',
+ },
+ {
+ id: 'gid://gitlab/Ci::GroupVariable/122',
+ key: 'INHERITED_VAR_3',
+ variableType: 'FILE',
+ environmentScope: 'production',
+ masked: false,
+ protected: true,
+ raw: true,
+ groupName: 'subgroup-name',
+ groupCiCdSettingsPath: '/groups/group-name/subgroup-name/-/settings/ci_cd',
+ __typename: 'InheritedCiVariable',
+ },
+];
+
export const mockVariablesWithScopes = (kind) =>
mockVariables(kind).map((variable) => {
return { ...variable, environmentScope: '*' };
diff --git a/spec/frontend/ci/inherited_ci_variables/components/inherited_ci_variables_app_spec.js b/spec/frontend/ci/inherited_ci_variables/components/inherited_ci_variables_app_spec.js
new file mode 100644
index 00000000000..0af026cfec4
--- /dev/null
+++ b/spec/frontend/ci/inherited_ci_variables/components/inherited_ci_variables_app_spec.js
@@ -0,0 +1,114 @@
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import { shallowMount } from '@vue/test-utils';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import { createAlert } from '~/alert';
+import CiVariableTable from '~/ci/ci_variable_list/components/ci_variable_table.vue';
+import InheritedCiVariablesApp, {
+ i18n,
+ FETCH_LIMIT,
+ VARIABLES_PER_FETCH,
+} from '~/ci/inherited_ci_variables/components/inherited_ci_variables_app.vue';
+import getInheritedCiVariables from '~/ci/inherited_ci_variables/graphql/queries/inherited_ci_variables.query.graphql';
+import { mockInheritedCiVariables } from '../mocks';
+
+jest.mock('~/alert');
+Vue.use(VueApollo);
+
+describe('Inherited CI Variables Component', () => {
+ let wrapper;
+ let mockApollo;
+ let mockVariables;
+
+ const defaultProvide = {
+ projectPath: 'namespace/project',
+ projectId: '1',
+ };
+
+ const findCiTable = () => wrapper.findComponent(CiVariableTable);
+
+ // eslint-disable-next-line consistent-return
+ function createComponentWithApollo({ isLoading = false } = {}) {
+ const handlers = [[getInheritedCiVariables, mockVariables]];
+
+ mockApollo = createMockApollo(handlers);
+
+ wrapper = shallowMount(InheritedCiVariablesApp, {
+ provide: defaultProvide,
+ apolloProvider: mockApollo,
+ });
+
+ if (!isLoading) {
+ return waitForPromises();
+ }
+ }
+
+ beforeEach(() => {
+ mockVariables = jest.fn();
+ });
+
+ describe('while variables are being fetched', () => {
+ beforeEach(() => {
+ mockVariables.mockResolvedValue(mockInheritedCiVariables());
+ createComponentWithApollo({ isLoading: true });
+ });
+
+ it('shows a loading icon', () => {
+ expect(findCiTable().props('isLoading')).toBe(true);
+ });
+ });
+
+ describe('when there are more variables to fetch', () => {
+ beforeEach(async () => {
+ mockVariables.mockResolvedValue(mockInheritedCiVariables({ withNextPage: true }));
+
+ await createComponentWithApollo();
+ });
+
+ it('re-fetches the query up to <FETCH_LIMIT> times', () => {
+ expect(mockVariables).toHaveBeenCalledTimes(FETCH_LIMIT);
+ });
+
+ it('shows alert message when calls have exceeded FETCH_LIMIT', () => {
+ expect(createAlert).toHaveBeenCalledWith({ message: i18n.tooManyCallsError });
+ });
+ });
+
+ describe('when variables are fetched successfully', () => {
+ beforeEach(async () => {
+ mockVariables.mockResolvedValue(mockInheritedCiVariables());
+
+ await createComponentWithApollo();
+ });
+
+ it('query was called with the correct arguments', () => {
+ expect(mockVariables).toHaveBeenCalledWith({
+ first: VARIABLES_PER_FETCH,
+ fullPath: defaultProvide.projectPath,
+ });
+ });
+
+ it('passes down variables to the table component', () => {
+ expect(findCiTable().props('variables')).toEqual(
+ mockInheritedCiVariables().data.project.inheritedCiVariables.nodes,
+ );
+ });
+
+ it('createAlert was not called', () => {
+ expect(createAlert).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('when fetch error occurs', () => {
+ beforeEach(async () => {
+ mockVariables.mockRejectedValue();
+
+ await createComponentWithApollo();
+ });
+
+ it('shows alert message with the expected error message', () => {
+ expect(createAlert).toHaveBeenCalledWith({ message: i18n.fetchError });
+ });
+ });
+});
diff --git a/spec/frontend/ci/inherited_ci_variables/mocks.js b/spec/frontend/ci/inherited_ci_variables/mocks.js
new file mode 100644
index 00000000000..841ba0a0043
--- /dev/null
+++ b/spec/frontend/ci/inherited_ci_variables/mocks.js
@@ -0,0 +1,44 @@
+export const mockInheritedCiVariables = ({ withNextPage = false } = {}) => ({
+ data: {
+ project: {
+ __typename: 'Project',
+ id: 'gid://gitlab/Project/38',
+ inheritedCiVariables: {
+ __typename: `InheritedCiVariableConnection`,
+ pageInfo: {
+ startCursor: 'adsjsd12kldpsa',
+ endCursor: 'adsjsd12kldpsa',
+ hasPreviousPage: withNextPage,
+ hasNextPage: withNextPage,
+ __typename: 'PageInfo',
+ },
+ nodes: [
+ {
+ __typename: `InheritedCiVariable`,
+ id: 'gid://gitlab/Ci::GroupVariable/1',
+ environmentScope: '*',
+ groupName: 'group_abc',
+ groupCiCdSettingsPath: '/groups/group_abc/-/settings/ci_cd',
+ key: 'GROUP_VAR',
+ masked: false,
+ protected: true,
+ raw: false,
+ variableType: 'ENV_VAR',
+ },
+ {
+ __typename: `InheritedCiVariable`,
+ id: 'gid://gitlab/Ci::GroupVariable/2',
+ environmentScope: '*',
+ groupName: 'subgroup_xyz',
+ groupCiCdSettingsPath: '/groups/group_abc/subgroup_xyz/-/settings/ci_cd',
+ key: 'SUB_GROUP_VAR',
+ masked: true,
+ protected: false,
+ raw: true,
+ variableType: 'ENV_VAR',
+ },
+ ],
+ },
+ },
+ },
+});
diff --git a/spec/frontend/design_management/components/design_description/description_form_spec.js b/spec/frontend/design_management/components/design_description/description_form_spec.js
new file mode 100644
index 00000000000..8c01023b1a8
--- /dev/null
+++ b/spec/frontend/design_management/components/design_description/description_form_spec.js
@@ -0,0 +1,299 @@
+import Vue, { nextTick } from 'vue';
+import VueApollo from 'vue-apollo';
+
+import { GlAlert } from '@gitlab/ui';
+
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+
+import DescriptionForm from '~/design_management/components/design_description/description_form.vue';
+import MarkdownEditor from '~/vue_shared/components/markdown/markdown_editor.vue';
+import updateDesignDescriptionMutation from '~/design_management/graphql/mutations/update_design_description.mutation.graphql';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
+
+import { designFactory, designUpdateFactory } from '../../mock_data/apollo_mock';
+
+jest.mock('~/behaviors/markdown/render_gfm');
+
+Vue.use(VueApollo);
+
+describe('Design description form', () => {
+ const formFieldProps = {
+ id: 'design-description',
+ name: 'design-description',
+ placeholder: 'Write a comment or drag your files here…',
+ 'aria-label': 'Design description',
+ };
+ const mockDesign = designFactory();
+ const mockDesignVariables = {
+ fullPath: '',
+ iid: '1',
+ filenames: ['test.jpg'],
+ atVersion: null,
+ };
+
+ const mockDesignResponse = designUpdateFactory();
+ const mockDesignUpdateMutationHandler = jest.fn().mockResolvedValue(mockDesignResponse);
+ let wrapper;
+ let mockApollo;
+
+ const createComponent = ({
+ design = mockDesign,
+ descriptionText = '',
+ showEditor = false,
+ isSubmitting = false,
+ designVariables = mockDesignVariables,
+ contentEditorOnIssues = false,
+ designUpdateMutationHandler = mockDesignUpdateMutationHandler,
+ } = {}) => {
+ mockApollo = createMockApollo([[updateDesignDescriptionMutation, designUpdateMutationHandler]]);
+ wrapper = mountExtended(DescriptionForm, {
+ propsData: {
+ design,
+ markdownPreviewPath: '/gitlab-org/gitlab-test/preview_markdown?target_type=Issue',
+ designVariables,
+ },
+ provide: {
+ glFeatures: {
+ contentEditorOnIssues,
+ },
+ },
+ apolloProvider: mockApollo,
+ data() {
+ return {
+ formFieldProps,
+ descriptionText,
+ showEditor,
+ isSubmitting,
+ };
+ },
+ });
+ };
+
+ afterEach(() => {
+ mockApollo = null;
+ });
+
+ const findDesignContent = () => wrapper.findByTestId('design-description-content');
+ const findDesignNoneBlock = () => wrapper.findByTestId('design-description-none');
+ const findEditDescriptionButton = () => wrapper.findByTestId('edit-description');
+ const findSaveDescriptionButton = () => wrapper.findByTestId('save-description');
+ const findMarkdownEditor = () => wrapper.findComponent(MarkdownEditor);
+ const findTextarea = () => wrapper.find('textarea');
+ const findCheckboxAtIndex = (index) => wrapper.findAll('input[type="checkbox"]').at(index);
+ const findAlert = () => wrapper.findComponent(GlAlert);
+
+ describe('user has updateDesign permission', () => {
+ const ctrlKey = {
+ ctrlKey: true,
+ };
+ const metaKey = {
+ metaKey: true,
+ };
+ const mockDescription = 'Hello world';
+ const errorMessage = 'Could not update description. Please try again.';
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders description content with the edit button', () => {
+ expect(findDesignContent().text()).toEqual('Test description');
+ expect(findEditDescriptionButton().exists()).toBe(true);
+ });
+
+ it('renders none when description is empty', () => {
+ createComponent({ design: designFactory({ description: '', descriptionHtml: '' }) });
+
+ expect(findDesignNoneBlock().text()).toEqual('None');
+ });
+
+ it('renders save button when editor is open', () => {
+ createComponent({
+ design: designFactory({ description: '', descriptionHtml: '' }),
+ showEditor: true,
+ });
+
+ expect(findSaveDescriptionButton().exists()).toBe(true);
+ expect(findSaveDescriptionButton().attributes('disabled')).toBeUndefined();
+ });
+
+ it('renders the markdown editor with default props', () => {
+ createComponent({
+ showEditor: true,
+ descriptionText: 'Test description',
+ });
+
+ expect(findMarkdownEditor().exists()).toBe(true);
+ expect(findMarkdownEditor().props()).toMatchObject({
+ value: 'Test description',
+ renderMarkdownPath: '/gitlab-org/gitlab-test/preview_markdown?target_type=Issue',
+ enableContentEditor: false,
+ formFieldProps,
+ autofocus: true,
+ enableAutocomplete: true,
+ supportsQuickActions: false,
+ autosaveKey: `Issue/${getIdFromGraphQLId(mockDesign.issue.id)}/Design/${getIdFromGraphQLId(
+ mockDesign.id,
+ )}`,
+ markdownDocsPath: '/help/user/markdown',
+ quickActionsDocsPath: '/help/user/project/quick_actions',
+ });
+ });
+
+ it.each`
+ isKeyEvent | assertionName | key | keyData
+ ${true} | ${'Ctrl + Enter keypress'} | ${'ctrl'} | ${ctrlKey}
+ ${true} | ${'Meta + Enter keypress'} | ${'meta'} | ${metaKey}
+ ${false} | ${'Save button click'} | ${''} | ${null}
+ `(
+ 'hides form and calls mutation when form is submitted via $assertionName',
+ async ({ isKeyEvent, keyData }) => {
+ const mockDesignUpdateResponseHandler = jest.fn().mockResolvedValue(
+ designUpdateFactory({
+ description: mockDescription,
+ descriptionHtml: `<p data-sourcepos="1:1-1:16" dir="auto">${mockDescription}</p>`,
+ }),
+ );
+
+ createComponent({
+ showEditor: true,
+ designUpdateMutationHandler: mockDesignUpdateResponseHandler,
+ });
+
+ findMarkdownEditor().vm.$emit('input', 'Hello world');
+ if (isKeyEvent) {
+ findTextarea().trigger('keydown.enter', keyData);
+ } else {
+ findSaveDescriptionButton().vm.$emit('click');
+ }
+
+ await nextTick();
+
+ expect(mockDesignUpdateResponseHandler).toHaveBeenCalledWith({
+ input: {
+ description: 'Hello world',
+ id: 'gid::/gitlab/Design/1',
+ },
+ });
+
+ await waitForPromises();
+
+ expect(findMarkdownEditor().exists()).toBe(false);
+ },
+ );
+
+ it('shows error message when mutation fails', async () => {
+ const failureHandler = jest.fn().mockRejectedValue(new Error(errorMessage));
+ createComponent({
+ showEditor: true,
+ descriptionText: 'Hello world',
+ designUpdateMutationHandler: failureHandler,
+ });
+
+ findMarkdownEditor().vm.$emit('input', 'Hello world');
+ findSaveDescriptionButton().vm.$emit('click');
+
+ await waitForPromises();
+
+ expect(findAlert().exists()).toBe(true);
+ expect(findAlert().text()).toBe(errorMessage);
+ });
+ });
+
+ describe('content has checkboxes', () => {
+ const mockCheckboxDescription = '- [x] todo 1\n- [ ] todo 2';
+ const mockCheckboxDescriptionHtml = `<ul dir="auto" class="task-list" data-sourcepos="1:1-4:0">
+ <li class="task-list-item" data-sourcepos="1:1-2:15">
+ <input checked="" class="task-list-item-checkbox" type="checkbox"> todo 1</li>
+ <li class="task-list-item" data-sourcepos="2:1-2:15">
+ <input class="task-list-item-checkbox" type="checkbox"> todo 2</li>
+ </ul>`;
+ const checkboxDesignDescription = designFactory({
+ updateDesign: true,
+ description: mockCheckboxDescription,
+ descriptionHtml: mockCheckboxDescriptionHtml,
+ });
+ const mockCheckedDescriptionUpdateResponseHandler = jest.fn().mockResolvedValue(
+ designUpdateFactory({
+ description: '- [x] todo 1\n- [x] todo 2',
+ descriptionHtml: `<ul dir="auto" class="task-list" data-sourcepos="1:1-4:0">
+ <li class="task-list-item" data-sourcepos="1:1-2:15">
+ <input checked="" class="task-list-item-checkbox" type="checkbox"> todo 1</li>
+ <li class="task-list-item" data-sourcepos="2:1-2:15">
+ <input class="task-list-item-checkbox" type="checkbox"> todo 2</li>
+ </ul>`,
+ }),
+ );
+ const mockUnCheckedDescriptionUpdateResponseHandler = jest.fn().mockResolvedValue(
+ designUpdateFactory({
+ description: '- [ ] todo 1\n- [ ] todo 2',
+ descriptionHtml: `<ul dir="auto" class="task-list" data-sourcepos="1:1-4:0">
+ <li class="task-list-item" data-sourcepos="1:1-2:15">
+ <input class="task-list-item-checkbox" type="checkbox"> todo 1</li>
+ <li class="task-list-item" data-sourcepos="2:1-2:15">
+ <input class="task-list-item-checkbox" type="checkbox"> todo 2</li>
+ </ul>`,
+ }),
+ );
+
+ it.each`
+ assertionName | mockDesignUpdateResponseHandler | checkboxIndex | checked | expectedDesignDescription
+ ${'checked'} | ${mockCheckedDescriptionUpdateResponseHandler} | ${1} | ${true} | ${'- [x] todo 1\n- [x] todo 2'}
+ ${'unchecked'} | ${mockUnCheckedDescriptionUpdateResponseHandler} | ${0} | ${false} | ${'- [ ] todo 1\n- [ ] todo 2'}
+ `(
+ 'updates the store object when checkbox is $assertionName',
+ async ({
+ mockDesignUpdateResponseHandler,
+ checkboxIndex,
+ checked,
+ expectedDesignDescription,
+ }) => {
+ createComponent({
+ design: checkboxDesignDescription,
+ descriptionText: mockCheckboxDescription,
+ designUpdateMutationHandler: mockDesignUpdateResponseHandler,
+ });
+
+ findCheckboxAtIndex(checkboxIndex).setChecked(checked);
+
+ expect(mockDesignUpdateResponseHandler).toHaveBeenCalledWith({
+ input: {
+ description: expectedDesignDescription,
+ id: 'gid::/gitlab/Design/1',
+ },
+ });
+
+ await waitForPromises();
+
+ expect(renderGFM).toHaveBeenCalled();
+ },
+ );
+
+ it('disables checkbox while updating', () => {
+ createComponent({
+ design: checkboxDesignDescription,
+ descriptionText: mockCheckboxDescription,
+ });
+
+ findCheckboxAtIndex(1).setChecked();
+
+ expect(findCheckboxAtIndex(1).attributes().disabled).toBeDefined();
+ });
+ });
+
+ describe('user has no updateDesign permission', () => {
+ beforeEach(() => {
+ const designWithNoUpdateUserPermission = designFactory({
+ updateDesign: false,
+ });
+ createComponent({ design: designWithNoUpdateUserPermission });
+ });
+
+ it('does not render edit button', () => {
+ expect(findEditDescriptionButton().exists()).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/design_management/components/design_sidebar_spec.js b/spec/frontend/design_management/components/design_sidebar_spec.js
index 90424175417..e3f056df4c6 100644
--- a/spec/frontend/design_management/components/design_sidebar_spec.js
+++ b/spec/frontend/design_management/components/design_sidebar_spec.js
@@ -26,6 +26,13 @@ const $route = {
},
};
+const mockDesignVariables = {
+ fullPath: 'project-path',
+ iid: '1',
+ filenames: ['gid::/gitlab/Design/1'],
+ atVersion: null,
+};
+
const mutate = jest.fn().mockResolvedValue();
describe('Design management design sidebar component', () => {
@@ -47,6 +54,7 @@ describe('Design management design sidebar component', () => {
resolvedDiscussionsExpanded: false,
markdownPreviewPath: '',
isLoading: false,
+ designVariables: mockDesignVariables,
...props,
},
mocks: {
diff --git a/spec/frontend/design_management/mock_data/apollo_mock.js b/spec/frontend/design_management/mock_data/apollo_mock.js
index 18e08ecd729..063df9366e9 100644
--- a/spec/frontend/design_management/mock_data/apollo_mock.js
+++ b/spec/frontend/design_management/mock_data/apollo_mock.js
@@ -119,6 +119,8 @@ export const reorderedDesigns = [
notesCount: 2,
image: 'image-2',
imageV432x230: 'image-2',
+ description: '',
+ descriptionHtml: '',
currentUserTodos: {
__typename: 'ToDo',
nodes: [],
@@ -132,6 +134,8 @@ export const reorderedDesigns = [
notesCount: 3,
image: 'image-1',
imageV432x230: 'image-1',
+ description: '',
+ descriptionHtml: '',
currentUserTodos: {
__typename: 'ToDo',
nodes: [],
@@ -145,6 +149,8 @@ export const reorderedDesigns = [
notesCount: 1,
image: 'image-3',
imageV432x230: 'image-3',
+ description: '',
+ descriptionHtml: '',
currentUserTodos: {
__typename: 'ToDo',
nodes: [],
@@ -320,3 +326,59 @@ export const mockCreateImageNoteDiffResponse = {
},
},
};
+
+export const designFactory = ({
+ updateDesign = true,
+ discussions = {},
+ description = 'Test description',
+ descriptionHtml = '<p data-sourcepos="1:1-1:16" dir="auto">Test description</p>',
+} = {}) => ({
+ id: 'gid::/gitlab/Design/1',
+ iid: 1,
+ filename: 'test.jpg',
+ fullPath: 'full-design-path',
+ image: 'test.jpg',
+ description,
+ descriptionHtml,
+ updatedAt: '01-01-2019',
+ updatedBy: {
+ name: 'test',
+ },
+ issue: {
+ id: 'gid::/gitlab/Issue/1',
+ title: 'My precious issue',
+ webPath: 'full-issue-path',
+ webUrl: 'full-issue-url',
+ participants: {
+ nodes: [
+ {
+ name: 'Administrator',
+ username: 'root',
+ webUrl: 'link-to-author',
+ avatarUrl: 'link-to-avatar',
+ __typename: 'UserCore',
+ },
+ ],
+ __typename: 'UserCoreConnection',
+ },
+ userPermissions: {
+ updateDesign,
+ __typename: 'IssuePermissions',
+ },
+ __typename: 'Issue',
+ },
+ discussions,
+ __typename: 'Design',
+});
+
+export const designUpdateFactory = (options) => {
+ return {
+ data: {
+ designManagementUpdate: {
+ errors: [],
+ design: designFactory(options),
+ },
+ __typename: 'DesignManagementUpdatePayload',
+ },
+ };
+};
diff --git a/spec/frontend/design_management/mock_data/design.js b/spec/frontend/design_management/mock_data/design.js
index f2a3a800969..8379408b27c 100644
--- a/spec/frontend/design_management/mock_data/design.js
+++ b/spec/frontend/design_management/mock_data/design.js
@@ -3,6 +3,8 @@ export default {
filename: 'test.jpg',
fullPath: 'full-design-path',
image: 'test.jpg',
+ description: 'Test description',
+ descriptionHtml: 'Test description',
updatedAt: '01-01-2019',
updatedBy: {
name: 'test',
diff --git a/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap b/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap
index 18b63082e4a..bd37d917faa 100644
--- a/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap
+++ b/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap
@@ -61,6 +61,12 @@ exports[`Design management design index page renders design index 1`] = `
ull-issue-path
</a>
+ <description-form-stub
+ design="[object Object]"
+ designvariables="[object Object]"
+ markdownpreviewpath="/project-path/preview_markdown?target_type=Issue"
+ />
+
<participants-stub
class="gl-mb-4"
lazy="true"
@@ -192,6 +198,12 @@ exports[`Design management design index page with error GlAlert is rendered in c
ull-issue-path
</a>
+ <description-form-stub
+ design="[object Object]"
+ designvariables="[object Object]"
+ markdownpreviewpath="/project-path/preview_markdown?target_type=Issue"
+ />
+
<participants-stub
class="gl-mb-4"
lazy="true"
diff --git a/spec/frontend/design_management/pages/design/index_spec.js b/spec/frontend/design_management/pages/design/index_spec.js
index fcb03ea3700..6cddb0cbbf1 100644
--- a/spec/frontend/design_management/pages/design/index_spec.js
+++ b/spec/frontend/design_management/pages/design/index_spec.js
@@ -188,6 +188,12 @@ describe('Design management design index page', () => {
markdownPreviewPath: '/project-path/preview_markdown?target_type=Issue',
resolvedDiscussionsExpanded: false,
isLoading: false,
+ designVariables: {
+ fullPath: 'project-path',
+ iid: '1',
+ filenames: ['gid::/gitlab/Design/1'],
+ atVersion: null,
+ },
});
});
diff --git a/spec/frontend/design_management/pages/index_spec.js b/spec/frontend/design_management/pages/index_spec.js
index 0a3f513918c..90caf956126 100644
--- a/spec/frontend/design_management/pages/index_spec.js
+++ b/spec/frontend/design_management/pages/index_spec.js
@@ -310,6 +310,8 @@ describe('Design management index page', () => {
},
image: '',
imageV432x230: '',
+ description: '',
+ descriptionHtml: '',
filename: 'test',
fullPath: '',
event: 'NONE',
diff --git a/spec/frontend/design_management/utils/design_management_utils_spec.js b/spec/frontend/design_management/utils/design_management_utils_spec.js
index dc6056badb9..cbfe8e3a243 100644
--- a/spec/frontend/design_management/utils/design_management_utils_spec.js
+++ b/spec/frontend/design_management/utils/design_management_utils_spec.js
@@ -89,6 +89,8 @@ describe('optimistic responses', () => {
id: -1,
image: '',
imageV432x230: '',
+ description: '',
+ descriptionHtml: '',
filename: 'test',
fullPath: '',
notesCount: 0,
diff --git a/spec/lib/banzai/filter/inline_alert_metrics_filter_spec.rb b/spec/lib/banzai/filter/inline_alert_metrics_filter_spec.rb
index be40195f001..cc6d9c67b1b 100644
--- a/spec/lib/banzai/filter/inline_alert_metrics_filter_spec.rb
+++ b/spec/lib/banzai/filter/inline_alert_metrics_filter_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Banzai::Filter::InlineAlertMetricsFilter do
+RSpec.describe Banzai::Filter::InlineAlertMetricsFilter, feature_category: :metrics do
include FilterSpecHelper
let(:params) { ['foo', 'bar', 12] }
diff --git a/spec/lib/banzai/filter/inline_cluster_metrics_filter_spec.rb b/spec/lib/banzai/filter/inline_cluster_metrics_filter_spec.rb
index fe048daa601..364a8160094 100644
--- a/spec/lib/banzai/filter/inline_cluster_metrics_filter_spec.rb
+++ b/spec/lib/banzai/filter/inline_cluster_metrics_filter_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Banzai::Filter::InlineClusterMetricsFilter do
+RSpec.describe Banzai::Filter::InlineClusterMetricsFilter, feature_category: :metrics do
include FilterSpecHelper
let!(:cluster) { create(:cluster) }
diff --git a/spec/lib/banzai/filter/inline_metrics_filter_spec.rb b/spec/lib/banzai/filter/inline_metrics_filter_spec.rb
index cdebd886b16..2a8f4507429 100644
--- a/spec/lib/banzai/filter/inline_metrics_filter_spec.rb
+++ b/spec/lib/banzai/filter/inline_metrics_filter_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Banzai::Filter::InlineMetricsFilter do
+RSpec.describe Banzai::Filter::InlineMetricsFilter, feature_category: :metrics do
include FilterSpecHelper
let(:environment_id) { 12 }
diff --git a/spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb b/spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb
index 9ccea1cc3e9..3512ae97f54 100644
--- a/spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb
+++ b/spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Banzai::Filter::InlineMetricsRedactorFilter do
+RSpec.describe Banzai::Filter::InlineMetricsRedactorFilter, feature_category: :metrics do
include FilterSpecHelper
let_it_be(:project) { create(:project) }
diff --git a/spec/lib/banzai/filter/markdown_filter_spec.rb b/spec/lib/banzai/filter/markdown_filter_spec.rb
index 64d65528426..251e6efe50b 100644
--- a/spec/lib/banzai/filter/markdown_filter_spec.rb
+++ b/spec/lib/banzai/filter/markdown_filter_spec.rb
@@ -23,54 +23,43 @@ RSpec.describe Banzai::Filter::MarkdownFilter, feature_category: :team_planning
end
describe 'code block' do
- context 'using CommonMark' do
- before do
- stub_const('Banzai::Filter::MarkdownFilter::DEFAULT_ENGINE', :common_mark)
- end
+ it 'adds language to lang attribute when specified' do
+ result = filter("```html\nsome code\n```", no_sourcepos: true)
- it 'adds language to lang attribute when specified' do
- result = filter("```html\nsome code\n```", no_sourcepos: true)
-
- expect(result).to start_with('<pre lang="html"><code>')
- end
+ expect(result).to start_with('<pre lang="html"><code>')
+ end
- it 'does not add language to lang attribute when not specified' do
- result = filter("```\nsome code\n```", no_sourcepos: true)
+ it 'does not add language to lang attribute when not specified' do
+ result = filter("```\nsome code\n```", no_sourcepos: true)
- expect(result).to start_with('<pre><code>')
- end
+ expect(result).to start_with('<pre><code>')
+ end
- it 'works with utf8 chars in language' do
- result = filter("```日\nsome code\n```", no_sourcepos: true)
+ it 'works with utf8 chars in language' do
+ result = filter("```日\nsome code\n```", no_sourcepos: true)
- expect(result).to start_with('<pre lang="日"><code>')
- end
+ expect(result).to start_with('<pre lang="日"><code>')
+ end
- it 'works with additional language parameters' do
- result = filter("```ruby:red gem foo\nsome code\n```", no_sourcepos: true)
+ it 'works with additional language parameters' do
+ result = filter("```ruby:red gem foo\nsome code\n```", no_sourcepos: true)
- expect(result).to start_with('<pre lang="ruby:red" data-meta="gem foo"><code>')
- end
+ expect(result).to include('lang="ruby:red"')
+ expect(result).to include('data-meta="gem foo"')
end
end
describe 'source line position' do
- context 'using CommonMark' do
- before do
- stub_const('Banzai::Filter::MarkdownFilter::DEFAULT_ENGINE', :common_mark)
- end
-
- it 'defaults to add data-sourcepos' do
- result = filter('test')
+ it 'defaults to add data-sourcepos' do
+ result = filter('test')
- expect(result).to eq '<p data-sourcepos="1:1-1:4">test</p>'
- end
+ expect(result).to eq '<p data-sourcepos="1:1-1:4">test</p>'
+ end
- it 'disables data-sourcepos' do
- result = filter('test', no_sourcepos: true)
+ it 'disables data-sourcepos' do
+ result = filter('test', no_sourcepos: true)
- expect(result).to eq '<p>test</p>'
- end
+ expect(result).to eq '<p>test</p>'
end
end
diff --git a/spec/lib/banzai/filter/truncate_visible_filter_spec.rb b/spec/lib/banzai/filter/truncate_visible_filter_spec.rb
index 404b23a886f..0d352850682 100644
--- a/spec/lib/banzai/filter/truncate_visible_filter_spec.rb
+++ b/spec/lib/banzai/filter/truncate_visible_filter_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe Banzai::Filter::TruncateVisibleFilter, feature_category: :team_pl
# Since we're truncating nodes of an html document, actually use the
# full pipeline to generate full documents.
def convert_markdown(text, context = {})
- Banzai::Pipeline::FullPipeline.to_html(text, { project: project }.merge(context))
+ Banzai::Pipeline::FullPipeline.to_html(text, { project: project, no_sourcepos: true }.merge(context))
end
shared_examples_for 'truncates text' do
diff --git a/spec/lib/banzai/pipeline/full_pipeline_spec.rb b/spec/lib/banzai/pipeline/full_pipeline_spec.rb
index 6654eec7241..5d56035f6df 100644
--- a/spec/lib/banzai/pipeline/full_pipeline_spec.rb
+++ b/spec/lib/banzai/pipeline/full_pipeline_spec.rb
@@ -9,6 +9,10 @@ RSpec.describe Banzai::Pipeline::FullPipeline, feature_category: :team_planning
let(:project) { create(:project, :public) }
let(:issue) { create(:issue, project: project) }
+ before do
+ stub_commonmark_sourcepos_disabled
+ end
+
it 'handles markdown inside a reference' do
markdown = "[some `code` inside](#{issue.to_reference})"
result = described_class.call(markdown, project: project)
@@ -135,6 +139,8 @@ RSpec.describe Banzai::Pipeline::FullPipeline, feature_category: :team_planning
end
it 'does not insert a table of contents' do
+ stub_commonmark_sourcepos_disabled
+
output = described_class.to_html(invalid_markdown, project: project)
expect(output).to include("test #{tag_html}")
@@ -163,6 +169,8 @@ RSpec.describe Banzai::Pipeline::FullPipeline, feature_category: :team_planning
end
it 'converts user reference with escaped underscore because of italics' do
+ stub_commonmark_sourcepos_disabled
+
markdown = '_@test\__'
output = described_class.to_html(markdown, project: project)
diff --git a/spec/lib/gitlab/auth/ldap/auth_hash_spec.rb b/spec/lib/gitlab/auth/ldap/auth_hash_spec.rb
index e8008aeaf57..c19d890a703 100644
--- a/spec/lib/gitlab/auth/ldap/auth_hash_spec.rb
+++ b/spec/lib/gitlab/auth/ldap/auth_hash_spec.rb
@@ -27,12 +27,12 @@ RSpec.describe Gitlab::Auth::Ldap::AuthHash do
end
let(:raw_info) do
- {
- uid: ['123456'],
- email: ['johnsmith@example.com'],
- cn: ['Smith, J.'],
- fullName: ['John Smith']
- }
+ Net::LDAP::Entry.new.tap do |entry|
+ entry['uid'] = ['123456']
+ entry['email'] = ['johnsmith@example.com']
+ entry['cn'] = ['Smith, J.']
+ entry['fullName'] = ['John Smith']
+ end
end
context "without overridden attributes" do
diff --git a/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb b/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb
index 81d423598f2..2246272d3af 100644
--- a/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb
+++ b/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb
@@ -27,10 +27,14 @@ RSpec.describe Gitlab::MarkdownCache::ActiveRecord::Extension do
end
let(:markdown) { '`Foo`' }
- let(:html) { '<p data-sourcepos="1:1-1:5" dir="auto"><code>Foo</code></p>' }
+ let(:html) { '<p dir="auto"><code>Foo</code></p>' }
let(:updated_markdown) { '`Bar`' }
- let(:updated_html) { '<p data-sourcepos="1:1-1:5" dir="auto"><code>Bar</code></p>' }
+ let(:updated_html) { '<p dir="auto"><code>Bar</code></p>' }
+
+ before do
+ stub_commonmark_sourcepos_disabled
+ end
context 'an unchanged markdown field' do
let(:thing) { klass.new(project_id: project.id, namespace_id: project.project_namespace_id, title: markdown) }
diff --git a/spec/lib/gitlab/markdown_cache/redis/extension_spec.rb b/spec/lib/gitlab/markdown_cache/redis/extension_spec.rb
index 8e75009099d..071f6e090c6 100644
--- a/spec/lib/gitlab/markdown_cache/redis/extension_spec.rb
+++ b/spec/lib/gitlab/markdown_cache/redis/extension_spec.rb
@@ -82,9 +82,13 @@ RSpec.describe Gitlab::MarkdownCache::Redis::Extension, :clean_gitlab_redis_cach
end
describe "#refresh_markdown_cache!" do
+ before do
+ stub_commonmark_sourcepos_disabled
+ end
+
it "stores the value in redis" do
expected_results = { "title_html" => "`Hello`",
- "description_html" => "<p data-sourcepos=\"1:1-1:7\" dir=\"auto\"><code>World</code></p>",
+ "description_html" => "<p dir=\"auto\"><code>World</code></p>",
"cached_markdown_version" => cache_version.to_s }
thing.refresh_markdown_cache!
@@ -101,7 +105,7 @@ RSpec.describe Gitlab::MarkdownCache::Redis::Extension, :clean_gitlab_redis_cach
thing.refresh_markdown_cache!
expect(thing.title_html).to eq('`Hello`')
- expect(thing.description_html).to eq("<p data-sourcepos=\"1:1-1:7\" dir=\"auto\"><code>World</code></p>")
+ expect(thing.description_html).to eq("<p dir=\"auto\"><code>World</code></p>")
expect(thing.cached_markdown_version).to eq(cache_version)
end
end
diff --git a/spec/migrations/20230412141541_reschedule_links_avoiding_duplication_spec.rb b/spec/migrations/20230412141541_reschedule_links_avoiding_duplication_spec.rb
index 06eccf03ca4..342504ca3c5 100644
--- a/spec/migrations/20230412141541_reschedule_links_avoiding_duplication_spec.rb
+++ b/spec/migrations/20230412141541_reschedule_links_avoiding_duplication_spec.rb
@@ -10,13 +10,7 @@ RSpec.describe RescheduleLinksAvoidingDuplication, :migration, feature_category:
it 'schedules a batched background migration' do
migrate!
- expect(migration).to have_scheduled_batched_migration(
- table_name: :vulnerability_occurrences,
- column_name: :id,
- interval: described_class::DELAY_INTERVAL,
- batch_size: described_class::BATCH_SIZE,
- sub_batch_size: described_class::SUB_BATCH_SIZE
- )
+ expect(migration).not_to have_scheduled_batched_migration
end
end
diff --git a/spec/migrations/20230522111534_reschedule_migration_for_links_from_metadata_spec.rb b/spec/migrations/20230522111534_reschedule_migration_for_links_from_metadata_spec.rb
new file mode 100644
index 00000000000..efaef3e6892
--- /dev/null
+++ b/spec/migrations/20230522111534_reschedule_migration_for_links_from_metadata_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe RescheduleMigrationForLinksFromMetadata, :migration, feature_category: :vulnerability_management do
+ let(:migration) { described_class::MIGRATION }
+
+ describe '#up' do
+ it 'schedules a batched background migration' do
+ migrate!
+
+ expect(migration).to have_scheduled_batched_migration(
+ table_name: :vulnerability_occurrences,
+ column_name: :id,
+ interval: described_class::DELAY_INTERVAL,
+ batch_size: described_class::BATCH_SIZE,
+ sub_batch_size: described_class::SUB_BATCH_SIZE
+ )
+ end
+ end
+
+ describe '#down' do
+ it 'deletes all batched migration records' do
+ migrate!
+ schema_migrate_down!
+
+ expect(migration).not_to have_scheduled_batched_migration
+ end
+ end
+end
diff --git a/spec/models/concerns/cache_markdown_field_spec.rb b/spec/models/concerns/cache_markdown_field_spec.rb
index f85f636ebe5..97e43f3494c 100644
--- a/spec/models/concerns/cache_markdown_field_spec.rb
+++ b/spec/models/concerns/cache_markdown_field_spec.rb
@@ -41,10 +41,10 @@ RSpec.describe CacheMarkdownField, :clean_gitlab_redis_cache do
end
let(:markdown) { '`Foo`' }
- let(:html) { '<p data-sourcepos="1:1-1:5" dir="auto"><code>Foo</code></p>' }
+ let(:html) { '<p dir="auto"><code>Foo</code></p>' }
let(:updated_markdown) { '`Bar`' }
- let(:updated_html) { '<p data-sourcepos="1:1-1:5" dir="auto"><code>Bar</code></p>' }
+ let(:updated_html) { '<p dir="auto"><code>Bar</code></p>' }
let(:cache_version) { Gitlab::MarkdownCache::CACHE_COMMONMARK_VERSION << 16 }
@@ -112,6 +112,7 @@ RSpec.describe CacheMarkdownField, :clean_gitlab_redis_cache do
let(:thing) { klass.new(description: markdown, description_html: html, cached_markdown_version: cache_version) }
before do
+ stub_commonmark_sourcepos_disabled
thing.description = updated_markdown
end
@@ -139,6 +140,7 @@ RSpec.describe CacheMarkdownField, :clean_gitlab_redis_cache do
let(:thing) { klass.new(description: markdown, description_html: html, cached_markdown_version: cache_version) }
before do
+ stub_commonmark_sourcepos_disabled
thing.description = updated_markdown
end
@@ -253,6 +255,7 @@ RSpec.describe CacheMarkdownField, :clean_gitlab_redis_cache do
context 'when the markdown cache is up to date' do
before do
+ stub_commonmark_sourcepos_disabled
thing.try(:save)
end
@@ -269,6 +272,7 @@ RSpec.describe CacheMarkdownField, :clean_gitlab_redis_cache do
context 'when a field can be cached' do
it 'returns the html' do
+ stub_commonmark_sourcepos_disabled
thing.description = updated_markdown
expect(thing.rendered_field_content(:description)).to eq updated_html
@@ -332,6 +336,7 @@ RSpec.describe CacheMarkdownField, :clean_gitlab_redis_cache do
describe '#save' do
context 'when cache is outdated' do
before do
+ stub_commonmark_sourcepos_disabled
thing.cached_markdown_version += 1
end
@@ -433,6 +438,7 @@ RSpec.describe CacheMarkdownField, :clean_gitlab_redis_cache do
end
it 'correctly updates cached HTML even if refresh_markdown_cache is called before updating the attribute' do
+ stub_commonmark_sourcepos_disabled
thing.refresh_markdown_cache
thing.update!(description: updated_markdown)
diff --git a/spec/models/diff_discussion_spec.rb b/spec/models/diff_discussion_spec.rb
index fdfc4ec7cc4..f51c994ca5d 100644
--- a/spec/models/diff_discussion_spec.rb
+++ b/spec/models/diff_discussion_spec.rb
@@ -14,8 +14,8 @@ RSpec.describe DiffDiscussion do
describe '#reply_attributes' do
it 'includes position and original_position' do
attributes = subject.reply_attributes
- expect(attributes[:position]).to eq(diff_note.position.to_json)
- expect(attributes[:original_position]).to eq(diff_note.original_position.to_json)
+ expect(attributes[:position]).to eq(Gitlab::Json.dump(diff_note.position.to_h))
+ expect(attributes[:original_position]).to eq(Gitlab::Json.dump(diff_note.original_position.to_h))
end
end
diff --git a/spec/models/release_highlight_spec.rb b/spec/models/release_highlight_spec.rb
index d51998cc556..50a607040b6 100644
--- a/spec/models/release_highlight_spec.rb
+++ b/spec/models/release_highlight_spec.rb
@@ -65,7 +65,9 @@ RSpec.describe ReleaseHighlight, :clean_gitlab_redis_cache, feature_category: :r
end
it 'parses the description as markdown and returns html, and links are target="_blank"' do
- expect(subject[:items].first['description']).to match('<p data-sourcepos="1:1-1:62" dir="auto">bright and sunshinin\' <a href="https://en.wikipedia.org/wiki/Day" rel="nofollow noreferrer noopener" target="_blank">day</a></p>')
+ stub_commonmark_sourcepos_disabled
+
+ expect(subject[:items].first['description']).to eq('<p dir="auto">bright and sunshinin\' <a href="https://en.wikipedia.org/wiki/Day" rel="nofollow noreferrer noopener" target="_blank">day</a></p>')
end
it 'logs an error if theres an error parsing markdown for an item, and skips it' do
diff --git a/spec/requests/api/graphql/ci/runner_spec.rb b/spec/requests/api/graphql/ci/runner_spec.rb
index 52b548ce8b9..63a657f3962 100644
--- a/spec/requests/api/graphql/ci/runner_spec.rb
+++ b/spec/requests/api/graphql/ci/runner_spec.rb
@@ -74,6 +74,8 @@ RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do
end
it 'retrieves expected fields' do
+ stub_commonmark_sourcepos_disabled
+
post_graphql(query, current_user: user)
runner_data = graphql_data_at(:runner)
diff --git a/spec/requests/projects/metrics_dashboard_spec.rb b/spec/requests/projects/metrics_dashboard_spec.rb
deleted file mode 100644
index 7e94bc6134d..00000000000
--- a/spec/requests/projects/metrics_dashboard_spec.rb
+++ /dev/null
@@ -1,171 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Projects::MetricsDashboardController', feature_category: :metrics do
- let_it_be(:project) { create(:project) }
- let_it_be(:environment) { create(:environment, project: project) }
- let_it_be(:environment2) { create(:environment, project: project) }
- let_it_be(:user) { project.first_owner }
-
- before do
- stub_feature_flags(remove_monitor_metrics: false)
- project.add_developer(user)
- login_as(user)
- stub_feature_flags(remove_monitor_metrics: false)
- end
-
- shared_examples 'metrics dashboard is unavailable' do
- context 'when metrics dashboard feature is unavailable' do
- before do
- stub_feature_flags(remove_monitor_metrics: true)
- end
-
- it 'returns 404 not found' do
- send_request
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
- end
-
- describe 'GET /:namespace/:project/-/metrics' do
- include_examples 'metrics dashboard is unavailable'
-
- it "redirects to default environment's metrics dashboard" do
- send_request
- expect(response).to redirect_to(dashboard_route(environment: environment))
- end
-
- it 'assigns default_environment' do
- send_request
- expect(assigns(:default_environment).id).to eq(environment.id)
- end
-
- it 'retains existing parameters when redirecting' do
- params = {
- dashboard_path: '.gitlab/dashboards/dashboard_path.yml',
- page: 'panel/new',
- group: 'System metrics (Kubernetes)',
- title: 'Memory Usage (Pod average)',
- y_label: 'Memory Used per Pod (MB)'
- }
- send_request(params)
-
- expect(response).to redirect_to(dashboard_route(params.merge(environment: environment.id)))
- end
-
- context 'with remove_monitor_metrics returning true' do
- before do
- stub_feature_flags(remove_monitor_metrics: true)
- end
-
- it 'renders 404 page' do
- send_request
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
-
- context 'with anonymous user and public dashboard visibility' do
- let(:anonymous_user) { create(:user) }
- let(:project) do
- create(:project, :public, :metrics_dashboard_enabled)
- end
-
- before do
- project.update!(metrics_dashboard_access_level: 'enabled')
-
- login_as(anonymous_user)
- end
-
- it 'returns 200' do
- send_request
-
- expect(response).to have_gitlab_http_status(:ok)
- end
- end
- end
-
- describe 'GET /:namespace/:project/-/metrics?environment=:environment.id' do
- include_examples 'metrics dashboard is unavailable'
-
- it 'returns 200' do
- send_request(environment: environment2.id)
- expect(response).to have_gitlab_http_status(:ok)
- end
-
- it 'assigns query param environment' do
- send_request(environment: environment2.id)
- expect(assigns(:environment).id).to eq(environment2.id)
- end
-
- context 'when query param environment does not exist' do
- it 'responds with 404' do
- send_request(environment: non_existing_record_id)
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
- end
-
- describe 'GET /:namespace/:project/-/metrics/:dashboard_path' do
- let(:dashboard_path) { '.gitlab/dashboards/dashboard_path.yml' }
-
- include_examples 'metrics dashboard is unavailable'
-
- it 'returns 200' do
- send_request(dashboard_path: dashboard_path, environment: environment.id)
- expect(response).to have_gitlab_http_status(:ok)
- end
-
- it 'assigns environment' do
- send_request(dashboard_path: dashboard_path, environment: environment.id)
- expect(assigns(:environment).id).to eq(environment.id)
- end
- end
-
- describe 'GET :/namespace/:project/-/metrics/:dashboard_path?environment=:environment.id' do
- let(:dashboard_path) { '.gitlab/dashboards/dashboard_path.yml' }
-
- include_examples 'metrics dashboard is unavailable'
-
- it 'returns 200' do
- send_request(dahboard_path: dashboard_path, environment: environment.id)
- expect(response).to have_gitlab_http_status(:ok)
- end
-
- it 'assigns query param environment' do
- send_request(dashboard_path: dashboard_path, environment: environment2.id)
- expect(assigns(:environment).id).to eq(environment2.id)
- end
-
- context 'when query param environment does not exist' do
- it 'responds with 404' do
- send_request(dashboard_path: dashboard_path, environment: non_existing_record_id)
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
- end
-
- describe 'GET :/namespace/:project/-/metrics/:page' do
- include_examples 'metrics dashboard is unavailable'
-
- it 'returns 200 with path param page' do
- send_request(page: 'panel/new', environment: environment.id)
-
- expect(response).to have_gitlab_http_status(:ok)
- end
-
- it 'returns 200 with dashboard and path param page' do
- send_request(dashboard_path: 'dashboard.yml', page: 'panel/new', environment: environment.id)
-
- expect(response).to have_gitlab_http_status(:ok)
- end
- end
-
- def send_request(params = {})
- get dashboard_route(params)
- end
-
- def dashboard_route(params = {})
- namespace_project_metrics_dashboard_path(namespace_id: project.namespace, project_id: project, **params)
- end
-end
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index c2458d3485f..c78adc2dcef 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -916,73 +916,6 @@ RSpec.describe 'project routing' do
end
end
- describe Projects::MetricsDashboardController, 'routing' do
- it 'routes to #show with no dashboard_path' do
- expect(get: "/gitlab/gitlabhq/-/metrics").to route_to(
- "projects/metrics_dashboard#show",
- **base_params
- )
- end
-
- it 'routes to #show with only dashboard_path' do
- expect(get: "/gitlab/gitlabhq/-/metrics/dashboard1.yml").to route_to(
- "projects/metrics_dashboard#show",
- dashboard_path: 'dashboard1.yml',
- **base_params
- )
- end
-
- it 'routes to #show' do
- expect(get: "/gitlab/gitlabhq/-/metrics/panel/new").to route_to(
- "projects/metrics_dashboard#show",
- **base_params
- )
- end
-
- it 'routes to #show with dashboard_path' do
- expect(get: "/gitlab/gitlabhq/-/metrics/config%2Fprometheus%2Fcommon_metrics.yml/panel/new").to route_to(
- "projects/metrics_dashboard#show",
- dashboard_path: 'config/prometheus/common_metrics.yml',
- **base_params
- )
- end
-
- it 'routes to 404 with invalid page' do
- expect(get: "/gitlab/gitlabhq/-/metrics/invalid_page").to route_to(
- 'application#route_not_found',
- unmatched_route: 'gitlab/gitlabhq/-/metrics/invalid_page'
- )
- end
-
- it 'routes to 404 without format for invalid page' do
- expect(get: "/gitlab/gitlabhq/-/metrics/invalid_page.md").to route_to(
- 'application#route_not_found',
- unmatched_route: 'gitlab/gitlabhq/-/metrics/invalid_page.md'
- )
- end
-
- it 'routes to 404 with invalid dashboard_path' do
- expect(get: "/gitlab/gitlabhq/-/metrics/invalid_dashboard").to route_to(
- 'application#route_not_found',
- unmatched_route: 'gitlab/gitlabhq/-/metrics/invalid_dashboard'
- )
- end
-
- it 'routes to 404 with invalid dashboard_path and valid page' do
- expect(get: "/gitlab/gitlabhq/-/metrics/dashboard1/panel/new").to route_to(
- 'application#route_not_found',
- unmatched_route: 'gitlab/gitlabhq/-/metrics/dashboard1/panel/new'
- )
- end
-
- it 'routes to 404 with valid dashboard_path and invalid page' do
- expect(get: "/gitlab/gitlabhq/-/metrics/dashboard1.yml/invalid_page").to route_to(
- 'application#route_not_found',
- unmatched_route: 'gitlab/gitlabhq/-/metrics/dashboard1.yml/invalid_page'
- )
- end
- end
-
context 'with a non-existent project' do
it 'routes to 404 with get request' do
expect(get: "/gitlab/not_exist").to route_to(
diff --git a/spec/services/projects/operations/update_service_spec.rb b/spec/services/projects/operations/update_service_spec.rb
index da45d3c8ebc..5f9b1a59bf9 100644
--- a/spec/services/projects/operations/update_service_spec.rb
+++ b/spec/services/projects/operations/update_service_spec.rb
@@ -92,61 +92,6 @@ RSpec.describe Projects::Operations::UpdateService, feature_category: :groups_an
end
end
- context 'metrics dashboard setting' do
- let(:params) do
- {
- metrics_setting_attributes: {
- external_dashboard_url: 'http://gitlab.com',
- dashboard_timezone: 'utc'
- }
- }
- end
-
- context 'without existing metrics dashboard setting' do
- it 'creates a setting' do
- expect(result[:status]).to eq(:success)
-
- expect(project.reload.metrics_setting.external_dashboard_url).to eq(
- 'http://gitlab.com'
- )
- expect(project.metrics_setting.dashboard_timezone).to eq('utc')
- end
- end
-
- context 'with existing metrics dashboard setting' do
- before do
- create(:project_metrics_setting, project: project)
- end
-
- it 'updates the settings' do
- expect(result[:status]).to eq(:success)
-
- expect(project.reload.metrics_setting.external_dashboard_url).to eq(
- 'http://gitlab.com'
- )
- expect(project.metrics_setting.dashboard_timezone).to eq('utc')
- end
- end
-
- context 'with blank external_dashboard_url' do
- let(:params) do
- {
- metrics_setting_attributes: {
- external_dashboard_url: '',
- dashboard_timezone: 'utc'
- }
- }
- end
-
- it 'updates dashboard_timezone' do
- expect(result[:status]).to eq(:success)
-
- expect(project.reload.metrics_setting.external_dashboard_url).to be(nil)
- expect(project.metrics_setting.dashboard_timezone).to eq('utc')
- end
- end
- end
-
context 'error tracking' do
context 'with existing error tracking setting' do
let(:params) do
@@ -354,62 +299,6 @@ RSpec.describe Projects::Operations::UpdateService, feature_category: :groups_an
end
end
- context 'grafana integration' do
- let(:params) do
- {
- grafana_integration_attributes: {
- grafana_url: 'http://new.grafana.com',
- token: 'VerySecureToken='
- }
- }
- end
-
- context 'without existing grafana integration' do
- it 'creates an integration' do
- expect(result[:status]).to eq(:success)
-
- expected_attrs = params[:grafana_integration_attributes]
- integration = project.reload.grafana_integration
-
- expect(integration.grafana_url).to eq(expected_attrs[:grafana_url])
- expect(integration.send(:token)).to eq(expected_attrs[:token])
- end
- end
-
- context 'with an existing grafana integration' do
- before do
- create(:grafana_integration, project: project)
- end
-
- it 'updates the settings' do
- expect(result[:status]).to eq(:success)
-
- expected_attrs = params[:grafana_integration_attributes]
- integration = project.reload.grafana_integration
-
- expect(integration.grafana_url).to eq(expected_attrs[:grafana_url])
- expect(integration.send(:token)).to eq(expected_attrs[:token])
- end
-
- context 'with all grafana attributes blank in params' do
- let(:params) do
- {
- grafana_integration_attributes: {
- grafana_url: '',
- token: ''
- }
- }
- end
-
- it 'destroys the metrics_setting entry in DB' do
- expect(result[:status]).to eq(:success)
-
- expect(project.reload.grafana_integration).to be_nil
- end
- end
- end
- end
-
context 'prometheus integration' do
context 'prometheus params were passed into service' do
let!(:prometheus_integration) do
diff --git a/spec/support/helpers/markdown_helpers.rb b/spec/support/helpers/markdown_helpers.rb
new file mode 100644
index 00000000000..9a25238465a
--- /dev/null
+++ b/spec/support/helpers/markdown_helpers.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+module MarkdownHelpers
+ def remove_sourcepos(html)
+ html.gsub(/\ ?data-sourcepos=".*?"/, '')
+ end
+end
diff --git a/spec/support/matchers/sourcepos_matchers.rb b/spec/support/matchers/sourcepos_matchers.rb
new file mode 100644
index 00000000000..903fe2bd201
--- /dev/null
+++ b/spec/support/matchers/sourcepos_matchers.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+# remove data-sourcepos from compare
+RSpec::Matchers.define :eq_no_sourcepos do |expected|
+ include MarkdownHelpers
+
+ match do |actual|
+ remove_sourcepos(actual) == expected
+ end
+
+ description do
+ "equal ignoring sourcepos #{expected}"
+ end
+end