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>2021-06-18 21:10:13 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-06-18 21:10:13 +0300
commitdd6e32bf47776514b8fe2abcfe7998503f16baab (patch)
treef47d5c93958146fcfc6dd0dba3a6e11a2f8061ab /spec
parentc8cc2fe990c52cabcb9912b2b01b5bf16b33d88f (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/factories/packages.rb6
-rw-r--r--spec/factories/packages/package_file.rb8
-rw-r--r--spec/features/merge_request/batch_comments_spec.rb4
-rw-r--r--spec/features/merge_request/user_comments_on_diff_spec.rb8
-rw-r--r--spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb4
-rw-r--r--spec/features/merge_request/user_posts_diff_notes_spec.rb16
-rw-r--r--spec/features/merge_request/user_sees_versions_spec.rb4
-rw-r--r--spec/features/merge_request/user_views_diffs_spec.rb6
-rw-r--r--spec/fixtures/packages/nuget/package.snupkgbin0 -> 4691 bytes
-rw-r--r--spec/fixtures/packages/nuget/with_package_types.nuspec14
-rw-r--r--spec/frontend/clusters/clusters_bundle_spec.js166
-rw-r--r--spec/frontend/clusters/components/__snapshots__/applications_spec.js.snap105
-rw-r--r--spec/frontend/clusters/components/application_row_spec.js505
-rw-r--r--spec/frontend/clusters/components/applications_spec.js510
-rw-r--r--spec/frontend/clusters/components/knative_domain_editor_spec.js179
-rw-r--r--spec/frontend/clusters/components/uninstall_application_button_spec.js39
-rw-r--r--spec/frontend/clusters/components/uninstall_application_confirmation_modal_spec.js57
-rw-r--r--spec/frontend/clusters/components/update_application_confirmation_modal_spec.js52
-rw-r--r--spec/frontend/clusters/services/application_state_machine_spec.js206
-rw-r--r--spec/frontend/clusters/services/crossplane_provider_stack_spec.js85
-rw-r--r--spec/frontend/clusters/services/mock_data.js155
-rw-r--r--spec/frontend/clusters/stores/clusters_store_spec.js192
-rw-r--r--spec/frontend/diffs/components/diff_content_spec.js17
-rw-r--r--spec/frontend/diffs/components/diff_row_utils_spec.js27
-rw-r--r--spec/frontend/diffs/components/inline_diff_table_row_spec.js325
-rw-r--r--spec/frontend/diffs/components/inline_diff_view_spec.js57
-rw-r--r--spec/frontend/diffs/components/parallel_diff_table_row_spec.js445
-rw-r--r--spec/frontend/diffs/components/parallel_diff_view_spec.js37
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_utils_spec.js16
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js20
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/tokens/base_token_spec.js34
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js2
-rw-r--r--spec/frontend_integration/diffs/diffs_interopability_spec.js82
-rw-r--r--spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb40
-rw-r--r--spec/models/ci/build_spec.rb17
-rw-r--r--spec/presenters/packages/nuget/service_index_presenter_spec.rb13
-rw-r--r--spec/requests/api/nuget_project_packages_spec.rb127
-rw-r--r--spec/services/packages/nuget/metadata_extraction_service_spec.rb13
-rw-r--r--spec/services/packages/nuget/update_package_from_metadata_service_spec.rb37
-rw-r--r--spec/spec_helper.rb1
-rw-r--r--spec/support/helpers/merge_request_diff_helpers.rb4
-rw-r--r--spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb132
42 files changed, 358 insertions, 3409 deletions
diff --git a/spec/factories/packages.rb b/spec/factories/packages.rb
index cedda8d0854..cd9c8a8bfbb 100644
--- a/spec/factories/packages.rb
+++ b/spec/factories/packages.rb
@@ -162,6 +162,12 @@ FactoryBot.define do
pkg.nuget_metadatum = build(:nuget_metadatum)
end
end
+
+ trait(:with_symbol_package) do
+ after :create do |package|
+ create :package_file, :snupkg, package: package, file_name: "#{package.name}.#{package.version}.snupkg"
+ end
+ end
end
factory :pypi_package do
diff --git a/spec/factories/packages/package_file.rb b/spec/factories/packages/package_file.rb
index d82fbe02311..9b60f769505 100644
--- a/spec/factories/packages/package_file.rb
+++ b/spec/factories/packages/package_file.rb
@@ -271,6 +271,14 @@ FactoryBot.define do
size { 300.kilobytes }
end
+ trait(:snupkg) do
+ package
+ file_fixture { 'spec/fixtures/packages/nuget/package.snupkg' }
+ file_name { 'package.snupkg' }
+ file_sha1 { '5fe852b2a6abd96c22c11fa1ff2fb19d9ce58b57' }
+ size { 300.kilobytes }
+ end
+
trait(:gem) do
package
file_fixture { 'spec/fixtures/packages/rubygems/package-0.0.1.gem' }
diff --git a/spec/features/merge_request/batch_comments_spec.rb b/spec/features/merge_request/batch_comments_spec.rb
index 5b11d9cb919..a5094be2bcf 100644
--- a/spec/features/merge_request/batch_comments_spec.rb
+++ b/spec/features/merge_request/batch_comments_spec.rb
@@ -259,8 +259,8 @@ RSpec.describe 'Merge request > Batch comments', :js do
end
def write_parallel_comment(line, **params)
- find("td[id='#{line}']").hover
- find(".is-over button").click
+ find("div[id='#{line}']").hover
+ find(".js-add-diff-note-button").click
write_comment(selector: "form[data-line-code='#{line}']", **params)
end
diff --git a/spec/features/merge_request/user_comments_on_diff_spec.rb b/spec/features/merge_request/user_comments_on_diff_spec.rb
index 0fd140a00bd..54c3fe738d2 100644
--- a/spec/features/merge_request/user_comments_on_diff_spec.rb
+++ b/spec/features/merge_request/user_comments_on_diff_spec.rb
@@ -132,7 +132,7 @@ RSpec.describe 'User comments on a diff', :js do
# In `files/ruby/popen.rb`
it 'allows comments for changes involving both sides' do
# click +15, select -13 add and verify comment
- click_diff_line(find('div[data-path="files/ruby/popen.rb"] .new_line a[data-linenumber="15"]').find(:xpath, '../..'), 'right')
+ click_diff_line(find('div[data-path="files/ruby/popen.rb"] .right-side a[data-linenumber="15"]').find(:xpath, '../../..'), 'right')
add_comment('-13', '+15')
end
@@ -141,7 +141,7 @@ RSpec.describe 'User comments on a diff', :js do
page.within('[data-path="files/ruby/popen.rb"]') do
all('.js-unfold-all')[0].click
end
- click_diff_line(find('div[data-path="files/ruby/popen.rb"] .old_line a[data-linenumber="9"]').find(:xpath, '../..'), 'left')
+ click_diff_line(find('div[data-path="files/ruby/popen.rb"] .left-side a[data-linenumber="9"]').find(:xpath, '../..'), 'left')
add_comment('1', '-9')
end
@@ -150,7 +150,7 @@ RSpec.describe 'User comments on a diff', :js do
page.within('[data-path="files/ruby/popen.rb"]') do
all('.js-unfold-all')[1].click
end
- click_diff_line(find('div[data-path="files/ruby/popen.rb"] .old_line a[data-linenumber="21"]').find(:xpath, '../..'), 'left')
+ click_diff_line(find('div[data-path="files/ruby/popen.rb"] .left-side a[data-linenumber="21"]').find(:xpath, '../..'), 'left')
add_comment('18', '21')
end
@@ -159,7 +159,7 @@ RSpec.describe 'User comments on a diff', :js do
page.within('[data-path="files/ruby/popen.rb"]') do
all('.js-unfold-down')[1].click
end
- click_diff_line(find('div[data-path="files/ruby/popen.rb"] .old_line a[data-linenumber="30"]').find(:xpath, '../..'), 'left')
+ click_diff_line(find('div[data-path="files/ruby/popen.rb"] .left-side a[data-linenumber="30"]').find(:xpath, '../..'), 'left')
add_comment('+28', '37')
end
end
diff --git a/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb b/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
index ac0c66524f0..3665ad91dd6 100644
--- a/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
+++ b/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
@@ -16,14 +16,14 @@ RSpec.describe 'Batch diffs', :js do
wait_for_requests
# Add discussion to first line of first file
- click_diff_line(find('.diff-file.file-holder:first-of-type tr.line_holder.new:first-of-type'))
+ click_diff_line(find('.diff-file.file-holder:first-of-type .line_holder .left-side:first-of-type'))
page.within('.js-discussion-note-form') do
fill_in('note_note', with: 'First Line Comment')
click_button('Add comment now')
end
# Add discussion to first line of last file
- click_diff_line(find('.diff-file.file-holder:last-of-type tr.line_holder.new:first-of-type'))
+ click_diff_line(find('.diff-file.file-holder:last-of-type .line_holder .left-side:first-of-type'))
page.within('.js-discussion-note-form') do
fill_in('note_note', with: 'Last Line Comment')
click_button('Add comment now')
diff --git a/spec/features/merge_request/user_posts_diff_notes_spec.rb b/spec/features/merge_request/user_posts_diff_notes_spec.rb
index 163ce10132e..c339a7d9976 100644
--- a/spec/features/merge_request/user_posts_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_posts_diff_notes_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe 'Merge request > User posts diff notes', :js do
let(:user) { project.creator }
let(:comment_button_class) { '.add-diff-note' }
let(:notes_holder_input_class) { 'js-temp-notes-holder' }
- let(:notes_holder_input_xpath) { './following-sibling::*[contains(concat(" ", @class, " "), " notes_holder ")]' }
+ let(:notes_holder_input_xpath) { '..//following-sibling::*[contains(concat(" ", @class, " "), " notes_holder ")]' }
let(:test_note_comment) { 'this is a test note!' }
before do
@@ -27,7 +27,7 @@ RSpec.describe 'Merge request > User posts diff notes', :js do
context 'with an old line on the left and no line on the right' do
it 'allows commenting on the left side' do
- should_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_23_22"]').find(:xpath, '..'), 'left')
+ should_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_23_22"]'), 'left')
end
it 'does not allow commenting on the right side' do
@@ -67,7 +67,7 @@ RSpec.describe 'Merge request > User posts diff notes', :js do
context 'with a match line' do
it 'does not allow commenting' do
- line_holder = find('.match', match: :first).find(:xpath, '..')
+ line_holder = find('.match', match: :first)
match_should_not_allow_commenting(line_holder)
end
end
@@ -81,17 +81,13 @@ RSpec.describe 'Merge request > User posts diff notes', :js do
wait_for_requests
end
- # The first `.js-unfold` unfolds upwards, therefore the first
- # `.line_holder` will be an unfolded line.
- let(:line_holder) { first('#a5cc2925ca8258af241be7e5b0381edf30266302 .line_holder') }
-
it 'allows commenting on the left side' do
- should_allow_commenting(line_holder, 'left')
+ should_allow_commenting(first('#a5cc2925ca8258af241be7e5b0381edf30266302 .line_holder [data-testid="left-side"]'))
end
it 'allows commenting on the right side' do
# Automatically shifts comment box to left side.
- should_allow_commenting(line_holder, 'right')
+ should_allow_commenting(first('#a5cc2925ca8258af241be7e5b0381edf30266302 .line_holder [data-testid="right-side"]'))
end
end
end
@@ -149,7 +145,7 @@ RSpec.describe 'Merge request > User posts diff notes', :js do
# The first `.js-unfold` unfolds upwards, therefore the first
# `.line_holder` will be an unfolded line.
- let(:line_holder) { first('.line_holder[id="a5cc2925ca8258af241be7e5b0381edf30266302_1_1"]') }
+ let(:line_holder) { first('[id="a5cc2925ca8258af241be7e5b0381edf30266302_1_1"]') }
it 'allows commenting' do
should_allow_commenting line_holder
diff --git a/spec/features/merge_request/user_sees_versions_spec.rb b/spec/features/merge_request/user_sees_versions_spec.rb
index 34ae082750b..d0d09e678c6 100644
--- a/spec/features/merge_request/user_sees_versions_spec.rb
+++ b/spec/features/merge_request/user_sees_versions_spec.rb
@@ -30,8 +30,8 @@ RSpec.describe 'Merge request > User sees versions', :js do
line_code = "#{file_id}_#{line_code}"
page.within(diff_file_selector) do
- find(".line_holder[id='#{line_code}'] td:nth-of-type(1)").hover
- find(".line_holder[id='#{line_code}'] button").click
+ first("[id='#{line_code}']").hover
+ first("[id='#{line_code}'] [role='button']").click
page.within("form[data-line-code='#{line_code}']") do
fill_in "note[note]", with: comment
diff --git a/spec/features/merge_request/user_views_diffs_spec.rb b/spec/features/merge_request/user_views_diffs_spec.rb
index d5061657c59..09dfe41a718 100644
--- a/spec/features/merge_request/user_views_diffs_spec.rb
+++ b/spec/features/merge_request/user_views_diffs_spec.rb
@@ -24,7 +24,7 @@ RSpec.describe 'User views diffs', :js do
page.within('.file-holder[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd"]') do
expect(find('.text-file')).to have_content('fileutils')
- expect(page).to have_selector('.new_line [data-linenumber="1"]', count: 1)
+ expect(page).to have_selector('[data-interop-type="new"] [data-linenumber="1"]')
end
end
@@ -32,8 +32,8 @@ RSpec.describe 'User views diffs', :js do
page.within('.file-holder[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd"]') do
all('.js-unfold-all')[1].click
- expect(page).to have_selector('.new_line [data-linenumber="24"]', count: 1)
- expect(page).not_to have_selector('.new_line [data-linenumber="1"]')
+ expect(page).to have_selector('[data-interop-type="new"] [data-linenumber="24"]', count: 1)
+ expect(page).not_to have_selector('[data-interop-type="new"] [data-linenumber="1"]')
end
end
diff --git a/spec/fixtures/packages/nuget/package.snupkg b/spec/fixtures/packages/nuget/package.snupkg
new file mode 100644
index 00000000000..9d97b36e792
--- /dev/null
+++ b/spec/fixtures/packages/nuget/package.snupkg
Binary files differ
diff --git a/spec/fixtures/packages/nuget/with_package_types.nuspec b/spec/fixtures/packages/nuget/with_package_types.nuspec
new file mode 100644
index 00000000000..b0e61e9d47e
--- /dev/null
+++ b/spec/fixtures/packages/nuget/with_package_types.nuspec
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
+ <metadata>
+ <id>Test.Package</id>
+ <version>3.5.2</version>
+ <authors>Test Author</authors>
+ <owners>Test Owner</owners>
+ <requireLicenseAcceptance>false</requireLicenseAcceptance>
+ <description>Package Description</description>
+ <packageTypes>
+ <packageType name="SymbolsPackage" />
+ </packageTypes>
+ </metadata>
+</package>
diff --git a/spec/frontend/clusters/clusters_bundle_spec.js b/spec/frontend/clusters/clusters_bundle_spec.js
index cd0eda2ab49..42990334f0a 100644
--- a/spec/frontend/clusters/clusters_bundle_spec.js
+++ b/spec/frontend/clusters/clusters_bundle_spec.js
@@ -2,15 +2,12 @@ import MockAdapter from 'axios-mock-adapter';
import { loadHTMLFixture } from 'helpers/fixtures';
import { setTestTimeout } from 'helpers/timeout';
import Clusters from '~/clusters/clusters_bundle';
-import { APPLICATION_STATUS, APPLICATIONS, RUNNER } from '~/clusters/constants';
import axios from '~/lib/utils/axios_utils';
import initProjectSelectDropdown from '~/project_select';
jest.mock('~/lib/utils/poll');
jest.mock('~/project_select');
-const { INSTALLING, INSTALLABLE, INSTALLED, UNINSTALLING } = APPLICATION_STATUS;
-
describe('Clusters', () => {
setTestTimeout(1000);
@@ -57,67 +54,6 @@ describe('Clusters', () => {
});
});
- describe('checkForNewInstalls', () => {
- const INITIAL_APP_MAP = {
- helm: { status: null, title: 'Helm Tiller' },
- ingress: { status: null, title: 'Ingress' },
- runner: { status: null, title: 'GitLab Runner' },
- };
-
- it('does not show alert when things transition from initial null state to something', () => {
- cluster.checkForNewInstalls(INITIAL_APP_MAP, {
- ...INITIAL_APP_MAP,
- helm: { status: INSTALLABLE, title: 'Helm Tiller' },
- });
-
- const flashMessage = document.querySelector('.js-cluster-application-notice .flash-text');
-
- expect(flashMessage).toBeNull();
- });
-
- it('shows an alert when something gets newly installed', () => {
- cluster.checkForNewInstalls(
- {
- ...INITIAL_APP_MAP,
- helm: { status: INSTALLING, title: 'Helm Tiller' },
- },
- {
- ...INITIAL_APP_MAP,
- helm: { status: INSTALLED, title: 'Helm Tiller' },
- },
- );
-
- const flashMessage = document.querySelector('.js-cluster-application-notice .flash-text');
-
- expect(flashMessage).not.toBeNull();
- expect(flashMessage.textContent.trim()).toEqual(
- 'Helm Tiller was successfully installed on your Kubernetes cluster',
- );
- });
-
- it('shows an alert when multiple things gets newly installed', () => {
- cluster.checkForNewInstalls(
- {
- ...INITIAL_APP_MAP,
- helm: { status: INSTALLING, title: 'Helm Tiller' },
- ingress: { status: INSTALLABLE, title: 'Ingress' },
- },
- {
- ...INITIAL_APP_MAP,
- helm: { status: INSTALLED, title: 'Helm Tiller' },
- ingress: { status: INSTALLED, title: 'Ingress' },
- },
- );
-
- const flashMessage = document.querySelector('.js-cluster-application-notice .flash-text');
-
- expect(flashMessage).not.toBeNull();
- expect(flashMessage.textContent.trim()).toEqual(
- 'Helm Tiller, Ingress was successfully installed on your Kubernetes cluster',
- );
- });
- });
-
describe('updateContainer', () => {
const { location } = window;
@@ -237,77 +173,6 @@ describe('Clusters', () => {
});
});
- describe('installApplication', () => {
- it.each(APPLICATIONS)('tries to install %s', (applicationId, done) => {
- jest.spyOn(cluster.service, 'installApplication').mockResolvedValue();
-
- cluster.store.state.applications[applicationId].status = INSTALLABLE;
-
- const params = {};
- if (applicationId === 'knative') {
- params.hostname = 'test-example.com';
- }
-
- // eslint-disable-next-line promise/valid-params
- cluster
- .installApplication({ id: applicationId, params })
- .then(() => {
- expect(cluster.store.state.applications[applicationId].status).toEqual(INSTALLING);
- expect(cluster.store.state.applications[applicationId].requestReason).toEqual(null);
- expect(cluster.service.installApplication).toHaveBeenCalledWith(applicationId, params);
- done();
- })
- .catch();
- });
-
- it('sets error request status when the request fails', () => {
- jest
- .spyOn(cluster.service, 'installApplication')
- .mockRejectedValueOnce(new Error('STUBBED ERROR'));
-
- cluster.store.state.applications.helm.status = INSTALLABLE;
-
- const promise = cluster.installApplication({ id: 'helm' });
-
- return promise.then(() => {
- expect(cluster.store.state.applications.helm.status).toEqual(INSTALLABLE);
- expect(cluster.store.state.applications.helm.installFailed).toBe(true);
-
- expect(cluster.store.state.applications.helm.requestReason).toBeDefined();
- });
- });
- });
-
- describe('uninstallApplication', () => {
- it.each(APPLICATIONS)('tries to uninstall %s', (applicationId) => {
- jest.spyOn(cluster.service, 'uninstallApplication').mockResolvedValueOnce();
-
- cluster.store.state.applications[applicationId].status = INSTALLED;
-
- cluster.uninstallApplication({ id: applicationId });
-
- expect(cluster.store.state.applications[applicationId].status).toEqual(UNINSTALLING);
- expect(cluster.store.state.applications[applicationId].requestReason).toEqual(null);
- expect(cluster.service.uninstallApplication).toHaveBeenCalledWith(applicationId);
- });
-
- it('sets error request status when the uninstall request fails', () => {
- jest
- .spyOn(cluster.service, 'uninstallApplication')
- .mockRejectedValueOnce(new Error('STUBBED ERROR'));
-
- cluster.store.state.applications.helm.status = INSTALLED;
-
- const promise = cluster.uninstallApplication({ id: 'helm' });
-
- return promise.then(() => {
- expect(cluster.store.state.applications.helm.status).toEqual(INSTALLED);
- expect(cluster.store.state.applications.helm.uninstallFailed).toBe(true);
- expect(cluster.store.state.applications.helm.requestReason).toBeDefined();
- });
- });
- });
-
describe('fetch cluster environments success', () => {
beforeEach(() => {
jest.spyOn(cluster.store, 'toggleFetchEnvironments').mockReturnThis();
@@ -328,7 +193,6 @@ describe('Clusters', () => {
describe('handleClusterStatusSuccess', () => {
beforeEach(() => {
jest.spyOn(cluster.store, 'updateStateFromServer').mockReturnThis();
- jest.spyOn(cluster, 'checkForNewInstalls').mockReturnThis();
jest.spyOn(cluster, 'updateContainer').mockReturnThis();
cluster.handleClusterStatusSuccess({ data: {} });
});
@@ -337,38 +201,8 @@ describe('Clusters', () => {
expect(cluster.store.updateStateFromServer).toHaveBeenCalled();
});
- it('checks for new installable apps', () => {
- expect(cluster.checkForNewInstalls).toHaveBeenCalled();
- });
-
it('updates message containers', () => {
expect(cluster.updateContainer).toHaveBeenCalled();
});
});
-
- describe('updateApplication', () => {
- const params = { version: '1.0.0' };
- let storeUpdateApplication;
- let installApplication;
-
- beforeEach(() => {
- storeUpdateApplication = jest.spyOn(cluster.store, 'updateApplication');
- installApplication = jest.spyOn(cluster.service, 'installApplication');
-
- cluster.updateApplication({ id: RUNNER, params });
- });
-
- afterEach(() => {
- storeUpdateApplication.mockRestore();
- installApplication.mockRestore();
- });
-
- it('calls store updateApplication method', () => {
- expect(storeUpdateApplication).toHaveBeenCalledWith(RUNNER);
- });
-
- it('sends installApplication request', () => {
- expect(installApplication).toHaveBeenCalledWith(RUNNER, params);
- });
- });
});
diff --git a/spec/frontend/clusters/components/__snapshots__/applications_spec.js.snap b/spec/frontend/clusters/components/__snapshots__/applications_spec.js.snap
deleted file mode 100644
index c2ace1b4e30..00000000000
--- a/spec/frontend/clusters/components/__snapshots__/applications_spec.js.snap
+++ /dev/null
@@ -1,105 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Applications Cert-Manager application shows the correct description 1`] = `
-<p
- data-testid="certManagerDescription"
->
- Cert-Manager is a native Kubernetes certificate management controller that helps with issuing certificates. Installing Cert-Manager on your cluster will issue a certificate by
- <a
- class="gl-link"
- href="https://letsencrypt.org/"
- rel="noopener noreferrer"
- target="_blank"
- >
- Let's Encrypt
- </a>
- and ensure that certificates are valid and up-to-date.
-</p>
-`;
-
-exports[`Applications Cilium application shows the correct description 1`] = `
-<p
- data-testid="ciliumDescription"
->
- Protect your clusters with GitLab Container Network Policies by enforcing how pods communicate with each other and other network endpoints.
- <a
- class="gl-link"
- href="cilium-help-path"
- rel="noopener"
- target="_blank"
- >
- Learn more about configuring Network Policies here.
- </a>
-</p>
-`;
-
-exports[`Applications Crossplane application shows the correct description 1`] = `
-<p
- data-testid="crossplaneDescription"
->
- Crossplane enables declarative provisioning of managed services from your cloud of choice using
- <code>
- kubectl
- </code>
- or
- <a
- class="gl-link"
- href="https://docs.gitlab.com/ee/user/clusters/applications.html#crossplane"
- rel="noopener noreferrer"
- target="_blank"
- >
- GitLab Integration
- </a>
- . Crossplane runs inside your Kubernetes cluster and supports secure connectivity and secrets management between app containers and the cloud services they depend on.
-</p>
-`;
-
-exports[`Applications Ingress application shows the correct warning message 1`] = `
-<span
- data-testid="ingressCostWarning"
->
- Installing Ingress may incur additional costs. Learn more about
- <a
- class="gl-link"
- href="https://cloud.google.com/compute/pricing#lb"
- rel="noopener noreferrer"
- target="_blank"
- >
- pricing
- </a>
- .
-</span>
-`;
-
-exports[`Applications Knative application shows the correct description 1`] = `
-<span
- data-testid="installed-via"
->
- installed via
- <a
- class="gl-link"
- href=""
- rel="noopener"
- target="_blank"
- >
- Cloud Run
- </a>
-</span>
-`;
-
-exports[`Applications Prometheus application shows the correct description 1`] = `
-<span
- data-testid="prometheusDescription"
->
- Prometheus is an open-source monitoring system with
- <a
- class="gl-link"
- href="https://docs.gitlab.com/ee/user/project/integrations/prometheus.html"
- rel="noopener noreferrer"
- target="_blank"
- >
- GitLab Integration
- </a>
- to monitor deployed applications.
-</span>
-`;
diff --git a/spec/frontend/clusters/components/application_row_spec.js b/spec/frontend/clusters/components/application_row_spec.js
deleted file mode 100644
index 6bad1db542b..00000000000
--- a/spec/frontend/clusters/components/application_row_spec.js
+++ /dev/null
@@ -1,505 +0,0 @@
-import { GlSprintf } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import ApplicationRow from '~/clusters/components/application_row.vue';
-import UninstallApplicationConfirmationModal from '~/clusters/components/uninstall_application_confirmation_modal.vue';
-import UpdateApplicationConfirmationModal from '~/clusters/components/update_application_confirmation_modal.vue';
-import { APPLICATION_STATUS, ELASTIC_STACK } from '~/clusters/constants';
-import eventHub from '~/clusters/event_hub';
-
-import { DEFAULT_APPLICATION_STATE } from '../services/mock_data';
-
-describe('Application Row', () => {
- let wrapper;
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- const mountComponent = (data) => {
- wrapper = shallowMount(ApplicationRow, {
- stubs: { GlSprintf },
- propsData: {
- ...DEFAULT_APPLICATION_STATE,
- ...data,
- },
- });
- };
-
- describe('Title', () => {
- it('shows title', () => {
- mountComponent({ titleLink: null });
-
- const title = wrapper.find('.js-cluster-application-title');
-
- expect(title.element).toBeInstanceOf(HTMLSpanElement);
- expect(title.text()).toEqual(DEFAULT_APPLICATION_STATE.title);
- });
-
- it('shows title link', () => {
- expect(DEFAULT_APPLICATION_STATE.titleLink).toBeDefined();
- mountComponent();
- const title = wrapper.find('.js-cluster-application-title');
-
- expect(title.element).toBeInstanceOf(HTMLAnchorElement);
- expect(title.text()).toEqual(DEFAULT_APPLICATION_STATE.title);
- });
- });
-
- describe('Install button', () => {
- const button = () => wrapper.find('.js-cluster-application-install-button');
- const checkButtonState = (label, loading, disabled) => {
- expect(button().text()).toEqual(label);
- expect(button().props('loading')).toEqual(loading);
- expect(button().props('disabled')).toEqual(disabled);
- };
-
- it('has indeterminate state on page load', () => {
- mountComponent({ status: null });
-
- expect(button().text()).toBe('');
- });
-
- it('has install button', () => {
- mountComponent();
-
- expect(button().exists()).toBe(true);
- });
-
- it('has disabled "Install" when APPLICATION_STATUS.NOT_INSTALLABLE', () => {
- mountComponent({ status: APPLICATION_STATUS.NOT_INSTALLABLE });
-
- checkButtonState('Install', false, true);
- });
-
- it('has enabled "Install" when APPLICATION_STATUS.INSTALLABLE', () => {
- mountComponent({ status: APPLICATION_STATUS.INSTALLABLE });
-
- checkButtonState('Install', false, false);
- });
-
- it('has loading "Installing" when APPLICATION_STATUS.INSTALLING', () => {
- mountComponent({ status: APPLICATION_STATUS.INSTALLING });
-
- checkButtonState('Installing', true, true);
- });
-
- it('has disabled "Install" when APPLICATION_STATUS.UNINSTALLED', () => {
- mountComponent({ status: APPLICATION_STATUS.UNINSTALLED });
-
- checkButtonState('Install', false, true);
- });
-
- it('has disabled "Externally installed" when APPLICATION_STATUS.EXTERNALLY_INSTALLED', () => {
- mountComponent({ status: APPLICATION_STATUS.EXTERNALLY_INSTALLED });
-
- checkButtonState('Externally installed', false, true);
- });
-
- it('has disabled "Installed" when application is installed and not uninstallable', () => {
- mountComponent({
- status: APPLICATION_STATUS.INSTALLED,
- installed: true,
- uninstallable: false,
- });
-
- checkButtonState('Installed', false, true);
- });
-
- it('hides when application is installed and uninstallable', () => {
- mountComponent({
- status: APPLICATION_STATUS.INSTALLED,
- installed: true,
- uninstallable: true,
- });
-
- expect(button().exists()).toBe(false);
- });
-
- it('has enabled "Install" when install fails', () => {
- mountComponent({
- status: APPLICATION_STATUS.INSTALLABLE,
- installFailed: true,
- });
-
- checkButtonState('Install', false, false);
- });
-
- it('has disabled "Install" when installation disabled', () => {
- mountComponent({
- status: APPLICATION_STATUS.INSTALLABLE,
- installable: false,
- });
-
- checkButtonState('Install', false, true);
- });
-
- it('has enabled "Install" when REQUEST_FAILURE (so you can try installing again)', () => {
- mountComponent({ status: APPLICATION_STATUS.INSTALLABLE });
-
- checkButtonState('Install', false, false);
- });
-
- it('clicking install button emits event', () => {
- const spy = jest.spyOn(eventHub, '$emit');
- mountComponent({ status: APPLICATION_STATUS.INSTALLABLE });
-
- button().vm.$emit('click');
-
- expect(spy).toHaveBeenCalledWith('installApplication', {
- id: DEFAULT_APPLICATION_STATE.id,
- params: {},
- });
- });
-
- it('clicking install button when installApplicationRequestParams are provided emits event', () => {
- const spy = jest.spyOn(eventHub, '$emit');
- mountComponent({
- status: APPLICATION_STATUS.INSTALLABLE,
- installApplicationRequestParams: { hostname: 'jupyter' },
- });
-
- button().vm.$emit('click');
-
- expect(spy).toHaveBeenCalledWith('installApplication', {
- id: DEFAULT_APPLICATION_STATE.id,
- params: { hostname: 'jupyter' },
- });
- });
-
- it('clicking disabled install button emits nothing', () => {
- const spy = jest.spyOn(eventHub, '$emit');
- mountComponent({ status: APPLICATION_STATUS.INSTALLING });
-
- expect(button().props('disabled')).toEqual(true);
-
- button().vm.$emit('click');
-
- expect(spy).not.toHaveBeenCalled();
- });
- });
-
- describe('Uninstall button', () => {
- it('displays button when app is installed and uninstallable', () => {
- mountComponent({
- installed: true,
- uninstallable: true,
- status: APPLICATION_STATUS.NOT_INSTALLABLE,
- });
- const uninstallButton = wrapper.find('.js-cluster-application-uninstall-button');
-
- expect(uninstallButton.exists()).toBe(true);
- });
-
- it('displays a success toast message if application uninstall was successful', async () => {
- mountComponent({
- title: 'GitLab Runner',
- uninstallSuccessful: false,
- });
-
- wrapper.vm.$toast = { show: jest.fn() };
- wrapper.setProps({ uninstallSuccessful: true });
-
- await wrapper.vm.$nextTick();
- expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(
- 'GitLab Runner uninstalled successfully.',
- );
- });
- });
-
- describe('when confirmation modal triggers confirm event', () => {
- it('triggers uninstallApplication event', () => {
- jest.spyOn(eventHub, '$emit');
- mountComponent();
- wrapper.find(UninstallApplicationConfirmationModal).vm.$emit('confirm');
-
- expect(eventHub.$emit).toHaveBeenCalledWith('uninstallApplication', {
- id: DEFAULT_APPLICATION_STATE.id,
- });
- });
- });
-
- describe('Update button', () => {
- const button = () => wrapper.find('.js-cluster-application-update-button');
-
- it('has indeterminate state on page load', () => {
- mountComponent();
-
- expect(button().exists()).toBe(false);
- });
-
- it('has enabled "Update" when "updateAvailable" is true', () => {
- mountComponent({ updateAvailable: true });
-
- expect(button().exists()).toBe(true);
- expect(button().text()).toContain('Update');
- });
-
- it('has enabled "Retry update" when update process fails', () => {
- mountComponent({
- status: APPLICATION_STATUS.INSTALLED,
- updateFailed: true,
- });
-
- expect(button().exists()).toBe(true);
- expect(button().text()).toContain('Retry update');
- });
-
- it('has disabled "Updating" when APPLICATION_STATUS.UPDATING', () => {
- mountComponent({ status: APPLICATION_STATUS.UPDATING });
-
- expect(button().exists()).toBe(true);
- expect(button().text()).toContain('Updating');
- });
-
- it('clicking update button emits event', () => {
- const spy = jest.spyOn(eventHub, '$emit');
- mountComponent({
- status: APPLICATION_STATUS.INSTALLED,
- updateAvailable: true,
- });
-
- button().vm.$emit('click');
-
- expect(spy).toHaveBeenCalledWith('updateApplication', {
- id: DEFAULT_APPLICATION_STATE.id,
- params: {},
- });
- });
-
- it('clicking disabled update button emits nothing', () => {
- const spy = jest.spyOn(eventHub, '$emit');
- mountComponent({ status: APPLICATION_STATUS.UPDATING });
-
- button().vm.$emit('click');
-
- expect(spy).not.toHaveBeenCalled();
- });
-
- it('displays an error message if application update failed', () => {
- mountComponent({
- title: 'GitLab Runner',
- status: APPLICATION_STATUS.INSTALLED,
- updateFailed: true,
- });
- const failureMessage = wrapper.find('.js-cluster-application-update-details');
-
- expect(failureMessage.exists()).toBe(true);
- expect(failureMessage.text()).toContain(
- 'Update failed. Please check the logs and try again.',
- );
- });
-
- it('displays a success toast message if application update was successful', async () => {
- mountComponent({
- title: 'GitLab Runner',
- updateSuccessful: false,
- });
-
- wrapper.vm.$toast = { show: jest.fn() };
- wrapper.setProps({ updateSuccessful: true });
-
- await wrapper.vm.$nextTick();
- expect(wrapper.vm.$toast.show).toHaveBeenCalledWith('GitLab Runner updated successfully.');
- });
-
- describe('when updating does not require confirmation', () => {
- beforeEach(() => mountComponent({ updateAvailable: true }));
-
- it('the modal is not rendered', () => {
- expect(wrapper.find(UpdateApplicationConfirmationModal).exists()).toBe(false);
- });
-
- it('the correct button is rendered', () => {
- expect(wrapper.find("[data-qa-selector='update_button']").exists()).toBe(true);
- });
- });
-
- describe('when updating requires confirmation', () => {
- beforeEach(() => {
- mountComponent({
- updateAvailable: true,
- id: ELASTIC_STACK,
- version: '1.1.2',
- });
- });
-
- it('displays a modal', () => {
- expect(wrapper.find(UpdateApplicationConfirmationModal).exists()).toBe(true);
- });
-
- it('the correct button is rendered', () => {
- expect(wrapper.find("[data-qa-selector='update_button_with_confirmation']").exists()).toBe(
- true,
- );
- });
-
- it('triggers updateApplication event', () => {
- jest.spyOn(eventHub, '$emit');
- wrapper.find(UpdateApplicationConfirmationModal).vm.$emit('confirm');
-
- expect(eventHub.$emit).toHaveBeenCalledWith('updateApplication', {
- id: ELASTIC_STACK,
- params: {},
- });
- });
- });
-
- describe('updating Elastic Stack special case', () => {
- it('needs confirmation if version is lower than 3.0.0', () => {
- mountComponent({
- updateAvailable: true,
- id: ELASTIC_STACK,
- version: '1.1.2',
- });
-
- expect(wrapper.find("[data-qa-selector='update_button_with_confirmation']").exists()).toBe(
- true,
- );
- expect(wrapper.find(UpdateApplicationConfirmationModal).exists()).toBe(true);
- });
-
- it('does not need confirmation is version is 3.0.0', () => {
- mountComponent({
- updateAvailable: true,
- id: ELASTIC_STACK,
- version: '3.0.0',
- });
-
- expect(wrapper.find("[data-qa-selector='update_button']").exists()).toBe(true);
- expect(wrapper.find(UpdateApplicationConfirmationModal).exists()).toBe(false);
- });
-
- it('does not need confirmation if version is higher than 3.0.0', () => {
- mountComponent({
- updateAvailable: true,
- id: ELASTIC_STACK,
- version: '5.2.1',
- });
-
- expect(wrapper.find("[data-qa-selector='update_button']").exists()).toBe(true);
- expect(wrapper.find(UpdateApplicationConfirmationModal).exists()).toBe(false);
- });
- });
- });
-
- describe('Version', () => {
- const updateDetails = () => wrapper.find('.js-cluster-application-update-details');
- const versionEl = () => wrapper.find('.js-cluster-application-update-version');
-
- it('displays a version number if application has been updated', () => {
- const version = '0.1.45';
- mountComponent({
- status: APPLICATION_STATUS.INSTALLED,
- updateSuccessful: true,
- version,
- });
-
- expect(updateDetails().text()).toBe(`Updated to chart v${version}`);
- });
-
- it('contains a link to the chart repo if application has been updated', () => {
- const version = '0.1.45';
- const chartRepo = 'https://gitlab.com/gitlab-org/charts/gitlab-runner';
- mountComponent({
- status: APPLICATION_STATUS.INSTALLED,
- updateSuccessful: true,
- chartRepo,
- version,
- });
-
- expect(versionEl().attributes('href')).toEqual(chartRepo);
- expect(versionEl().props('target')).toEqual('_blank');
- });
-
- it('does not display a version number if application update failed', () => {
- const version = '0.1.45';
- mountComponent({
- status: APPLICATION_STATUS.INSTALLED,
- updateFailed: true,
- version,
- });
-
- expect(updateDetails().text()).toBe('Update failed');
- expect(versionEl().exists()).toBe(false);
- });
-
- it('displays updating when the application update is currently updating', () => {
- mountComponent({
- status: APPLICATION_STATUS.UPDATING,
- updateSuccessful: true,
- version: '1.2.3',
- });
-
- expect(updateDetails().text()).toBe('Updating');
- expect(versionEl().exists()).toBe(false);
- });
- });
-
- describe('Error block', () => {
- const generalErrorMessage = () => wrapper.find('.js-cluster-application-general-error-message');
-
- describe('when nothing fails', () => {
- it('does not show error block', () => {
- mountComponent();
-
- expect(generalErrorMessage().exists()).toBe(false);
- });
- });
-
- describe('when install or uninstall fails', () => {
- const statusReason = 'We broke it 0.0';
- const requestReason = 'We broke the request 0.0';
-
- beforeEach(() => {
- mountComponent({
- status: APPLICATION_STATUS.ERROR,
- statusReason,
- requestReason,
- installFailed: true,
- });
- });
-
- it('shows status reason if it is available', () => {
- const statusErrorMessage = wrapper.find('.js-cluster-application-status-error-message');
-
- expect(statusErrorMessage.text()).toEqual(statusReason);
- });
-
- it('shows request reason if it is available', () => {
- const requestErrorMessage = wrapper.find('.js-cluster-application-request-error-message');
-
- expect(requestErrorMessage.text()).toEqual(requestReason);
- });
- });
-
- describe('when install fails', () => {
- beforeEach(() => {
- mountComponent({
- status: APPLICATION_STATUS.ERROR,
- installFailed: true,
- });
- });
-
- it('shows a general message indicating the installation failed', () => {
- expect(generalErrorMessage().text()).toEqual(
- `Something went wrong while installing ${DEFAULT_APPLICATION_STATE.title}`,
- );
- });
- });
-
- describe('when uninstall fails', () => {
- beforeEach(() => {
- mountComponent({
- status: APPLICATION_STATUS.ERROR,
- uninstallFailed: true,
- });
- });
-
- it('shows a general message indicating the uninstalling failed', () => {
- expect(generalErrorMessage().text()).toEqual(
- `Something went wrong while uninstalling ${DEFAULT_APPLICATION_STATE.title}`,
- );
- });
- });
- });
-});
diff --git a/spec/frontend/clusters/components/applications_spec.js b/spec/frontend/clusters/components/applications_spec.js
deleted file mode 100644
index 511f5fc1d89..00000000000
--- a/spec/frontend/clusters/components/applications_spec.js
+++ /dev/null
@@ -1,510 +0,0 @@
-import { shallowMount, mount } from '@vue/test-utils';
-import ApplicationRow from '~/clusters/components/application_row.vue';
-import Applications from '~/clusters/components/applications.vue';
-import CrossplaneProviderStack from '~/clusters/components/crossplane_provider_stack.vue';
-import KnativeDomainEditor from '~/clusters/components/knative_domain_editor.vue';
-import { CLUSTER_TYPE, PROVIDER_TYPE } from '~/clusters/constants';
-import eventHub from '~/clusters/event_hub';
-import { APPLICATIONS_MOCK_STATE } from '../services/mock_data';
-
-describe('Applications', () => {
- let wrapper;
-
- beforeEach(() => {
- gon.features = gon.features || {};
- });
-
- const createComponent = ({ applications, type, propsData } = {}, isShallow) => {
- const mountMethod = isShallow ? shallowMount : mount;
-
- wrapper = mountMethod(Applications, {
- stubs: { ApplicationRow },
- propsData: {
- type,
- applications: { ...APPLICATIONS_MOCK_STATE, ...applications },
- ...propsData,
- },
- });
- };
-
- const createShallowComponent = (options) => createComponent(options, true);
- const findByTestId = (id) => wrapper.find(`[data-testid="${id}"]`);
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('Project cluster applications', () => {
- beforeEach(() => {
- createComponent({ type: CLUSTER_TYPE.PROJECT });
- });
-
- it('renders a row for Ingress', () => {
- expect(wrapper.find('.js-cluster-application-row-ingress').exists()).toBe(true);
- });
-
- it('renders a row for Cert-Manager', () => {
- expect(wrapper.find('.js-cluster-application-row-cert_manager').exists()).toBe(true);
- });
-
- it('renders a row for Crossplane', () => {
- expect(wrapper.find('.js-cluster-application-row-crossplane').exists()).toBe(true);
- });
-
- it('renders a row for Prometheus', () => {
- expect(wrapper.find('.js-cluster-application-row-prometheus').exists()).toBe(true);
- });
-
- it('renders a row for GitLab Runner', () => {
- expect(wrapper.find('.js-cluster-application-row-runner').exists()).toBe(true);
- });
-
- it('renders a row for Jupyter', () => {
- expect(wrapper.find('.js-cluster-application-row-jupyter').exists()).toBe(true);
- });
-
- it('renders a row for Knative', () => {
- expect(wrapper.find('.js-cluster-application-row-knative').exists()).toBe(true);
- });
-
- it('renders a row for Elastic Stack', () => {
- expect(wrapper.find('.js-cluster-application-row-elastic_stack').exists()).toBe(true);
- });
-
- it('renders a row for Cilium', () => {
- expect(wrapper.find('.js-cluster-application-row-cilium').exists()).toBe(true);
- });
- });
-
- describe('Group cluster applications', () => {
- beforeEach(() => {
- createComponent({ type: CLUSTER_TYPE.GROUP });
- });
-
- it('renders a row for Ingress', () => {
- expect(wrapper.find('.js-cluster-application-row-ingress').exists()).toBe(true);
- });
-
- it('renders a row for Cert-Manager', () => {
- expect(wrapper.find('.js-cluster-application-row-cert_manager').exists()).toBe(true);
- });
-
- it('renders a row for Crossplane', () => {
- expect(wrapper.find('.js-cluster-application-row-crossplane').exists()).toBe(true);
- });
-
- it('renders a row for Prometheus', () => {
- expect(wrapper.find('.js-cluster-application-row-prometheus').exists()).toBe(true);
- });
-
- it('renders a row for GitLab Runner', () => {
- expect(wrapper.find('.js-cluster-application-row-runner').exists()).toBe(true);
- });
-
- it('renders a row for Jupyter', () => {
- expect(wrapper.find('.js-cluster-application-row-jupyter').exists()).toBe(true);
- });
-
- it('renders a row for Knative', () => {
- expect(wrapper.find('.js-cluster-application-row-knative').exists()).toBe(true);
- });
-
- it('renders a row for Elastic Stack', () => {
- expect(wrapper.find('.js-cluster-application-row-elastic_stack').exists()).toBe(true);
- });
-
- it('renders a row for Cilium', () => {
- expect(wrapper.find('.js-cluster-application-row-cilium').exists()).toBe(true);
- });
- });
-
- describe('Instance cluster applications', () => {
- beforeEach(() => {
- createComponent({ type: CLUSTER_TYPE.INSTANCE });
- });
-
- it('renders a row for Ingress', () => {
- expect(wrapper.find('.js-cluster-application-row-ingress').exists()).toBe(true);
- });
-
- it('renders a row for Cert-Manager', () => {
- expect(wrapper.find('.js-cluster-application-row-cert_manager').exists()).toBe(true);
- });
-
- it('renders a row for Crossplane', () => {
- expect(wrapper.find('.js-cluster-application-row-crossplane').exists()).toBe(true);
- });
-
- it('renders a row for Prometheus', () => {
- expect(wrapper.find('.js-cluster-application-row-prometheus').exists()).toBe(true);
- });
-
- it('renders a row for GitLab Runner', () => {
- expect(wrapper.find('.js-cluster-application-row-runner').exists()).toBe(true);
- });
-
- it('renders a row for Jupyter', () => {
- expect(wrapper.find('.js-cluster-application-row-jupyter').exists()).toBe(true);
- });
-
- it('renders a row for Knative', () => {
- expect(wrapper.find('.js-cluster-application-row-knative').exists()).toBe(true);
- });
-
- it('renders a row for Elastic Stack', () => {
- expect(wrapper.find('.js-cluster-application-row-elastic_stack').exists()).toBe(true);
- });
-
- it('renders a row for Cilium', () => {
- expect(wrapper.find('.js-cluster-application-row-cilium').exists()).toBe(true);
- });
- });
-
- describe('Helm application', () => {
- it('does not render a row for Helm Tiller', () => {
- createComponent();
- expect(wrapper.find('.js-cluster-application-row-helm').exists()).toBe(false);
- });
- });
-
- describe('Ingress application', () => {
- it('shows the correct warning message', () => {
- createComponent();
- expect(findByTestId('ingressCostWarning').element).toMatchSnapshot();
- });
-
- describe('when installed', () => {
- describe('with ip address', () => {
- it('renders ip address with a clipboard button', () => {
- createComponent({
- applications: {
- ingress: {
- title: 'Ingress',
- status: 'installed',
- externalIp: '0.0.0.0',
- },
- },
- });
-
- expect(wrapper.find('.js-endpoint').element.value).toEqual('0.0.0.0');
- expect(wrapper.find('.js-clipboard-btn').attributes('data-clipboard-text')).toEqual(
- '0.0.0.0',
- );
- });
- });
-
- describe('with hostname', () => {
- it('renders hostname with a clipboard button', () => {
- createComponent({
- applications: {
- ingress: {
- title: 'Ingress',
- status: 'installed',
- externalHostname: 'localhost.localdomain',
- },
- cert_manager: { title: 'Cert-Manager' },
- crossplane: { title: 'Crossplane', stack: '' },
- runner: { title: 'GitLab Runner' },
- prometheus: { title: 'Prometheus' },
- jupyter: { title: 'JupyterHub', hostname: '' },
- knative: { title: 'Knative', hostname: '' },
- elastic_stack: { title: 'Elastic Stack' },
- cilium: { title: 'GitLab Container Network Policies' },
- },
- });
-
- expect(wrapper.find('.js-endpoint').element.value).toEqual('localhost.localdomain');
-
- expect(wrapper.find('.js-clipboard-btn').attributes('data-clipboard-text')).toEqual(
- 'localhost.localdomain',
- );
- });
- });
-
- describe('without ip address', () => {
- it('renders an input text with a loading icon and an alert text', () => {
- createComponent({
- applications: {
- ingress: {
- title: 'Ingress',
- status: 'installed',
- },
- },
- });
-
- expect(wrapper.find('.js-ingress-ip-loading-icon').exists()).toBe(true);
- expect(wrapper.find('.js-no-endpoint-message').exists()).toBe(true);
- });
- });
- });
-
- describe('before installing', () => {
- it('does not render the IP address', () => {
- createComponent();
-
- expect(wrapper.text()).not.toContain('Ingress IP Address');
- expect(wrapper.find('.js-endpoint').exists()).toBe(false);
- });
- });
- });
-
- describe('Cert-Manager application', () => {
- it('shows the correct description', () => {
- createComponent();
- expect(findByTestId('certManagerDescription').element).toMatchSnapshot();
- });
-
- describe('when not installed', () => {
- it('renders email & allows editing', () => {
- createComponent({
- applications: {
- cert_manager: {
- title: 'Cert-Manager',
- email: 'before@example.com',
- status: 'installable',
- },
- },
- });
-
- expect(wrapper.find('.js-email').element.value).toEqual('before@example.com');
- expect(wrapper.find('.js-email').attributes('readonly')).toBe(undefined);
- });
- });
-
- describe('when installed', () => {
- it('renders email in readonly', () => {
- createComponent({
- applications: {
- cert_manager: {
- title: 'Cert-Manager',
- email: 'after@example.com',
- status: 'installed',
- },
- },
- });
-
- expect(wrapper.find('.js-email').element.value).toEqual('after@example.com');
- expect(wrapper.find('.js-email').attributes('readonly')).toEqual('readonly');
- });
- });
- });
-
- describe('Jupyter application', () => {
- describe('with ingress installed with ip & jupyter installable', () => {
- it('renders hostname active input', () => {
- createComponent({
- applications: {
- ingress: {
- title: 'Ingress',
- status: 'installed',
- externalIp: '1.1.1.1',
- },
- },
- });
-
- expect(
- wrapper.find('.js-cluster-application-row-jupyter .js-hostname').attributes('readonly'),
- ).toEqual(undefined);
- });
- });
-
- describe('with ingress installed without external ip', () => {
- it('does not render hostname input', () => {
- createComponent({
- applications: {
- ingress: { title: 'Ingress', status: 'installed' },
- },
- });
-
- expect(wrapper.find('.js-cluster-application-row-jupyter .js-hostname').exists()).toBe(
- false,
- );
- });
- });
-
- describe('with ingress & jupyter installed', () => {
- it('renders readonly input', () => {
- createComponent({
- applications: {
- ingress: {
- title: 'Ingress',
- status: 'installed',
- externalIp: '1.1.1.1',
- },
- jupyter: { title: 'JupyterHub', status: 'installed', hostname: '' },
- },
- });
-
- expect(
- wrapper.find('.js-cluster-application-row-jupyter .js-hostname').attributes('readonly'),
- ).toEqual('readonly');
- });
- });
-
- describe('without ingress installed', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('does not render input', () => {
- expect(wrapper.find('.js-cluster-application-row-jupyter .js-hostname').exists()).toBe(
- false,
- );
- });
- });
- });
-
- describe('Prometheus application', () => {
- it('shows the correct description', () => {
- createComponent();
- expect(findByTestId('prometheusDescription').element).toMatchSnapshot();
- });
- });
-
- describe('Knative application', () => {
- const availableDomain = {
- id: 4,
- domain: 'newhostname.com',
- };
- const propsData = {
- applications: {
- knative: {
- title: 'Knative',
- hostname: 'example.com',
- status: 'installed',
- externalIp: '1.1.1.1',
- installed: true,
- availableDomains: [availableDomain],
- pagesDomain: null,
- },
- },
- };
- let knativeDomainEditor;
-
- beforeEach(() => {
- createShallowComponent(propsData);
- jest.spyOn(eventHub, '$emit');
-
- knativeDomainEditor = wrapper.find(KnativeDomainEditor);
- });
-
- it('shows the correct description', async () => {
- createComponent();
- wrapper.setProps({
- providerType: PROVIDER_TYPE.GCP,
- preInstalledKnative: true,
- });
-
- await wrapper.vm.$nextTick();
-
- expect(findByTestId('installed-via').element).toMatchSnapshot();
- });
-
- it('emits saveKnativeDomain event when knative domain editor emits save event', () => {
- propsData.applications.knative.hostname = availableDomain.domain;
- propsData.applications.knative.pagesDomain = availableDomain;
- knativeDomainEditor.vm.$emit('save');
-
- expect(eventHub.$emit).toHaveBeenCalledWith('saveKnativeDomain', {
- id: 'knative',
- params: {
- hostname: availableDomain.domain,
- pages_domain_id: availableDomain.id,
- },
- });
- });
-
- it('emits saveKnativeDomain event when knative domain editor emits save event with custom domain', () => {
- const newHostName = 'someothernewhostname.com';
- propsData.applications.knative.hostname = newHostName;
- propsData.applications.knative.pagesDomain = null;
- knativeDomainEditor.vm.$emit('save');
-
- expect(eventHub.$emit).toHaveBeenCalledWith('saveKnativeDomain', {
- id: 'knative',
- params: {
- hostname: newHostName,
- pages_domain_id: undefined,
- },
- });
- });
-
- it('emits setKnativeHostname event when knative domain editor emits change event', () => {
- wrapper.find(KnativeDomainEditor).vm.$emit('set', {
- domain: availableDomain.domain,
- domainId: availableDomain.id,
- });
-
- expect(eventHub.$emit).toHaveBeenCalledWith('setKnativeDomain', {
- id: 'knative',
- domain: availableDomain.domain,
- domainId: availableDomain.id,
- });
- });
- });
-
- describe('Crossplane application', () => {
- const propsData = {
- applications: {
- crossplane: {
- title: 'Crossplane',
- stack: {
- code: '',
- },
- },
- },
- };
-
- beforeEach(() => createShallowComponent(propsData));
-
- it('renders the correct Component', () => {
- const crossplane = wrapper.find(CrossplaneProviderStack);
- expect(crossplane.exists()).toBe(true);
- });
-
- it('shows the correct description', () => {
- createComponent();
- expect(findByTestId('crossplaneDescription').element).toMatchSnapshot();
- });
- });
-
- describe('Elastic Stack application', () => {
- describe('with elastic stack installable', () => {
- it('renders the install button enabled', () => {
- createComponent();
-
- expect(
- wrapper
- .find(
- '.js-cluster-application-row-elastic_stack .js-cluster-application-install-button',
- )
- .attributes('disabled'),
- ).toBeUndefined();
- });
- });
-
- describe('elastic stack installed', () => {
- it('renders uninstall button', () => {
- createComponent({
- applications: {
- elastic_stack: { title: 'Elastic Stack', status: 'installed' },
- },
- });
-
- expect(
- wrapper
- .find(
- '.js-cluster-application-row-elastic_stack .js-cluster-application-install-button',
- )
- .attributes('disabled'),
- ).toEqual('disabled');
- });
- });
- });
-
- describe('Cilium application', () => {
- it('shows the correct description', () => {
- createComponent({ propsData: { ciliumHelpPath: 'cilium-help-path' } });
- expect(findByTestId('ciliumDescription').element).toMatchSnapshot();
- });
- });
-});
diff --git a/spec/frontend/clusters/components/knative_domain_editor_spec.js b/spec/frontend/clusters/components/knative_domain_editor_spec.js
deleted file mode 100644
index 207eb071171..00000000000
--- a/spec/frontend/clusters/components/knative_domain_editor_spec.js
+++ /dev/null
@@ -1,179 +0,0 @@
-import { GlDropdownItem, GlButton } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import KnativeDomainEditor from '~/clusters/components/knative_domain_editor.vue';
-import { APPLICATION_STATUS } from '~/clusters/constants';
-
-const { UPDATING } = APPLICATION_STATUS;
-
-describe('KnativeDomainEditor', () => {
- let wrapper;
- let knative;
-
- const createComponent = (props = {}) => {
- wrapper = shallowMount(KnativeDomainEditor, {
- propsData: { ...props },
- });
- };
-
- beforeEach(() => {
- knative = {
- title: 'Knative',
- hostname: 'example.com',
- installed: true,
- };
- });
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- describe('knative has an assigned IP address', () => {
- beforeEach(() => {
- knative.externalIp = '1.1.1.1';
- createComponent({ knative });
- });
-
- it('renders ip address with a clipboard button', () => {
- expect(wrapper.find('.js-knative-endpoint').exists()).toBe(true);
- expect(wrapper.find('.js-knative-endpoint').element.value).toEqual(knative.externalIp);
- });
-
- it('displays ip address clipboard button', () => {
- expect(wrapper.find('.js-knative-endpoint-clipboard-btn').attributes('text')).toEqual(
- knative.externalIp,
- );
- });
-
- it('renders domain & allows editing', () => {
- const domainNameInput = wrapper.find('.js-knative-domainname');
-
- expect(domainNameInput.element.value).toEqual(knative.hostname);
- expect(domainNameInput.attributes('readonly')).toBeFalsy();
- });
-
- it('renders an update/save Knative domain button', () => {
- expect(wrapper.find('.js-knative-save-domain-button').exists()).toBe(true);
- });
- });
-
- describe('knative without ip address', () => {
- beforeEach(() => {
- knative.externalIp = null;
- createComponent({ knative });
- });
-
- it('renders an input text with a loading icon', () => {
- expect(wrapper.find('.js-knative-ip-loading-icon').exists()).toBe(true);
- });
-
- it('renders message indicating there is not IP address assigned', () => {
- expect(wrapper.find('.js-no-knative-endpoint-message').exists()).toBe(true);
- });
- });
-
- describe('clicking save changes button', () => {
- beforeEach(() => {
- createComponent({ knative });
- });
-
- it('triggers save event and pass current knative hostname', () => {
- wrapper.find(GlButton).vm.$emit('click');
- return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.emitted('save').length).toEqual(1);
- });
- });
- });
-
- describe('when knative domain name was saved successfully', () => {
- beforeEach(() => {
- createComponent({ knative });
- });
-
- it('displays toast indicating a successful update', () => {
- wrapper.vm.$toast = { show: jest.fn() };
- wrapper.setProps({ knative: { updateSuccessful: true, ...knative } });
-
- return wrapper.vm.$nextTick(() => {
- expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(
- 'Knative domain name was updated successfully.',
- );
- });
- });
- });
-
- describe('when knative domain name input changes', () => {
- it('emits "set" event with updated domain name', () => {
- const newDomain = {
- id: 4,
- domain: 'newhostname.com',
- };
-
- createComponent({ knative: { ...knative, availableDomains: [newDomain] } });
- jest.spyOn(wrapper.vm, 'selectDomain');
-
- wrapper.find(GlDropdownItem).vm.$emit('click');
-
- return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.vm.selectDomain).toHaveBeenCalledWith(newDomain);
- expect(wrapper.emitted('set')[0]).toEqual([
- {
- domain: newDomain.domain,
- domainId: newDomain.id,
- },
- ]);
- });
- });
-
- it('emits "set" event with updated custom domain name', () => {
- const newHostname = 'newhostname.com';
-
- createComponent({ knative });
- jest.spyOn(wrapper.vm, 'selectCustomDomain');
-
- wrapper.setData({ knativeHostname: newHostname });
-
- return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.vm.selectCustomDomain).toHaveBeenCalledWith(newHostname);
- expect(wrapper.emitted('set')[0]).toEqual([
- {
- domain: newHostname,
- domainId: null,
- },
- ]);
- });
- });
- });
-
- describe('when updating knative domain name failed', () => {
- beforeEach(() => {
- createComponent({ knative });
- });
-
- it('displays an error banner indicating the operation failure', () => {
- wrapper.setProps({ knative: { updateFailed: true, ...knative } });
-
- return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.find('.js-cluster-knative-domain-name-failure-message').exists()).toBe(true);
- });
- });
- });
-
- describe(`when knative status is ${UPDATING}`, () => {
- beforeEach(() => {
- createComponent({ knative: { status: UPDATING, ...knative } });
- });
-
- it('renders loading spinner in save button', () => {
- expect(wrapper.find(GlButton).props('loading')).toBe(true);
- });
-
- it('renders disabled save button', () => {
- expect(wrapper.find(GlButton).props('disabled')).toBe(true);
- });
-
- it('renders save button with "Saving" label', () => {
- expect(wrapper.find(GlButton).text()).toBe('Saving');
- });
- });
-});
diff --git a/spec/frontend/clusters/components/uninstall_application_button_spec.js b/spec/frontend/clusters/components/uninstall_application_button_spec.js
deleted file mode 100644
index 2596820e5ac..00000000000
--- a/spec/frontend/clusters/components/uninstall_application_button_spec.js
+++ /dev/null
@@ -1,39 +0,0 @@
-import { GlButton } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import UninstallApplicationButton from '~/clusters/components/uninstall_application_button.vue';
-import { APPLICATION_STATUS } from '~/clusters/constants';
-
-const { INSTALLED, UPDATING, UNINSTALLING } = APPLICATION_STATUS;
-
-describe('UninstallApplicationButton', () => {
- let wrapper;
-
- const createComponent = (props = {}) => {
- wrapper = shallowMount(UninstallApplicationButton, {
- propsData: { ...props },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe.each`
- status | loading | disabled | text
- ${INSTALLED} | ${false} | ${false} | ${'Uninstall'}
- ${UPDATING} | ${false} | ${true} | ${'Uninstall'}
- ${UNINSTALLING} | ${true} | ${true} | ${'Uninstalling'}
- `('when app status is $status', ({ loading, disabled, status, text }) => {
- beforeEach(() => {
- createComponent({ status });
- });
-
- it(`renders a button with loading=${loading} and disabled=${disabled}`, () => {
- expect(wrapper.find(GlButton).props()).toMatchObject({ loading, disabled });
- });
-
- it(`renders a button with text="${text}"`, () => {
- expect(wrapper.find(GlButton).text()).toBe(text);
- });
- });
-});
diff --git a/spec/frontend/clusters/components/uninstall_application_confirmation_modal_spec.js b/spec/frontend/clusters/components/uninstall_application_confirmation_modal_spec.js
deleted file mode 100644
index 74ae4ecc486..00000000000
--- a/spec/frontend/clusters/components/uninstall_application_confirmation_modal_spec.js
+++ /dev/null
@@ -1,57 +0,0 @@
-import { GlModal } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import UninstallApplicationConfirmationModal from '~/clusters/components/uninstall_application_confirmation_modal.vue';
-import { INGRESS } from '~/clusters/constants';
-
-describe('UninstallApplicationConfirmationModal', () => {
- let wrapper;
- const appTitle = 'Ingress';
-
- const createComponent = (props = {}) => {
- wrapper = shallowMount(UninstallApplicationConfirmationModal, {
- propsData: { ...props },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- beforeEach(() => {
- createComponent({ application: INGRESS, applicationTitle: appTitle });
- });
-
- it(`renders a modal with a title "Uninstall ${appTitle}"`, () => {
- expect(wrapper.find(GlModal).attributes('title')).toEqual(`Uninstall ${appTitle}`);
- });
-
- it(`renders a modal with an ok button labeled "Uninstall ${appTitle}"`, () => {
- expect(wrapper.find(GlModal).attributes('ok-title')).toEqual(`Uninstall ${appTitle}`);
- });
-
- describe('when ok button is clicked', () => {
- beforeEach(() => {
- jest.spyOn(wrapper.vm, 'trackUninstallButtonClick');
- wrapper.find(GlModal).vm.$emit('ok');
- });
-
- it('emits confirm event', () =>
- wrapper.vm.$nextTick().then(() => {
- expect(wrapper.emitted('confirm')).toBeTruthy();
- }));
-
- it('calls track uninstall button click mixin', () => {
- expect(wrapper.vm.trackUninstallButtonClick).toHaveBeenCalledWith(INGRESS);
- });
- });
-
- it('displays a warning text indicating the app will be uninstalled', () => {
- expect(wrapper.text()).toContain(`You are about to uninstall ${appTitle} from your cluster.`);
- });
-
- it('displays a custom warning text depending on the application', () => {
- expect(wrapper.text()).toContain(
- `The associated load balancer and IP will be deleted and cannot be restored.`,
- );
- });
-});
diff --git a/spec/frontend/clusters/components/update_application_confirmation_modal_spec.js b/spec/frontend/clusters/components/update_application_confirmation_modal_spec.js
deleted file mode 100644
index e933f17a980..00000000000
--- a/spec/frontend/clusters/components/update_application_confirmation_modal_spec.js
+++ /dev/null
@@ -1,52 +0,0 @@
-import { GlModal } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import UpdateApplicationConfirmationModal from '~/clusters/components/update_application_confirmation_modal.vue';
-import { ELASTIC_STACK } from '~/clusters/constants';
-
-describe('UpdateApplicationConfirmationModal', () => {
- let wrapper;
- const appTitle = 'Elastic stack';
-
- const createComponent = (props = {}) => {
- wrapper = shallowMount(UpdateApplicationConfirmationModal, {
- propsData: { ...props },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- beforeEach(() => {
- createComponent({ application: ELASTIC_STACK, applicationTitle: appTitle });
- });
-
- it(`renders a modal with a title "Update ${appTitle}"`, () => {
- expect(wrapper.find(GlModal).attributes('title')).toEqual(`Update ${appTitle}`);
- });
-
- it(`renders a modal with an ok button labeled "Update ${appTitle}"`, () => {
- expect(wrapper.find(GlModal).attributes('ok-title')).toEqual(`Update ${appTitle}`);
- });
-
- describe('when ok button is clicked', () => {
- beforeEach(() => {
- wrapper.find(GlModal).vm.$emit('ok');
- });
-
- it('emits confirm event', () =>
- wrapper.vm.$nextTick().then(() => {
- expect(wrapper.emitted('confirm')).toBeTruthy();
- }));
-
- it('displays a warning text indicating the app will be updated', () => {
- expect(wrapper.text()).toContain(`You are about to update ${appTitle} on your cluster.`);
- });
-
- it('displays a custom warning text depending on the application', () => {
- expect(wrapper.text()).toContain(
- `Your Elasticsearch cluster will be re-created during this upgrade. Your logs will be re-indexed, and you will lose historical logs from hosts terminated in the last 30 days.`,
- );
- });
- });
-});
diff --git a/spec/frontend/clusters/services/application_state_machine_spec.js b/spec/frontend/clusters/services/application_state_machine_spec.js
deleted file mode 100644
index 4e731e331c2..00000000000
--- a/spec/frontend/clusters/services/application_state_machine_spec.js
+++ /dev/null
@@ -1,206 +0,0 @@
-import {
- APPLICATION_STATUS,
- UNINSTALL_EVENT,
- UPDATE_EVENT,
- INSTALL_EVENT,
-} from '~/clusters/constants';
-import transitionApplicationState from '~/clusters/services/application_state_machine';
-
-const {
- NO_STATUS,
- SCHEDULED,
- NOT_INSTALLABLE,
- INSTALLABLE,
- INSTALLING,
- INSTALLED,
- ERROR,
- UPDATING,
- UPDATED,
- UPDATE_ERRORED,
- UNINSTALLING,
- UNINSTALL_ERRORED,
- UNINSTALLED,
- PRE_INSTALLED,
- EXTERNALLY_INSTALLED,
-} = APPLICATION_STATUS;
-
-const NO_EFFECTS = 'no effects';
-
-describe('applicationStateMachine', () => {
- const noEffectsToEmptyObject = (effects) => (typeof effects === 'string' ? {} : effects);
-
- describe(`current state is ${NO_STATUS}`, () => {
- it.each`
- expectedState | event | effects
- ${INSTALLING} | ${SCHEDULED} | ${NO_EFFECTS}
- ${NOT_INSTALLABLE} | ${NOT_INSTALLABLE} | ${NO_EFFECTS}
- ${INSTALLABLE} | ${INSTALLABLE} | ${NO_EFFECTS}
- ${INSTALLING} | ${INSTALLING} | ${NO_EFFECTS}
- ${INSTALLED} | ${INSTALLED} | ${NO_EFFECTS}
- ${INSTALLABLE} | ${ERROR} | ${{ installFailed: true }}
- ${UPDATING} | ${UPDATING} | ${NO_EFFECTS}
- ${INSTALLED} | ${UPDATED} | ${NO_EFFECTS}
- ${INSTALLED} | ${UPDATE_ERRORED} | ${{ updateFailed: true }}
- ${UNINSTALLING} | ${UNINSTALLING} | ${NO_EFFECTS}
- ${INSTALLED} | ${UNINSTALL_ERRORED} | ${{ uninstallFailed: true }}
- ${UNINSTALLED} | ${UNINSTALLED} | ${NO_EFFECTS}
- ${PRE_INSTALLED} | ${PRE_INSTALLED} | ${NO_EFFECTS}
- ${EXTERNALLY_INSTALLED} | ${EXTERNALLY_INSTALLED} | ${NO_EFFECTS}
- `(`transitions to $expectedState on $event event and applies $effects`, (data) => {
- const { expectedState, event, effects } = data;
- const currentAppState = {
- status: NO_STATUS,
- };
-
- expect(transitionApplicationState(currentAppState, event)).toEqual({
- status: expectedState,
- ...noEffectsToEmptyObject(effects),
- });
- });
- });
-
- describe(`current state is ${NOT_INSTALLABLE}`, () => {
- it.each`
- expectedState | event | effects
- ${INSTALLABLE} | ${INSTALLABLE} | ${NO_EFFECTS}
- `(`transitions to $expectedState on $event event and applies $effects`, (data) => {
- const { expectedState, event, effects } = data;
- const currentAppState = {
- status: NOT_INSTALLABLE,
- };
-
- expect(transitionApplicationState(currentAppState, event)).toEqual({
- status: expectedState,
- ...noEffectsToEmptyObject(effects),
- });
- });
- });
-
- describe(`current state is ${INSTALLABLE}`, () => {
- it.each`
- expectedState | event | effects
- ${INSTALLING} | ${INSTALL_EVENT} | ${{ installFailed: false }}
- ${INSTALLED} | ${INSTALLED} | ${{ installFailed: false }}
- ${NOT_INSTALLABLE} | ${NOT_INSTALLABLE} | ${NO_EFFECTS}
- ${UNINSTALLED} | ${UNINSTALLED} | ${{ installFailed: false }}
- `(`transitions to $expectedState on $event event and applies $effects`, (data) => {
- const { expectedState, event, effects } = data;
- const currentAppState = {
- status: INSTALLABLE,
- };
-
- expect(transitionApplicationState(currentAppState, event)).toEqual({
- status: expectedState,
- ...noEffectsToEmptyObject(effects),
- });
- });
- });
-
- describe(`current state is ${INSTALLING}`, () => {
- it.each`
- expectedState | event | effects
- ${INSTALLED} | ${INSTALLED} | ${NO_EFFECTS}
- ${INSTALLABLE} | ${ERROR} | ${{ installFailed: true }}
- `(`transitions to $expectedState on $event event and applies $effects`, (data) => {
- const { expectedState, event, effects } = data;
- const currentAppState = {
- status: INSTALLING,
- };
-
- expect(transitionApplicationState(currentAppState, event)).toEqual({
- status: expectedState,
- ...noEffectsToEmptyObject(effects),
- });
- });
- });
-
- describe(`current state is ${INSTALLED}`, () => {
- it.each`
- expectedState | event | effects
- ${UPDATING} | ${UPDATE_EVENT} | ${{ updateFailed: false, updateSuccessful: false }}
- ${UNINSTALLING} | ${UNINSTALL_EVENT} | ${{ uninstallFailed: false, uninstallSuccessful: false }}
- ${NOT_INSTALLABLE} | ${NOT_INSTALLABLE} | ${NO_EFFECTS}
- ${UNINSTALLED} | ${UNINSTALLED} | ${NO_EFFECTS}
- ${INSTALLABLE} | ${ERROR} | ${{ installFailed: true }}
- `(`transitions to $expectedState on $event event and applies $effects`, (data) => {
- const { expectedState, event, effects } = data;
- const currentAppState = {
- status: INSTALLED,
- };
-
- expect(transitionApplicationState(currentAppState, event)).toEqual({
- status: expectedState,
- ...noEffectsToEmptyObject(effects),
- });
- });
- });
-
- describe(`current state is ${UPDATING}`, () => {
- it.each`
- expectedState | event | effects
- ${INSTALLED} | ${UPDATED} | ${{ updateSuccessful: true }}
- ${INSTALLED} | ${UPDATE_ERRORED} | ${{ updateFailed: true }}
- `(`transitions to $expectedState on $event event and applies $effects`, (data) => {
- const { expectedState, event, effects } = data;
- const currentAppState = {
- status: UPDATING,
- };
-
- expect(transitionApplicationState(currentAppState, event)).toEqual({
- status: expectedState,
- ...effects,
- });
- });
- });
-
- describe(`current state is ${UNINSTALLING}`, () => {
- it.each`
- expectedState | event | effects
- ${INSTALLABLE} | ${INSTALLABLE} | ${{ uninstallSuccessful: true }}
- ${INSTALLED} | ${UNINSTALL_ERRORED} | ${{ uninstallFailed: true }}
- `(`transitions to $expectedState on $event event and applies $effects`, (data) => {
- const { expectedState, event, effects } = data;
- const currentAppState = {
- status: UNINSTALLING,
- };
-
- expect(transitionApplicationState(currentAppState, event)).toEqual({
- status: expectedState,
- ...effects,
- });
- });
- });
-
- describe(`current state is ${UNINSTALLED}`, () => {
- it.each`
- expectedState | event | effects
- ${INSTALLED} | ${INSTALLED} | ${NO_EFFECTS}
- ${INSTALLABLE} | ${ERROR} | ${{ installFailed: true }}
- `(`transitions to $expectedState on $event event and applies $effects`, (data) => {
- const { expectedState, event, effects } = data;
- const currentAppState = {
- status: UNINSTALLED,
- };
-
- expect(transitionApplicationState(currentAppState, event)).toEqual({
- status: expectedState,
- ...noEffectsToEmptyObject(effects),
- });
- });
- });
- describe('current state is undefined', () => {
- it('returns the current state without having any effects', () => {
- const currentAppState = {};
- expect(transitionApplicationState(currentAppState, INSTALLABLE)).toEqual(currentAppState);
- });
- });
-
- describe('with event is undefined', () => {
- it('returns the current state without having any effects', () => {
- const currentAppState = {
- status: NO_STATUS,
- };
- expect(transitionApplicationState(currentAppState, undefined)).toEqual(currentAppState);
- });
- });
-});
diff --git a/spec/frontend/clusters/services/crossplane_provider_stack_spec.js b/spec/frontend/clusters/services/crossplane_provider_stack_spec.js
deleted file mode 100644
index f95b175ca64..00000000000
--- a/spec/frontend/clusters/services/crossplane_provider_stack_spec.js
+++ /dev/null
@@ -1,85 +0,0 @@
-import { GlDropdownItem, GlIcon } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import CrossplaneProviderStack from '~/clusters/components/crossplane_provider_stack.vue';
-
-describe('CrossplaneProviderStack component', () => {
- let wrapper;
-
- const defaultProps = {
- stacks: [
- {
- name: 'Google Cloud Platform',
- code: 'gcp',
- },
- {
- name: 'Amazon Web Services',
- code: 'aws',
- },
- ],
- };
-
- function createComponent(props = {}) {
- const propsData = {
- ...defaultProps,
- ...props,
- };
-
- wrapper = shallowMount(CrossplaneProviderStack, {
- propsData,
- });
- }
-
- beforeEach(() => {
- const crossplane = {
- title: 'crossplane',
- stack: '',
- };
- createComponent({ crossplane });
- });
-
- const findDropdownElements = () => wrapper.findAll(GlDropdownItem);
- const findFirstDropdownElement = () => findDropdownElements().at(0);
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders all of the available stacks in the dropdown', () => {
- const dropdownElements = findDropdownElements();
-
- expect(dropdownElements.length).toBe(defaultProps.stacks.length);
-
- defaultProps.stacks.forEach((stack, index) =>
- expect(dropdownElements.at(index).text()).toEqual(stack.name),
- );
- });
-
- it('displays the correct label for the first dropdown item if a stack is selected', () => {
- const crossplane = {
- title: 'crossplane',
- stack: 'gcp',
- };
- createComponent({ crossplane });
- expect(wrapper.vm.dropdownText).toBe('Google Cloud Platform');
- });
-
- it('emits the "set" event with the selected stack value', () => {
- const crossplane = {
- title: 'crossplane',
- stack: 'gcp',
- };
- createComponent({ crossplane });
- findFirstDropdownElement().vm.$emit('click');
- return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.emitted().set[0][0].code).toEqual('gcp');
- });
- });
-
- it('renders the correct dropdown text when no stack is selected', () => {
- expect(wrapper.vm.dropdownText).toBe('Select Stack');
- });
-
- it('renders an external link', () => {
- expect(wrapper.find(GlIcon).props('name')).toBe('external-link');
- });
-});
diff --git a/spec/frontend/clusters/services/mock_data.js b/spec/frontend/clusters/services/mock_data.js
index a75fcb0cb06..cf63d5452ac 100644
--- a/spec/frontend/clusters/services/mock_data.js
+++ b/spec/frontend/clusters/services/mock_data.js
@@ -1,170 +1,19 @@
-import { APPLICATION_STATUS } from '~/clusters/constants';
-
const CLUSTERS_MOCK_DATA = {
GET: {
'/gitlab-org/gitlab-shell/clusters/1/status.json': {
data: {
status: 'errored',
status_reason: 'Failed to request to CloudPlatform.',
- applications: [
- {
- name: 'helm',
- status: APPLICATION_STATUS.INSTALLABLE,
- status_reason: null,
- can_uninstall: false,
- },
- {
- name: 'ingress',
- status: APPLICATION_STATUS.ERROR,
- status_reason: 'Cannot connect',
- external_ip: null,
- external_hostname: null,
- can_uninstall: false,
- },
- {
- name: 'runner',
- status: APPLICATION_STATUS.INSTALLING,
- status_reason: null,
- can_uninstall: false,
- },
- {
- name: 'prometheus',
- status: APPLICATION_STATUS.ERROR,
- status_reason: 'Cannot connect',
- can_uninstall: false,
- },
- {
- name: 'jupyter',
- status: APPLICATION_STATUS.INSTALLING,
- status_reason: 'Cannot connect',
- can_uninstall: false,
- },
- {
- name: 'knative',
- status: APPLICATION_STATUS.INSTALLING,
- status_reason: 'Cannot connect',
- can_uninstall: false,
- },
- {
- name: 'cert_manager',
- status: APPLICATION_STATUS.ERROR,
- status_reason: 'Cannot connect',
- email: 'test@example.com',
- can_uninstall: false,
- },
- {
- name: 'crossplane',
- status: APPLICATION_STATUS.ERROR,
- status_reason: 'Cannot connect',
- can_uninstall: false,
- },
- {
- name: 'elastic_stack',
- status: APPLICATION_STATUS.ERROR,
- status_reason: 'Cannot connect',
- can_uninstall: false,
- },
- ],
},
},
'/gitlab-org/gitlab-shell/clusters/2/status.json': {
data: {
status: 'errored',
status_reason: 'Failed to request to CloudPlatform.',
- applications: [
- {
- name: 'helm',
- status: APPLICATION_STATUS.INSTALLED,
- status_reason: null,
- },
- {
- name: 'ingress',
- status: APPLICATION_STATUS.INSTALLED,
- status_reason: 'Cannot connect',
- external_ip: '1.1.1.1',
- external_hostname: null,
- },
- {
- name: 'runner',
- status: APPLICATION_STATUS.INSTALLING,
- status_reason: null,
- },
- {
- name: 'prometheus',
- status: APPLICATION_STATUS.ERROR,
- status_reason: 'Cannot connect',
- },
- {
- name: 'jupyter',
- status: APPLICATION_STATUS.INSTALLABLE,
- status_reason: 'Cannot connect',
- },
- {
- name: 'knative',
- status: APPLICATION_STATUS.INSTALLABLE,
- status_reason: 'Cannot connect',
- },
- {
- name: 'cert_manager',
- status: APPLICATION_STATUS.ERROR,
- status_reason: 'Cannot connect',
- email: 'test@example.com',
- },
- {
- name: 'crossplane',
- status: APPLICATION_STATUS.ERROR,
- status_reason: 'Cannot connect',
- stack: 'gcp',
- },
- {
- name: 'elastic_stack',
- status: APPLICATION_STATUS.ERROR,
- status_reason: 'Cannot connect',
- },
- ],
},
},
},
- POST: {
- '/gitlab-org/gitlab-shell/clusters/1/applications/helm': {},
- '/gitlab-org/gitlab-shell/clusters/1/applications/ingress': {},
- '/gitlab-org/gitlab-shell/clusters/1/applications/crossplane': {},
- '/gitlab-org/gitlab-shell/clusters/1/applications/cert_manager': {},
- '/gitlab-org/gitlab-shell/clusters/1/applications/runner': {},
- '/gitlab-org/gitlab-shell/clusters/1/applications/prometheus': {},
- '/gitlab-org/gitlab-shell/clusters/1/applications/jupyter': {},
- '/gitlab-org/gitlab-shell/clusters/1/applications/knative': {},
- '/gitlab-org/gitlab-shell/clusters/1/applications/elastic_stack': {},
- },
-};
-
-const DEFAULT_APPLICATION_STATE = {
- id: 'some-app',
- title: 'My App',
- titleLink: 'https://about.gitlab.com/',
- description: 'Some description about this interesting application!',
- status: null,
- statusReason: null,
- requestReason: null,
-};
-
-const APPLICATIONS_MOCK_STATE = {
- helm: { title: 'Helm Tiller', status: 'installable' },
- ingress: {
- title: 'Ingress',
- status: 'installable',
- },
- crossplane: { title: 'Crossplane', status: 'installable', stack: '' },
- cert_manager: { title: 'Cert-Manager', status: 'installable' },
- runner: { title: 'GitLab Runner' },
- prometheus: { title: 'Prometheus' },
- jupyter: { title: 'JupyterHub', status: 'installable', hostname: '' },
- knative: { title: 'Knative ', status: 'installable', hostname: '' },
- elastic_stack: { title: 'Elastic Stack', status: 'installable' },
- cilium: {
- title: 'GitLab Container Network Policies',
- status: 'not_installable',
- },
+ POST: {},
};
-export { CLUSTERS_MOCK_DATA, DEFAULT_APPLICATION_STATE, APPLICATIONS_MOCK_STATE };
+export { CLUSTERS_MOCK_DATA };
diff --git a/spec/frontend/clusters/stores/clusters_store_spec.js b/spec/frontend/clusters/stores/clusters_store_spec.js
index cdba6fc6ab8..5e797bbf8a8 100644
--- a/spec/frontend/clusters/stores/clusters_store_spec.js
+++ b/spec/frontend/clusters/stores/clusters_store_spec.js
@@ -1,4 +1,3 @@
-import { APPLICATION_INSTALLED_STATUSES, APPLICATION_STATUS, RUNNER } from '~/clusters/constants';
import ClustersStore from '~/clusters/stores/clusters_store';
import { CLUSTERS_MOCK_DATA } from '../services/mock_data';
@@ -31,17 +30,6 @@ describe('Clusters Store', () => {
});
});
- describe('updateAppProperty', () => {
- it('should store new request reason', () => {
- expect(store.state.applications.helm.requestReason).toEqual(null);
-
- const newReason = 'We broke it.';
- store.updateAppProperty('helm', 'requestReason', newReason);
-
- expect(store.state.applications.helm.requestReason).toEqual(newReason);
- });
- });
-
describe('updateStateFromServer', () => {
it('should store new polling data from server', () => {
const mockResponseData =
@@ -50,196 +38,16 @@ describe('Clusters Store', () => {
expect(store.state).toEqual({
helpPath: null,
- helmHelpPath: null,
- ingressHelpPath: null,
environmentsHelpPath: null,
clustersHelpPath: null,
deployBoardsHelpPath: null,
- cloudRunHelpPath: null,
status: mockResponseData.status,
statusReason: mockResponseData.status_reason,
providerType: null,
- preInstalledKnative: false,
rbac: false,
- applications: {
- helm: {
- title: 'Legacy Helm Tiller server',
- status: mockResponseData.applications[0].status,
- statusReason: mockResponseData.applications[0].status_reason,
- requestReason: null,
- installable: true,
- installed: false,
- installFailed: false,
- uninstallable: false,
- uninstallSuccessful: false,
- uninstallFailed: false,
- validationError: null,
- },
- ingress: {
- title: 'Ingress',
- status: APPLICATION_STATUS.INSTALLABLE,
- statusReason: mockResponseData.applications[1].status_reason,
- requestReason: null,
- externalIp: null,
- externalHostname: null,
- installable: true,
- installed: false,
- installFailed: true,
- uninstallable: false,
- updateFailed: false,
- uninstallSuccessful: false,
- uninstallFailed: false,
- validationError: null,
- },
- runner: {
- title: 'GitLab Runner',
- status: mockResponseData.applications[2].status,
- statusReason: mockResponseData.applications[2].status_reason,
- requestReason: null,
- version: mockResponseData.applications[2].version,
- updateAvailable: mockResponseData.applications[2].update_available,
- chartRepo: 'https://gitlab.com/gitlab-org/charts/gitlab-runner',
- installable: true,
- installed: false,
- installFailed: false,
- updateFailed: false,
- updateSuccessful: false,
- uninstallable: false,
- uninstallSuccessful: false,
- uninstallFailed: false,
- validationError: null,
- },
- prometheus: {
- title: 'Prometheus',
- status: APPLICATION_STATUS.INSTALLABLE,
- statusReason: mockResponseData.applications[3].status_reason,
- requestReason: null,
- installable: true,
- installed: false,
- installFailed: true,
- uninstallable: false,
- uninstallSuccessful: false,
- uninstallFailed: false,
- validationError: null,
- },
- jupyter: {
- title: 'JupyterHub',
- status: mockResponseData.applications[4].status,
- statusReason: mockResponseData.applications[4].status_reason,
- requestReason: null,
- hostname: '',
- installable: true,
- installed: false,
- installFailed: false,
- uninstallable: false,
- uninstallSuccessful: false,
- uninstallFailed: false,
- validationError: null,
- },
- knative: {
- title: 'Knative',
- status: mockResponseData.applications[5].status,
- statusReason: mockResponseData.applications[5].status_reason,
- requestReason: null,
- hostname: null,
- isEditingDomain: false,
- externalIp: null,
- externalHostname: null,
- installable: true,
- installed: false,
- installFailed: false,
- uninstallable: false,
- uninstallSuccessful: false,
- uninstallFailed: false,
- updateSuccessful: false,
- updateFailed: false,
- validationError: null,
- },
- cert_manager: {
- title: 'Cert-Manager',
- status: APPLICATION_STATUS.INSTALLABLE,
- installFailed: true,
- statusReason: mockResponseData.applications[6].status_reason,
- requestReason: null,
- email: mockResponseData.applications[6].email,
- installable: true,
- installed: false,
- uninstallable: false,
- uninstallSuccessful: false,
- uninstallFailed: false,
- validationError: null,
- },
- elastic_stack: {
- title: 'Elastic Stack',
- status: APPLICATION_STATUS.INSTALLABLE,
- installFailed: true,
- statusReason: mockResponseData.applications[7].status_reason,
- requestReason: null,
- installable: true,
- installed: false,
- uninstallable: false,
- uninstallSuccessful: false,
- uninstallFailed: false,
- validationError: null,
- },
- crossplane: {
- title: 'Crossplane',
- status: APPLICATION_STATUS.INSTALLABLE,
- installFailed: true,
- statusReason: mockResponseData.applications[8].status_reason,
- requestReason: null,
- installable: true,
- installed: false,
- uninstallable: false,
- uninstallSuccessful: false,
- uninstallFailed: false,
- validationError: null,
- },
- cilium: {
- title: 'GitLab Container Network Policies',
- status: null,
- statusReason: null,
- requestReason: null,
- installable: false,
- installed: false,
- installFailed: false,
- uninstallable: false,
- uninstallSuccessful: false,
- uninstallFailed: false,
- validationError: null,
- },
- },
environments: [],
fetchingEnvironments: false,
});
});
-
- describe.each(APPLICATION_INSTALLED_STATUSES)(
- 'given the current app status is %s',
- (status) => {
- it('marks application as installed', () => {
- const mockResponseData =
- CLUSTERS_MOCK_DATA.GET['/gitlab-org/gitlab-shell/clusters/2/status.json'].data;
- const runnerAppIndex = 2;
-
- mockResponseData.applications[runnerAppIndex].status = status;
-
- store.updateStateFromServer(mockResponseData);
-
- expect(store.state.applications[RUNNER].installed).toBe(true);
- });
- },
- );
-
- it('sets default hostname for jupyter when ingress has a ip address', () => {
- const mockResponseData =
- CLUSTERS_MOCK_DATA.GET['/gitlab-org/gitlab-shell/clusters/2/status.json'].data;
-
- store.updateStateFromServer(mockResponseData);
-
- expect(store.state.applications.jupyter.hostname).toEqual(
- `jupyter.${store.state.applications.ingress.externalIp}.nip.io`,
- );
- });
});
});
diff --git a/spec/frontend/diffs/components/diff_content_spec.js b/spec/frontend/diffs/components/diff_content_spec.js
index 7012889440c..0a7dfc02c65 100644
--- a/spec/frontend/diffs/components/diff_content_spec.js
+++ b/spec/frontend/diffs/components/diff_content_spec.js
@@ -4,8 +4,6 @@ import Vuex from 'vuex';
import DiffContentComponent from '~/diffs/components/diff_content.vue';
import DiffDiscussions from '~/diffs/components/diff_discussions.vue';
import DiffView from '~/diffs/components/diff_view.vue';
-import InlineDiffView from '~/diffs/components/inline_diff_view.vue';
-import ParallelDiffView from '~/diffs/components/parallel_diff_view.vue';
import { IMAGE_DIFF_POSITION_TYPE } from '~/diffs/constants';
import { diffViewerModes } from '~/ide/constants';
import NoteForm from '~/notes/components/note_form.vue';
@@ -107,25 +105,10 @@ describe('DiffContent', () => {
});
const textDiffFile = { ...defaultProps.diffFile, viewer: { name: diffViewerModes.text } };
- it('should render diff inline view if `isInlineView` is true', () => {
- isInlineViewGetterMock.mockReturnValue(true);
- createComponent({ props: { diffFile: textDiffFile } });
-
- expect(wrapper.find(InlineDiffView).exists()).toBe(true);
- });
-
- it('should render parallel view if `isParallelView` getter is true', () => {
- isParallelViewGetterMock.mockReturnValue(true);
- createComponent({ props: { diffFile: textDiffFile } });
-
- expect(wrapper.find(ParallelDiffView).exists()).toBe(true);
- });
it('should render diff view if `unifiedDiffComponents` are true', () => {
- isParallelViewGetterMock.mockReturnValue(true);
createComponent({
props: { diffFile: textDiffFile },
- provide: { glFeatures: { unifiedDiffComponents: true } },
});
expect(wrapper.find(DiffView).exists()).toBe(true);
diff --git a/spec/frontend/diffs/components/diff_row_utils_spec.js b/spec/frontend/diffs/components/diff_row_utils_spec.js
index 47ae3cd5867..7cc94464585 100644
--- a/spec/frontend/diffs/components/diff_row_utils_spec.js
+++ b/spec/frontend/diffs/components/diff_row_utils_spec.js
@@ -258,30 +258,3 @@ describe('mapParallel', () => {
expect(mapped.right).toMatchObject(rightExpectation);
});
});
-
-describe('mapInline', () => {
- it('should assign computed properties to the line object', () => {
- const content = {
- diffFile: {},
- shouldRenderDraftRow: () => false,
- };
- const line = {
- discussions: [{}],
- discussionsExpanded: true,
- hasForm: true,
- };
- const expectation = {
- commentRowClasses: '',
- hasDiscussions: true,
- isContextLine: false,
- isMatchLine: false,
- isMetaLine: false,
- renderDiscussion: true,
- hasDraft: false,
- hasCommentForm: true,
- };
- const mapped = utils.mapInline(content)(line);
-
- expect(mapped).toMatchObject(expectation);
- });
-});
diff --git a/spec/frontend/diffs/components/inline_diff_table_row_spec.js b/spec/frontend/diffs/components/inline_diff_table_row_spec.js
deleted file mode 100644
index 9c3e00cd6cf..00000000000
--- a/spec/frontend/diffs/components/inline_diff_table_row_spec.js
+++ /dev/null
@@ -1,325 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import DiffGutterAvatars from '~/diffs/components/diff_gutter_avatars.vue';
-import { mapInline } from '~/diffs/components/diff_row_utils';
-import InlineDiffTableRow from '~/diffs/components/inline_diff_table_row.vue';
-import { createStore } from '~/mr_notes/stores';
-import { findInteropAttributes } from '../find_interop_attributes';
-import discussionsMockData from '../mock_data/diff_discussions';
-import diffFileMockData from '../mock_data/diff_file';
-
-const TEST_USER_ID = 'abc123';
-const TEST_USER = { id: TEST_USER_ID };
-
-describe('InlineDiffTableRow', () => {
- let wrapper;
- let store;
- const mockDiffContent = {
- diffFile: diffFileMockData,
- shouldRenderDraftRow: jest.fn(),
- hasParallelDraftLeft: jest.fn(),
- hasParallelDraftRight: jest.fn(),
- draftForLine: jest.fn(),
- };
-
- const applyMap = mapInline(mockDiffContent);
- const thisLine = applyMap(diffFileMockData.highlighted_diff_lines[0]);
-
- const createComponent = (props = {}, propsStore = store) => {
- wrapper = shallowMount(InlineDiffTableRow, {
- store: propsStore,
- propsData: {
- line: thisLine,
- fileHash: diffFileMockData.file_hash,
- filePath: diffFileMockData.file_path,
- contextLinesPath: 'contextLinesPath',
- isHighlighted: false,
- ...props,
- },
- });
- };
-
- beforeEach(() => {
- store = createStore();
- store.state.notes.userData = TEST_USER;
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('does not add hll class to line content when line does not match highlighted row', () => {
- createComponent();
- expect(wrapper.find('.line_content').classes('hll')).toBe(false);
- });
-
- it('adds hll class to lineContent when line is the highlighted row', () => {
- store.state.diffs.highlightedRow = thisLine.line_code;
- createComponent({}, store);
- expect(wrapper.find('.line_content').classes('hll')).toBe(true);
- });
-
- it('adds hll class to lineContent when line is part of a multiline comment', () => {
- createComponent({ isCommented: true });
- expect(wrapper.find('.line_content').classes('hll')).toBe(true);
- });
-
- describe('sets coverage title and class', () => {
- it('for lines with coverage', () => {
- const name = diffFileMockData.file_path;
- const line = thisLine.new_line;
-
- store.state.diffs.coverageFiles = { files: { [name]: { [line]: 5 } } };
- createComponent({}, store);
- const coverage = wrapper.find('.line-coverage');
-
- expect(coverage.attributes('title')).toContain('Test coverage: 5 hits');
- expect(coverage.classes('coverage')).toBe(true);
- });
-
- it('for lines without coverage', () => {
- const name = diffFileMockData.file_path;
- const line = thisLine.new_line;
-
- store.state.diffs.coverageFiles = { files: { [name]: { [line]: 0 } } };
- createComponent({}, store);
- const coverage = wrapper.find('.line-coverage');
-
- expect(coverage.attributes('title')).toContain('No test coverage');
- expect(coverage.classes('no-coverage')).toBe(true);
- });
-
- it('for unknown lines', () => {
- store.state.diffs.coverageFiles = {};
- createComponent({}, store);
-
- const coverage = wrapper.find('.line-coverage');
-
- expect(coverage.attributes('title')).toBeUndefined();
- expect(coverage.classes('coverage')).toBe(false);
- expect(coverage.classes('no-coverage')).toBe(false);
- });
- });
-
- describe('Table Cells', () => {
- const findNewTd = () => wrapper.find({ ref: 'newTd' });
- const findOldTd = () => wrapper.find({ ref: 'oldTd' });
-
- describe('td', () => {
- it('highlights when isHighlighted true', () => {
- store.state.diffs.highlightedRow = thisLine.line_code;
- createComponent({}, store);
-
- expect(findNewTd().classes()).toContain('hll');
- expect(findOldTd().classes()).toContain('hll');
- });
-
- it('does not highlight when isHighlighted false', () => {
- createComponent();
-
- expect(findNewTd().classes()).not.toContain('hll');
- expect(findOldTd().classes()).not.toContain('hll');
- });
- });
-
- describe('comment button', () => {
- const findNoteButton = () => wrapper.find({ ref: 'addDiffNoteButton' });
-
- it.each`
- userData | expectation
- ${TEST_USER} | ${true}
- ${null} | ${false}
- `('exists is $expectation - with userData ($userData)', ({ userData, expectation }) => {
- store.state.notes.userData = userData;
- createComponent({}, store);
-
- expect(findNoteButton().exists()).toBe(expectation);
- });
-
- it.each`
- isHover | line | expectation
- ${true} | ${{ ...thisLine, discussions: [] }} | ${true}
- ${false} | ${{ ...thisLine, discussions: [] }} | ${false}
- ${true} | ${{ ...thisLine, type: 'context', discussions: [] }} | ${false}
- ${true} | ${{ ...thisLine, type: 'old-nonewline', discussions: [] }} | ${false}
- ${true} | ${{ ...thisLine, discussions: [{}] }} | ${false}
- `('visible is $expectation - line ($line)', ({ isHover, line, expectation }) => {
- createComponent({ line: applyMap(line) });
- wrapper.setData({ isHover });
-
- return wrapper.vm.$nextTick().then(() => {
- expect(findNoteButton().isVisible()).toBe(expectation);
- });
- });
-
- it.each`
- disabled | commentsDisabled
- ${'disabled'} | ${true}
- ${undefined} | ${false}
- `(
- 'has attribute disabled=$disabled when the outer component has prop commentsDisabled=$commentsDisabled',
- ({ disabled, commentsDisabled }) => {
- createComponent({
- line: applyMap({ ...thisLine, commentsDisabled }),
- });
-
- wrapper.setData({ isHover: true });
-
- return wrapper.vm.$nextTick().then(() => {
- expect(findNoteButton().attributes('disabled')).toBe(disabled);
- });
- },
- );
-
- const symlinkishFileTooltip =
- 'Commenting on symbolic links that replace or are replaced by files is currently not supported.';
- const realishFileTooltip =
- 'Commenting on files that replace or are replaced by symbolic links is currently not supported.';
- const otherFileTooltip = 'Add a comment to this line';
- const findTooltip = () => wrapper.find({ ref: 'addNoteTooltip' });
-
- it.each`
- tooltip | commentsDisabled
- ${symlinkishFileTooltip} | ${{ wasSymbolic: true }}
- ${symlinkishFileTooltip} | ${{ isSymbolic: true }}
- ${realishFileTooltip} | ${{ wasReal: true }}
- ${realishFileTooltip} | ${{ isReal: true }}
- ${otherFileTooltip} | ${false}
- `(
- 'has the correct tooltip when commentsDisabled=$commentsDisabled',
- ({ tooltip, commentsDisabled }) => {
- createComponent({
- line: applyMap({ ...thisLine, commentsDisabled }),
- });
-
- wrapper.setData({ isHover: true });
-
- return wrapper.vm.$nextTick().then(() => {
- expect(findTooltip().attributes('title')).toBe(tooltip);
- });
- },
- );
- });
-
- describe('line number', () => {
- const findLineNumberOld = () => wrapper.find({ ref: 'lineNumberRefOld' });
- const findLineNumberNew = () => wrapper.find({ ref: 'lineNumberRefNew' });
-
- it('renders line numbers in correct cells', () => {
- createComponent();
-
- expect(findLineNumberOld().exists()).toBe(false);
- expect(findLineNumberNew().exists()).toBe(true);
- });
-
- describe('with lineNumber prop', () => {
- const TEST_LINE_CODE = 'LC_42';
- const TEST_LINE_NUMBER = 1;
-
- describe.each`
- lineProps | findLineNumber | expectedHref | expectedClickArg
- ${{ line_code: TEST_LINE_CODE, old_line: TEST_LINE_NUMBER }} | ${findLineNumberOld} | ${`#${TEST_LINE_CODE}`} | ${TEST_LINE_CODE}
- ${{ line_code: undefined, old_line: TEST_LINE_NUMBER }} | ${findLineNumberOld} | ${'#'} | ${undefined}
- ${{ line_code: undefined, left: { line_code: TEST_LINE_CODE }, old_line: TEST_LINE_NUMBER }} | ${findLineNumberOld} | ${'#'} | ${TEST_LINE_CODE}
- ${{ line_code: undefined, right: { line_code: TEST_LINE_CODE }, new_line: TEST_LINE_NUMBER }} | ${findLineNumberNew} | ${'#'} | ${TEST_LINE_CODE}
- `(
- 'with line ($lineProps)',
- ({ lineProps, findLineNumber, expectedHref, expectedClickArg }) => {
- beforeEach(() => {
- jest.spyOn(store, 'dispatch').mockImplementation();
- createComponent({
- line: applyMap({ ...thisLine, ...lineProps }),
- });
- });
-
- it('renders', () => {
- expect(findLineNumber().exists()).toBe(true);
- expect(findLineNumber().attributes()).toEqual({
- href: expectedHref,
- 'data-linenumber': TEST_LINE_NUMBER.toString(),
- });
- });
-
- it('on click, dispatches setHighlightedRow', () => {
- expect(store.dispatch).toHaveBeenCalledTimes(1);
-
- findLineNumber().trigger('click');
-
- expect(store.dispatch).toHaveBeenCalledWith(
- 'diffs/setHighlightedRow',
- expectedClickArg,
- );
- expect(store.dispatch).toHaveBeenCalledTimes(2);
- });
- },
- );
- });
- });
-
- describe('diff-gutter-avatars', () => {
- const TEST_LINE_CODE = 'LC_42';
- const TEST_FILE_HASH = diffFileMockData.file_hash;
- const findAvatars = () => wrapper.find(DiffGutterAvatars);
- let line;
-
- beforeEach(() => {
- jest.spyOn(store, 'dispatch').mockImplementation();
-
- line = {
- line_code: TEST_LINE_CODE,
- type: 'new',
- old_line: null,
- new_line: 1,
- discussions: [{ ...discussionsMockData }],
- discussionsExpanded: true,
- text: '+<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n',
- rich_text: '+<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n',
- meta_data: null,
- };
- });
-
- describe('with showCommentButton', () => {
- it('renders if line has discussions', () => {
- createComponent({ line: applyMap(line) });
-
- expect(findAvatars().props()).toEqual({
- discussions: line.discussions,
- discussionsExpanded: line.discussionsExpanded,
- });
- });
-
- it('does notrender if line has no discussions', () => {
- line.discussions = [];
- createComponent({ line: applyMap(line) });
-
- expect(findAvatars().exists()).toEqual(false);
- });
-
- it('toggles line discussion', () => {
- createComponent({ line: applyMap(line) });
-
- expect(store.dispatch).toHaveBeenCalledTimes(1);
-
- findAvatars().vm.$emit('toggleLineDiscussions');
-
- expect(store.dispatch).toHaveBeenCalledWith('diffs/toggleLineDiscussions', {
- lineCode: TEST_LINE_CODE,
- fileHash: TEST_FILE_HASH,
- expanded: !line.discussionsExpanded,
- });
- });
- });
- });
- });
-
- describe('interoperability', () => {
- it.each`
- desc | line | expectation
- ${'with type old'} | ${{ ...thisLine, type: 'old', old_line: 3, new_line: 5 }} | ${{ type: 'old', line: '3', oldLine: '3', newLine: '5' }}
- ${'with type new'} | ${{ ...thisLine, type: 'new', old_line: 3, new_line: 5 }} | ${{ type: 'new', line: '5', oldLine: '3', newLine: '5' }}
- `('$desc, sets interop data attributes', ({ line, expectation }) => {
- createComponent({ line });
-
- expect(findInteropAttributes(wrapper)).toEqual(expectation);
- });
- });
-});
diff --git a/spec/frontend/diffs/components/inline_diff_view_spec.js b/spec/frontend/diffs/components/inline_diff_view_spec.js
deleted file mode 100644
index 27834804f77..00000000000
--- a/spec/frontend/diffs/components/inline_diff_view_spec.js
+++ /dev/null
@@ -1,57 +0,0 @@
-import '~/behaviors/markdown/render_gfm';
-import { getByText } from '@testing-library/dom';
-import { mount } from '@vue/test-utils';
-import { mapInline } from '~/diffs/components/diff_row_utils';
-import InlineDiffView from '~/diffs/components/inline_diff_view.vue';
-import { createStore } from '~/mr_notes/stores';
-import discussionsMockData from '../mock_data/diff_discussions';
-import diffFileMockData from '../mock_data/diff_file';
-
-describe('InlineDiffView', () => {
- let wrapper;
- const getDiffFileMock = () => ({ ...diffFileMockData });
- const getDiscussionsMockData = () => [{ ...discussionsMockData }];
- const notesLength = getDiscussionsMockData()[0].notes.length;
-
- const setup = (diffFile, diffLines) => {
- const mockDiffContent = {
- diffFile,
- shouldRenderDraftRow: jest.fn(),
- };
-
- const store = createStore();
-
- store.dispatch('diffs/setInlineDiffViewType');
- wrapper = mount(InlineDiffView, {
- store,
- propsData: {
- diffFile,
- diffLines: diffLines.map(mapInline(mockDiffContent)),
- },
- });
- };
-
- describe('template', () => {
- it('should have rendered diff lines', () => {
- const diffFile = getDiffFileMock();
- setup(diffFile, diffFile.highlighted_diff_lines);
-
- expect(wrapper.findAll('tr.line_holder').length).toEqual(8);
- expect(wrapper.findAll('tr.line_holder.new').length).toEqual(4);
- expect(wrapper.findAll('tr.line_expansion.match').length).toEqual(1);
- getByText(wrapper.element, /Bad dates/i);
- });
-
- it('should render discussions', () => {
- const diffFile = getDiffFileMock();
- diffFile.highlighted_diff_lines[1].discussions = getDiscussionsMockData();
- diffFile.highlighted_diff_lines[1].discussionsExpanded = true;
- setup(diffFile, diffFile.highlighted_diff_lines);
-
- expect(wrapper.findAll('.notes_holder').length).toEqual(1);
- expect(wrapper.findAll('.notes_holder .note').length).toEqual(notesLength + 1);
- getByText(wrapper.element, 'comment 5');
- wrapper.vm.$store.dispatch('setInitialNotes', []);
- });
- });
-});
diff --git a/spec/frontend/diffs/components/parallel_diff_table_row_spec.js b/spec/frontend/diffs/components/parallel_diff_table_row_spec.js
deleted file mode 100644
index ed191d849fd..00000000000
--- a/spec/frontend/diffs/components/parallel_diff_table_row_spec.js
+++ /dev/null
@@ -1,445 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import Vue from 'vue';
-import { createComponentWithStore } from 'helpers/vue_mount_component_helper';
-import DiffGutterAvatars from '~/diffs/components/diff_gutter_avatars.vue';
-import { mapParallel } from '~/diffs/components/diff_row_utils';
-import ParallelDiffTableRow from '~/diffs/components/parallel_diff_table_row.vue';
-import { createStore } from '~/mr_notes/stores';
-import { findInteropAttributes } from '../find_interop_attributes';
-import discussionsMockData from '../mock_data/diff_discussions';
-import diffFileMockData from '../mock_data/diff_file';
-
-describe('ParallelDiffTableRow', () => {
- const mockDiffContent = {
- diffFile: diffFileMockData,
- shouldRenderDraftRow: jest.fn(),
- hasParallelDraftLeft: jest.fn(),
- hasParallelDraftRight: jest.fn(),
- draftForLine: jest.fn(),
- };
-
- const applyMap = mapParallel(mockDiffContent);
-
- describe('when one side is empty', () => {
- let wrapper;
- let vm;
- const thisLine = diffFileMockData.parallel_diff_lines[0];
- const rightLine = diffFileMockData.parallel_diff_lines[0].right;
-
- beforeEach(() => {
- wrapper = shallowMount(ParallelDiffTableRow, {
- store: createStore(),
- propsData: {
- line: applyMap(thisLine),
- fileHash: diffFileMockData.file_hash,
- filePath: diffFileMockData.file_path,
- contextLinesPath: 'contextLinesPath',
- isHighlighted: false,
- },
- });
-
- vm = wrapper.vm;
- });
-
- it('does not highlight non empty line content when line does not match highlighted row', (done) => {
- vm.$nextTick()
- .then(() => {
- expect(vm.$el.querySelector('.line_content.right-side').classList).not.toContain('hll');
- })
- .then(done)
- .catch(done.fail);
- });
-
- it('highlights nonempty line content when line is the highlighted row', (done) => {
- vm.$nextTick()
- .then(() => {
- vm.$store.state.diffs.highlightedRow = rightLine.line_code;
-
- return vm.$nextTick();
- })
- .then(() => {
- expect(vm.$el.querySelector('.line_content.right-side').classList).toContain('hll');
- })
- .then(done)
- .catch(done.fail);
- });
-
- it('highlights nonempty line content when line is part of a multiline comment', () => {
- wrapper.setProps({ isCommented: true });
- return vm.$nextTick().then(() => {
- expect(vm.$el.querySelector('.line_content.right-side').classList).toContain('hll');
- });
- });
- });
-
- describe('when both sides have content', () => {
- let vm;
- const thisLine = diffFileMockData.parallel_diff_lines[2];
- const rightLine = diffFileMockData.parallel_diff_lines[2].right;
-
- beforeEach(() => {
- vm = createComponentWithStore(Vue.extend(ParallelDiffTableRow), createStore(), {
- line: applyMap(thisLine),
- fileHash: diffFileMockData.file_hash,
- filePath: diffFileMockData.file_path,
- contextLinesPath: 'contextLinesPath',
- isHighlighted: false,
- }).$mount();
- });
-
- it('does not highlight either line when line does not match highlighted row', (done) => {
- vm.$nextTick()
- .then(() => {
- expect(vm.$el.querySelector('.line_content.right-side').classList).not.toContain('hll');
- expect(vm.$el.querySelector('.line_content.left-side').classList).not.toContain('hll');
- })
- .then(done)
- .catch(done.fail);
- });
-
- it('adds hll class to lineContent when line is the highlighted row', (done) => {
- vm.$nextTick()
- .then(() => {
- vm.$store.state.diffs.highlightedRow = rightLine.line_code;
-
- return vm.$nextTick();
- })
- .then(() => {
- expect(vm.$el.querySelector('.line_content.right-side').classList).toContain('hll');
- expect(vm.$el.querySelector('.line_content.left-side').classList).toContain('hll');
- })
- .then(done)
- .catch(done.fail);
- });
-
- describe('sets coverage title and class', () => {
- it('for lines with coverage', (done) => {
- vm.$nextTick()
- .then(() => {
- const name = diffFileMockData.file_path;
- const line = rightLine.new_line;
-
- vm.$store.state.diffs.coverageFiles = { files: { [name]: { [line]: 5 } } };
-
- return vm.$nextTick();
- })
- .then(() => {
- const coverage = vm.$el.querySelector('.line-coverage.right-side');
-
- expect(coverage.title).toContain('Test coverage: 5 hits');
- expect(coverage.classList).toContain('coverage');
- })
- .then(done)
- .catch(done.fail);
- });
-
- it('for lines without coverage', (done) => {
- vm.$nextTick()
- .then(() => {
- const name = diffFileMockData.file_path;
- const line = rightLine.new_line;
-
- vm.$store.state.diffs.coverageFiles = { files: { [name]: { [line]: 0 } } };
-
- return vm.$nextTick();
- })
- .then(() => {
- const coverage = vm.$el.querySelector('.line-coverage.right-side');
-
- expect(coverage.title).toContain('No test coverage');
- expect(coverage.classList).toContain('no-coverage');
- })
- .then(done)
- .catch(done.fail);
- });
-
- it('for unknown lines', (done) => {
- vm.$nextTick()
- .then(() => {
- vm.$store.state.diffs.coverageFiles = {};
-
- return vm.$nextTick();
- })
- .then(() => {
- const coverage = vm.$el.querySelector('.line-coverage.right-side');
-
- expect(coverage.title).not.toContain('Coverage');
- expect(coverage.classList).not.toContain('coverage');
- expect(coverage.classList).not.toContain('no-coverage');
- })
- .then(done)
- .catch(done.fail);
- });
- });
- });
-
- describe('Table Cells', () => {
- let wrapper;
- let store;
- let thisLine;
- const TEST_USER_ID = 'abc123';
- const TEST_USER = { id: TEST_USER_ID };
-
- const createComponent = (props = {}, propsStore = store, data = {}) => {
- wrapper = shallowMount(ParallelDiffTableRow, {
- store: propsStore,
- propsData: {
- line: thisLine,
- fileHash: diffFileMockData.file_hash,
- filePath: diffFileMockData.file_path,
- contextLinesPath: 'contextLinesPath',
- isHighlighted: false,
- ...props,
- },
- data() {
- return data;
- },
- });
- };
-
- beforeEach(() => {
- // eslint-disable-next-line prefer-destructuring
- thisLine = diffFileMockData.parallel_diff_lines[2];
- store = createStore();
- store.state.notes.userData = TEST_USER;
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- const findNewTd = () => wrapper.find({ ref: 'newTd' });
- const findOldTd = () => wrapper.find({ ref: 'oldTd' });
-
- describe('td', () => {
- it('highlights when isHighlighted true', () => {
- store.state.diffs.highlightedRow = thisLine.left.line_code;
- createComponent({}, store);
-
- expect(findNewTd().classes()).toContain('hll');
- expect(findOldTd().classes()).toContain('hll');
- });
-
- it('does not highlight when isHighlighted false', () => {
- createComponent();
-
- expect(findNewTd().classes()).not.toContain('hll');
- expect(findOldTd().classes()).not.toContain('hll');
- });
- });
-
- describe('comment button', () => {
- const findNoteButton = () => wrapper.find({ ref: 'addDiffNoteButtonLeft' });
-
- it.each`
- hover | line | userData | expectation
- ${true} | ${{}} | ${TEST_USER} | ${true}
- ${true} | ${{ line: { left: null } }} | ${TEST_USER} | ${false}
- ${true} | ${{}} | ${null} | ${false}
- ${false} | ${{}} | ${TEST_USER} | ${false}
- `(
- 'exists is $expectation - with userData ($userData)',
- async ({ hover, line, userData, expectation }) => {
- store.state.notes.userData = userData;
- createComponent(line, store);
- if (hover) await wrapper.find('.line_holder').trigger('mouseover');
-
- expect(findNoteButton().exists()).toBe(expectation);
- },
- );
-
- it.each`
- line | expectation
- ${{ ...thisLine, left: { discussions: [] } }} | ${true}
- ${{ ...thisLine, left: { type: 'context', discussions: [] } }} | ${false}
- ${{ ...thisLine, left: { type: 'old-nonewline', discussions: [] } }} | ${false}
- ${{ ...thisLine, left: { discussions: [{}] } }} | ${false}
- `('visible is $expectation - line ($line)', async ({ line, expectation }) => {
- createComponent({ line: applyMap(line) }, store, {
- isLeftHover: true,
- isCommentButtonRendered: true,
- });
-
- expect(findNoteButton().isVisible()).toBe(expectation);
- });
-
- it.each`
- disabled | commentsDisabled
- ${'disabled'} | ${true}
- ${undefined} | ${false}
- `(
- 'has attribute disabled=$disabled when the outer component has prop commentsDisabled=$commentsDisabled',
- ({ disabled, commentsDisabled }) => {
- thisLine.left.commentsDisabled = commentsDisabled;
- createComponent({ line: { ...thisLine } }, store, {
- isLeftHover: true,
- isCommentButtonRendered: true,
- });
-
- expect(findNoteButton().attributes('disabled')).toBe(disabled);
- },
- );
-
- const symlinkishFileTooltip =
- 'Commenting on symbolic links that replace or are replaced by files is currently not supported.';
- const realishFileTooltip =
- 'Commenting on files that replace or are replaced by symbolic links is currently not supported.';
- const otherFileTooltip = 'Add a comment to this line';
- const findTooltip = () => wrapper.find({ ref: 'addNoteTooltipLeft' });
-
- it.each`
- tooltip | commentsDisabled
- ${symlinkishFileTooltip} | ${{ wasSymbolic: true }}
- ${symlinkishFileTooltip} | ${{ isSymbolic: true }}
- ${realishFileTooltip} | ${{ wasReal: true }}
- ${realishFileTooltip} | ${{ isReal: true }}
- ${otherFileTooltip} | ${false}
- `(
- 'has the correct tooltip when commentsDisabled=$commentsDisabled',
- ({ tooltip, commentsDisabled }) => {
- thisLine.left.commentsDisabled = commentsDisabled;
- createComponent({ line: { ...thisLine } }, store, {
- isLeftHover: true,
- isCommentButtonRendered: true,
- });
-
- expect(findTooltip().attributes('title')).toBe(tooltip);
- },
- );
- });
-
- describe('line number', () => {
- const findLineNumberOld = () => wrapper.find({ ref: 'lineNumberRefOld' });
- const findLineNumberNew = () => wrapper.find({ ref: 'lineNumberRefNew' });
-
- it('renders line numbers in correct cells', () => {
- createComponent();
-
- expect(findLineNumberOld().exists()).toBe(true);
- expect(findLineNumberNew().exists()).toBe(true);
- });
-
- describe('with lineNumber prop', () => {
- const TEST_LINE_CODE = 'LC_42';
- const TEST_LINE_NUMBER = 1;
-
- describe.each`
- lineProps | findLineNumber | expectedHref | expectedClickArg
- ${{ line_code: TEST_LINE_CODE, old_line: TEST_LINE_NUMBER }} | ${findLineNumberOld} | ${`#${TEST_LINE_CODE}`} | ${TEST_LINE_CODE}
- ${{ line_code: undefined, old_line: TEST_LINE_NUMBER }} | ${findLineNumberOld} | ${'#'} | ${undefined}
- `(
- 'with line ($lineProps)',
- ({ lineProps, findLineNumber, expectedHref, expectedClickArg }) => {
- beforeEach(() => {
- jest.spyOn(store, 'dispatch').mockImplementation();
- Object.assign(thisLine.left, lineProps);
- Object.assign(thisLine.right, lineProps);
- createComponent({
- line: applyMap({ ...thisLine }),
- });
- });
-
- it('renders', () => {
- expect(findLineNumber().exists()).toBe(true);
- expect(findLineNumber().attributes()).toEqual({
- href: expectedHref,
- 'data-linenumber': TEST_LINE_NUMBER.toString(),
- });
- });
-
- it('on click, dispatches setHighlightedRow', () => {
- expect(store.dispatch).toHaveBeenCalledTimes(1);
-
- findLineNumber().trigger('click');
-
- expect(store.dispatch).toHaveBeenCalledWith(
- 'diffs/setHighlightedRow',
- expectedClickArg,
- );
- expect(store.dispatch).toHaveBeenCalledTimes(2);
- });
- },
- );
- });
- });
-
- describe('diff-gutter-avatars', () => {
- const TEST_LINE_CODE = 'LC_42';
- const TEST_FILE_HASH = diffFileMockData.file_hash;
- const findAvatars = () => wrapper.find(DiffGutterAvatars);
- let line;
-
- beforeEach(() => {
- jest.spyOn(store, 'dispatch').mockImplementation();
-
- line = applyMap({
- left: {
- line_code: TEST_LINE_CODE,
- type: 'new',
- old_line: null,
- new_line: 1,
- discussions: [{ ...discussionsMockData }],
- discussionsExpanded: true,
- text: '+<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n',
- rich_text: '+<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n',
- meta_data: null,
- },
- });
- });
-
- describe('with showCommentButton', () => {
- it('renders if line has discussions', () => {
- createComponent({ line });
-
- expect(findAvatars().props()).toEqual({
- discussions: line.left.discussions,
- discussionsExpanded: line.left.discussionsExpanded,
- });
- });
-
- it('does notrender if line has no discussions', () => {
- line.left.discussions = [];
- createComponent({ line: applyMap(line) });
-
- expect(findAvatars().exists()).toEqual(false);
- });
-
- it('toggles line discussion', () => {
- createComponent({ line });
-
- expect(store.dispatch).toHaveBeenCalledTimes(1);
-
- findAvatars().vm.$emit('toggleLineDiscussions');
-
- expect(store.dispatch).toHaveBeenCalledWith('diffs/toggleLineDiscussions', {
- lineCode: TEST_LINE_CODE,
- fileHash: TEST_FILE_HASH,
- expanded: !line.left.discussionsExpanded,
- });
- });
- });
- });
-
- describe('interoperability', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('adds old side interoperability data attributes', () => {
- expect(findInteropAttributes(wrapper, '.line_content.left-side')).toEqual({
- type: 'old',
- line: thisLine.left.old_line.toString(),
- oldLine: thisLine.left.old_line.toString(),
- });
- });
-
- it('adds new side interoperability data attributes', () => {
- expect(findInteropAttributes(wrapper, '.line_content.right-side')).toEqual({
- type: 'new',
- line: thisLine.right.new_line.toString(),
- newLine: thisLine.right.new_line.toString(),
- });
- });
- });
- });
-});
diff --git a/spec/frontend/diffs/components/parallel_diff_view_spec.js b/spec/frontend/diffs/components/parallel_diff_view_spec.js
deleted file mode 100644
index 452e1f58551..00000000000
--- a/spec/frontend/diffs/components/parallel_diff_view_spec.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import { shallowMount, createLocalVue } from '@vue/test-utils';
-import Vuex from 'vuex';
-import parallelDiffTableRow from '~/diffs/components/parallel_diff_table_row.vue';
-import ParallelDiffView from '~/diffs/components/parallel_diff_view.vue';
-import { createStore } from '~/mr_notes/stores';
-import diffFileMockData from '../mock_data/diff_file';
-
-let wrapper;
-const localVue = createLocalVue();
-
-localVue.use(Vuex);
-
-function factory() {
- const diffFile = { ...diffFileMockData };
- const store = createStore();
-
- wrapper = shallowMount(ParallelDiffView, {
- localVue,
- store,
- propsData: {
- diffFile,
- diffLines: diffFile.parallel_diff_lines,
- },
- });
-}
-
-describe('ParallelDiffView', () => {
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders diff lines', () => {
- factory();
-
- expect(wrapper.findAll(parallelDiffTableRow).length).toBe(8);
- });
-});
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_utils_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_utils_spec.js
index 93cddff8421..1b97011bf7f 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_utils_spec.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_utils_spec.js
@@ -11,7 +11,7 @@ import {
processFilters,
filterToQueryObject,
urlQueryToFilter,
- getRecentlyUsedTokenValues,
+ getRecentlyUsedSuggestions,
setTokenValueToRecentlyUsed,
} from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
@@ -328,32 +328,32 @@ describe('urlQueryToFilter', () => {
);
});
-describe('getRecentlyUsedTokenValues', () => {
+describe('getRecentlyUsedSuggestions', () => {
useLocalStorageSpy();
beforeEach(() => {
localStorage.removeItem(mockStorageKey);
});
- it('returns array containing recently used token values from provided recentTokenValuesStorageKey', () => {
+ it('returns array containing recently used token values from provided recentSuggestionsStorageKey', () => {
setLocalStorageAvailability(true);
const mockExpectedArray = [{ foo: 'bar' }];
localStorage.setItem(mockStorageKey, JSON.stringify(mockExpectedArray));
- expect(getRecentlyUsedTokenValues(mockStorageKey)).toEqual(mockExpectedArray);
+ expect(getRecentlyUsedSuggestions(mockStorageKey)).toEqual(mockExpectedArray);
});
- it('returns empty array when provided recentTokenValuesStorageKey does not have anything in localStorage', () => {
+ it('returns empty array when provided recentSuggestionsStorageKey does not have anything in localStorage', () => {
setLocalStorageAvailability(true);
- expect(getRecentlyUsedTokenValues(mockStorageKey)).toEqual([]);
+ expect(getRecentlyUsedSuggestions(mockStorageKey)).toEqual([]);
});
it('returns empty array when when access to localStorage is not available', () => {
setLocalStorageAvailability(false);
- expect(getRecentlyUsedTokenValues(mockStorageKey)).toEqual([]);
+ expect(getRecentlyUsedSuggestions(mockStorageKey)).toEqual([]);
});
});
@@ -366,7 +366,7 @@ describe('setTokenValueToRecentlyUsed', () => {
localStorage.removeItem(mockStorageKey);
});
- it('adds provided tokenValue to localStorage for recentTokenValuesStorageKey', () => {
+ it('adds provided tokenValue to localStorage for recentSuggestionsStorageKey', () => {
setLocalStorageAvailability(true);
setTokenValueToRecentlyUsed(mockStorageKey, mockTokenValue1);
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js
index 951b050495c..74f579e77ed 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js
@@ -94,7 +94,7 @@ describe('AuthorToken', () => {
it('calls `config.fetchAuthors` with provided searchTerm param', () => {
jest.spyOn(wrapper.vm.config, 'fetchAuthors');
- getBaseToken().vm.$emit('fetch-token-values', mockAuthors[0].username);
+ getBaseToken().vm.$emit('fetch-suggestions', mockAuthors[0].username);
expect(wrapper.vm.config.fetchAuthors).toHaveBeenCalledWith(
mockAuthorToken.fetchPath,
@@ -105,17 +105,17 @@ describe('AuthorToken', () => {
it('sets response to `authors` when request is succesful', () => {
jest.spyOn(wrapper.vm.config, 'fetchAuthors').mockResolvedValue(mockAuthors);
- getBaseToken().vm.$emit('fetch-token-values', 'root');
+ getBaseToken().vm.$emit('fetch-suggestions', 'root');
return waitForPromises().then(() => {
- expect(getBaseToken().props('tokenValues')).toEqual(mockAuthors);
+ expect(getBaseToken().props('suggestions')).toEqual(mockAuthors);
});
});
it('calls `createFlash` with flash error message when request fails', () => {
jest.spyOn(wrapper.vm.config, 'fetchAuthors').mockRejectedValue({});
- getBaseToken().vm.$emit('fetch-token-values', 'root');
+ getBaseToken().vm.$emit('fetch-suggestions', 'root');
return waitForPromises().then(() => {
expect(createFlash).toHaveBeenCalledWith({
@@ -127,17 +127,17 @@ describe('AuthorToken', () => {
it('sets `loading` to false when request completes', async () => {
jest.spyOn(wrapper.vm.config, 'fetchAuthors').mockRejectedValue({});
- getBaseToken().vm.$emit('fetch-token-values', 'root');
+ getBaseToken().vm.$emit('fetch-suggestions', 'root');
await waitForPromises();
- expect(getBaseToken().props('tokensListLoading')).toBe(false);
+ expect(getBaseToken().props('suggestionsLoading')).toBe(false);
});
});
});
describe('template', () => {
- const activateTokenValuesList = async () => {
+ const activateSuggestionsList = async () => {
const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment);
const suggestionsSegment = tokenSegments.at(2);
suggestionsSegment.vm.$emit('activate');
@@ -154,7 +154,7 @@ describe('AuthorToken', () => {
expect(baseTokenEl.exists()).toBe(true);
expect(baseTokenEl.props()).toMatchObject({
- tokenValues: mockAuthors,
+ suggestions: mockAuthors,
fnActiveTokenValue: wrapper.vm.getActiveAuthor,
});
});
@@ -221,7 +221,7 @@ describe('AuthorToken', () => {
stubs: { Portal: true },
});
- await activateTokenValuesList();
+ await activateSuggestionsList();
const suggestions = wrapper.findAll(GlFilteredSearchSuggestion);
@@ -252,7 +252,7 @@ describe('AuthorToken', () => {
stubs: { Portal: true },
});
- await activateTokenValuesList();
+ await activateSuggestionsList();
const suggestions = wrapper.findAll(GlFilteredSearchSuggestion);
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/base_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/base_token_spec.js
index 89c5cedc9b8..cd6ffd679d0 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/base_token_spec.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/base_token_spec.js
@@ -7,7 +7,7 @@ import {
import { DEFAULT_LABELS } from '~/vue_shared/components/filtered_search_bar/constants';
import {
- getRecentlyUsedTokenValues,
+ getRecentlyUsedSuggestions,
setTokenValueToRecentlyUsed,
} from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
@@ -49,10 +49,10 @@ const mockProps = {
config: mockLabelToken,
value: { data: '' },
active: false,
- tokenValues: [],
- tokensListLoading: false,
- defaultTokenValues: DEFAULT_LABELS,
- recentTokenValuesStorageKey: mockStorageKey,
+ suggestions: [],
+ suggestionsLoading: false,
+ defaultSuggestions: DEFAULT_LABELS,
+ recentSuggestionsStorageKey: mockStorageKey,
fnCurrentTokenValue: jest.fn(),
};
@@ -83,7 +83,7 @@ describe('BaseToken', () => {
props: {
...mockProps,
value: { data: `"${mockRegularLabel.title}"` },
- tokenValues: mockLabels,
+ suggestions: mockLabels,
},
});
});
@@ -93,8 +93,8 @@ describe('BaseToken', () => {
});
describe('data', () => {
- it('calls `getRecentlyUsedTokenValues` to populate `recentTokenValues` when `recentTokenValuesStorageKey` is defined', () => {
- expect(getRecentlyUsedTokenValues).toHaveBeenCalledWith(mockStorageKey);
+ it('calls `getRecentlyUsedSuggestions` to populate `recentSuggestions` when `recentSuggestionsStorageKey` is defined', () => {
+ expect(getRecentlyUsedSuggestions).toHaveBeenCalledWith(mockStorageKey);
});
});
@@ -147,15 +147,15 @@ describe('BaseToken', () => {
wrapperWithTokenActive.destroy();
});
- it('emits `fetch-token-values` event on the component when value of this prop is changed to false and `tokenValues` array is empty', async () => {
+ it('emits `fetch-suggestions` event on the component when value of this prop is changed to false and `suggestions` array is empty', async () => {
wrapperWithTokenActive.setProps({
active: false,
});
await wrapperWithTokenActive.vm.$nextTick();
- expect(wrapperWithTokenActive.emitted('fetch-token-values')).toBeTruthy();
- expect(wrapperWithTokenActive.emitted('fetch-token-values')).toEqual([
+ expect(wrapperWithTokenActive.emitted('fetch-suggestions')).toBeTruthy();
+ expect(wrapperWithTokenActive.emitted('fetch-suggestions')).toEqual([
[`"${mockRegularLabel.title}"`],
]);
});
@@ -164,7 +164,7 @@ describe('BaseToken', () => {
describe('methods', () => {
describe('handleTokenValueSelected', () => {
- it('calls `setTokenValueToRecentlyUsed` when `recentTokenValuesStorageKey` is defined', () => {
+ it('calls `setTokenValueToRecentlyUsed` when `recentSuggestionsStorageKey` is defined', () => {
const mockTokenValue = {
id: 1,
title: 'Foo',
@@ -175,14 +175,14 @@ describe('BaseToken', () => {
expect(setTokenValueToRecentlyUsed).toHaveBeenCalledWith(mockStorageKey, mockTokenValue);
});
- it('does not add token from preloadedTokenValues', async () => {
+ it('does not add token from preloadedSuggestions', async () => {
const mockTokenValue = {
id: 1,
title: 'Foo',
};
wrapper.setProps({
- preloadedTokenValues: [mockTokenValue],
+ preloadedSuggestions: [mockTokenValue],
});
await wrapper.vm.$nextTick();
@@ -228,7 +228,7 @@ describe('BaseToken', () => {
wrapperWithNoStubs.destroy();
});
- it('emits `fetch-token-values` event on component after a delay when component emits `input` event', async () => {
+ it('emits `fetch-suggestions` event on component after a delay when component emits `input` event', async () => {
jest.useFakeTimers();
wrapperWithNoStubs.find(GlFilteredSearchToken).vm.$emit('input', { data: 'foo' });
@@ -236,8 +236,8 @@ describe('BaseToken', () => {
jest.runAllTimers();
- expect(wrapperWithNoStubs.emitted('fetch-token-values')).toBeTruthy();
- expect(wrapperWithNoStubs.emitted('fetch-token-values')[2]).toEqual(['foo']);
+ expect(wrapperWithNoStubs.emitted('fetch-suggestions')).toBeTruthy();
+ expect(wrapperWithNoStubs.emitted('fetch-suggestions')[2]).toEqual(['foo']);
});
});
});
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js
index cc40ff96b65..ec9458f64d2 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js
@@ -159,7 +159,7 @@ describe('LabelToken', () => {
expect(baseTokenEl.exists()).toBe(true);
expect(baseTokenEl.props()).toMatchObject({
- tokenValues: mockLabels,
+ suggestions: mockLabels,
fnActiveTokenValue: wrapper.vm.getActiveLabel,
});
});
diff --git a/spec/frontend_integration/diffs/diffs_interopability_spec.js b/spec/frontend_integration/diffs/diffs_interopability_spec.js
index cb7659e16d3..448641ed834 100644
--- a/spec/frontend_integration/diffs/diffs_interopability_spec.js
+++ b/spec/frontend_integration/diffs/diffs_interopability_spec.js
@@ -8,15 +8,6 @@ import {
getCodeElementFromLineNumber,
} from './diffs_interopability_api';
-jest.mock('~/vue_shared/mixins/gl_feature_flags_mixin', () => () => ({
- inject: {
- glFeatures: {
- from: 'window.gon.features',
- default: () => global.window.gon?.features,
- },
- },
-}));
-
const TEST_PROJECT_PATH = 'gitlab-org/gitlab-test';
const TEST_BASE_URL = `/${TEST_PROJECT_PATH}/-/merge_requests/1/`;
const TEST_DIFF_FILE = 'files/js/commit.coffee';
@@ -114,48 +105,41 @@ describe('diffs third party interoperability', () => {
);
describe.each`
- desc | unifiedDiffComponents | view | rowSelector | codeSelector | expectation
- ${'inline view'} | ${false} | ${'inline'} | ${'tr.line_holder'} | ${'td.line_content'} | ${EXPECT_INLINE}
- ${'parallel view left side'} | ${false} | ${'parallel'} | ${'tr.line_holder'} | ${'td.line_content.left-side'} | ${EXPECT_PARALLEL_LEFT_SIDE}
- ${'parallel view right side'} | ${false} | ${'parallel'} | ${'tr.line_holder'} | ${'td.line_content.right-side'} | ${EXPECT_PARALLEL_RIGHT_SIDE}
- ${'inline view'} | ${true} | ${'inline'} | ${'.diff-tr.line_holder'} | ${'.diff-td.line_content'} | ${EXPECT_INLINE}
- ${'parallel view left side'} | ${true} | ${'parallel'} | ${'.diff-tr.line_holder'} | ${'.diff-td.line_content.left-side'} | ${EXPECT_PARALLEL_LEFT_SIDE}
- ${'parallel view right side'} | ${true} | ${'parallel'} | ${'.diff-tr.line_holder'} | ${'.diff-td.line_content.right-side'} | ${EXPECT_PARALLEL_RIGHT_SIDE}
- `(
- '$desc (unifiedDiffComponents=$unifiedDiffComponents)',
- ({ unifiedDiffComponents, view, rowSelector, codeSelector, expectation }) => {
- beforeEach(async () => {
- global.jsdom.reconfigure({
- url: `${TEST_HOST}/${TEST_BASE_URL}/diffs?view=${view}`,
- });
- window.gon.features = { unifiedDiffComponents };
-
- vm = startDiffsApp();
-
- await waitFor(() => expect(hasLines(rowSelector)).toBe(true));
+ desc | view | rowSelector | codeSelector | expectation
+ ${'inline view'} | ${'inline'} | ${'.diff-tr.line_holder'} | ${'.diff-td.line_content'} | ${EXPECT_INLINE}
+ ${'parallel view left side'} | ${'parallel'} | ${'.diff-tr.line_holder'} | ${'.diff-td.line_content.left-side'} | ${EXPECT_PARALLEL_LEFT_SIDE}
+ ${'parallel view right side'} | ${'parallel'} | ${'.diff-tr.line_holder'} | ${'.diff-td.line_content.right-side'} | ${EXPECT_PARALLEL_RIGHT_SIDE}
+ `('$desc', ({ view, rowSelector, codeSelector, expectation }) => {
+ beforeEach(async () => {
+ global.jsdom.reconfigure({
+ url: `${TEST_HOST}/${TEST_BASE_URL}/diffs?view=${view}`,
});
- it('should match diff model', () => {
- const lines = findLineElements(rowSelector);
- const codes = findCodeElements(lines, codeSelector);
+ vm = startDiffsApp();
- expect(getCodeElementsInteropModel(codes)).toEqual(expectation);
- });
+ await waitFor(() => expect(hasLines(rowSelector)).toBe(true));
+ });
+
+ it('should match diff model', () => {
+ const lines = findLineElements(rowSelector);
+ const codes = findCodeElements(lines, codeSelector);
+
+ expect(getCodeElementsInteropModel(codes)).toEqual(expectation);
+ });
- it.each`
- lineNumber | part | expectedText
- ${4} | ${'base'} | ${'new CommitFile(this)'}
- ${4} | ${'head'} | ${'new CommitFile(@)'}
- ${2} | ${'base'} | ${'constructor: ->'}
- ${2} | ${'head'} | ${'constructor: ->'}
- `(
- 'should find code element lineNumber=$lineNumber part=$part',
- ({ lineNumber, part, expectedText }) => {
- const codeElement = getCodeElementFromLineNumber(findDiffFile(), lineNumber, part);
-
- expect(codeElement.textContent.trim()).toBe(expectedText);
- },
- );
- },
- );
+ it.each`
+ lineNumber | part | expectedText
+ ${4} | ${'base'} | ${'new CommitFile(this)'}
+ ${4} | ${'head'} | ${'new CommitFile(@)'}
+ ${2} | ${'base'} | ${'constructor: ->'}
+ ${2} | ${'head'} | ${'constructor: ->'}
+ `(
+ 'should find code element lineNumber=$lineNumber part=$part',
+ ({ lineNumber, part, expectedText }) => {
+ const codeElement = getCodeElementFromLineNumber(findDiffFile(), lineNumber, part);
+
+ expect(codeElement.textContent.trim()).toBe(expectedText);
+ },
+ );
+ });
});
diff --git a/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb b/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb
index 8d625cab1d8..9bd89abae83 100644
--- a/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb
+++ b/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb
@@ -83,24 +83,8 @@ RSpec.describe Gitlab::Cache::Ci::ProjectPipelineStatus, :clean_gitlab_redis_cac
expect(pipeline_status).not_to be_has_cache
end
- context 'ci_pipeline_status_omit_commit_sha_in_cache_key is enabled' do
- before do
- stub_feature_flags(ci_pipeline_status_omit_commit_sha_in_cache_key: project)
- end
-
- it 'makes a Gitaly call' do
- expect { pipeline_status.load_status }.to change { Gitlab::GitalyClient.get_request_count }.by(1)
- end
- end
-
- context 'ci_pipeline_status_omit_commit_sha_in_cache_key is disabled' do
- before do
- stub_feature_flags(ci_pipeline_status_omit_commit_sha_in_cache_key: false)
- end
-
- it 'makes a Gitaly calls' do
- expect { pipeline_status.load_status }.to change { Gitlab::GitalyClient.get_request_count }.by(1)
- end
+ it 'makes a Gitaly call' do
+ expect { pipeline_status.load_status }.to change { Gitlab::GitalyClient.get_request_count }.by(1)
end
end
@@ -111,24 +95,8 @@ RSpec.describe Gitlab::Cache::Ci::ProjectPipelineStatus, :clean_gitlab_redis_cac
expect(pipeline_status).to be_has_cache
end
- context 'ci_pipeline_status_omit_commit_sha_in_cache_key is enabled' do
- before do
- stub_feature_flags(ci_pipeline_status_omit_commit_sha_in_cache_key: project)
- end
-
- it 'makes no Gitaly calls' do
- expect { pipeline_status.load_status }.to change { Gitlab::GitalyClient.get_request_count }.by(0)
- end
- end
-
- context 'ci_pipeline_status_omit_commit_sha_in_cache_key is disabled' do
- before do
- stub_feature_flags(ci_pipeline_status_omit_commit_sha_in_cache_key: false)
- end
-
- it 'makes a Gitaly calls' do
- expect { pipeline_status.load_status }.to change { Gitlab::GitalyClient.get_request_count }.by(1)
- end
+ it 'makes no Gitaly calls' do
+ expect { pipeline_status.load_status }.to change { Gitlab::GitalyClient.get_request_count }.by(0)
end
end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 62dec522161..9c118325421 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -1966,6 +1966,23 @@ RSpec.describe Ci::Build do
end
end
+ describe '#tag_list' do
+ let_it_be(:build) { create(:ci_build, tag_list: ['tag']) }
+
+ context 'when tags are preloaded' do
+ it 'does not trigger queries' do
+ build_with_tags = described_class.eager_load_tags.id_in([build]).to_a.first
+
+ expect { build_with_tags.tag_list }.not_to exceed_all_query_limit(0)
+ expect(build_with_tags.tag_list).to eq(['tag'])
+ end
+ end
+
+ context 'when tags are not preloaded' do
+ it { expect(described_class.find(build.id).tag_list).to eq(['tag']) }
+ end
+ end
+
describe '#has_tags?' do
context 'when build has tags' do
subject { create(:ci_build, tag_list: ['tag']) }
diff --git a/spec/presenters/packages/nuget/service_index_presenter_spec.rb b/spec/presenters/packages/nuget/service_index_presenter_spec.rb
index 9c95fbc8fd2..aa69a9c3017 100644
--- a/spec/presenters/packages/nuget/service_index_presenter_spec.rb
+++ b/spec/presenters/packages/nuget/service_index_presenter_spec.rb
@@ -27,7 +27,7 @@ RSpec.describe ::Packages::Nuget::ServiceIndexPresenter do
describe '#resources' do
subject { presenter.resources }
- shared_examples 'returning valid resources' do |resources_count: 8, include_publish_service: true|
+ shared_examples 'returning valid resources' do |resources_count: 9, include_publish_service: true|
it 'has valid resources' do
expect(subject.size).to eq resources_count
subject.each do |resource|
@@ -38,10 +38,15 @@ RSpec.describe ::Packages::Nuget::ServiceIndexPresenter do
end
end
- it "does #{'not ' unless include_publish_service}return the publish resource" do
+ it "does #{'not ' unless include_publish_service}return the publish resource", :aggregate_failures do
services_types = subject.map { |res| res[:@type] }
- described_class::SERVICE_VERSIONS[:publish].each do |publish_service_version|
+ publish_service_versions = [
+ described_class::SERVICE_VERSIONS[:publish],
+ described_class::SERVICE_VERSIONS[:symbol]
+ ].flatten
+
+ publish_service_versions.each do |publish_service_version|
if include_publish_service
expect(services_types).to include(publish_service_version)
else
@@ -54,7 +59,7 @@ RSpec.describe ::Packages::Nuget::ServiceIndexPresenter do
context 'for a group' do
let(:target) { group }
- # at the group level we don't have the publish and download service
+ # at the group level we don't have the publish, symbol, and download service
it_behaves_like 'returning valid resources', resources_count: 6, include_publish_service: false
end
diff --git a/spec/requests/api/nuget_project_packages_spec.rb b/spec/requests/api/nuget_project_packages_spec.rb
index 572736cfc86..a9e695dacbb 100644
--- a/spec/requests/api/nuget_project_packages_spec.rb
+++ b/spec/requests/api/nuget_project_packages_spec.rb
@@ -92,9 +92,10 @@ RSpec.describe API::NugetProjectPackages do
describe 'GET /api/v4/projects/:id/packages/nuget/download/*package_name/*package_version/*package_filename' do
let_it_be(:package_name) { 'Dummy.Package' }
- let_it_be(:package) { create(:nuget_package, project: project, name: package_name) }
+ let_it_be(:package) { create(:nuget_package, :with_symbol_package, project: project, name: package_name) }
- let(:url) { "/projects/#{target.id}/packages/nuget/download/#{package.name}/#{package.version}/#{package.name}.#{package.version}.nupkg" }
+ let(:format) { 'nupkg' }
+ let(:url) { "/projects/#{target.id}/packages/nuget/download/#{package.name}/#{package.version}/#{package.name}.#{package.version}.#{format}" }
subject { get api(url) }
@@ -154,50 +155,7 @@ RSpec.describe API::NugetProjectPackages do
subject { put api(url), headers: headers }
- context 'with valid project' do
- where(:visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
- 'PUBLIC' | :developer | true | true | 'process nuget workhorse authorization' | :success
- 'PUBLIC' | :guest | true | true | 'rejects nuget packages access' | :forbidden
- 'PUBLIC' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
- 'PUBLIC' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
- 'PUBLIC' | :developer | false | true | 'rejects nuget packages access' | :forbidden
- 'PUBLIC' | :guest | false | true | 'rejects nuget packages access' | :forbidden
- 'PUBLIC' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
- 'PUBLIC' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
- 'PUBLIC' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :developer | true | true | 'process nuget workhorse authorization' | :success
- 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
- 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
- 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
- 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
- end
-
- with_them do
- let(:token) { user_token ? personal_access_token.token : 'wrong' }
- let(:user_headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
- let(:headers) { user_headers.merge(workhorse_headers) }
-
- before do
- update_visibility_to(Gitlab::VisibilityLevel.const_get(visibility_level, false))
- end
-
- it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
- end
- end
-
- it_behaves_like 'deploy token for package uploads'
-
- it_behaves_like 'job token for package uploads', authorize_endpoint: true do
- let_it_be(:job) { create(:ci_build, :running, user: user, project: project) }
- end
-
- it_behaves_like 'rejects nuget access with unknown target id'
-
- it_behaves_like 'rejects nuget access with invalid target id'
+ it_behaves_like 'nuget authorize upload endpoint'
end
describe 'PUT /api/v4/projects/:id/packages/nuget' do
@@ -221,63 +179,42 @@ RSpec.describe API::NugetProjectPackages do
)
end
- context 'with valid project' do
- where(:visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
- 'PUBLIC' | :developer | true | true | 'process nuget upload' | :created
- 'PUBLIC' | :guest | true | true | 'rejects nuget packages access' | :forbidden
- 'PUBLIC' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
- 'PUBLIC' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
- 'PUBLIC' | :developer | false | true | 'rejects nuget packages access' | :forbidden
- 'PUBLIC' | :guest | false | true | 'rejects nuget packages access' | :forbidden
- 'PUBLIC' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
- 'PUBLIC' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
- 'PUBLIC' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :developer | true | true | 'process nuget upload' | :created
- 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
- 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
- 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
- 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
- end
-
- with_them do
- let(:token) { user_token ? personal_access_token.token : 'wrong' }
- let(:user_headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
- let(:headers) { user_headers.merge(workhorse_headers) }
- let(:snowplow_gitlab_standard_context) { { project: project, user: user, namespace: project.namespace } }
-
- before do
- update_visibility_to(Gitlab::VisibilityLevel.const_get(visibility_level, false))
- end
-
- it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
- end
- end
+ it_behaves_like 'nuget upload endpoint'
+ end
- it_behaves_like 'deploy token for package uploads'
+ describe 'PUT /api/v4/projects/:id/packages/nuget/symbolpackage/authorize' do
+ include_context 'workhorse headers'
- it_behaves_like 'job token for package uploads' do
- let_it_be(:job) { create(:ci_build, :running, user: user, project: project) }
- end
+ let(:url) { "/projects/#{target.id}/packages/nuget/symbolpackage/authorize" }
+ let(:headers) { {} }
- it_behaves_like 'rejects nuget access with unknown target id'
+ subject { put api(url), headers: headers }
- it_behaves_like 'rejects nuget access with invalid target id'
+ it_behaves_like 'nuget authorize upload endpoint'
+ end
- context 'file size above maximum limit' do
- let(:headers) { basic_auth_header(deploy_token.username, deploy_token.token).merge(workhorse_headers) }
+ describe 'PUT /api/v4/projects/:id/packages/nuget/symbolpackage' do
+ include_context 'workhorse headers'
- before do
- allow_next_instance_of(UploadedFile) do |uploaded_file|
- allow(uploaded_file).to receive(:size).and_return(project.actual_limits.nuget_max_file_size + 1)
- end
- end
+ let_it_be(:file_name) { 'package.snupkg' }
+ let(:url) { "/projects/#{target.id}/packages/nuget/symbolpackage" }
+ let(:headers) { {} }
+ let(:params) { { package: temp_file(file_name) } }
+ let(:file_key) { :package }
+ let(:send_rewritten_field) { true }
- it_behaves_like 'returning response status', :bad_request
+ subject do
+ workhorse_finalize(
+ api(url),
+ method: :put,
+ file_key: file_key,
+ params: params,
+ headers: headers,
+ send_rewritten_field: send_rewritten_field
+ )
end
+
+ it_behaves_like 'nuget upload endpoint', symbol_package: true
end
def update_visibility_to(visibility)
diff --git a/spec/services/packages/nuget/metadata_extraction_service_spec.rb b/spec/services/packages/nuget/metadata_extraction_service_spec.rb
index 79428b58bd9..8eddd27f8a2 100644
--- a/spec/services/packages/nuget/metadata_extraction_service_spec.rb
+++ b/spec/services/packages/nuget/metadata_extraction_service_spec.rb
@@ -21,7 +21,8 @@ RSpec.describe Packages::Nuget::MetadataExtractionService do
version: '12.0.3'
}
],
- package_tags: []
+ package_tags: [],
+ package_types: []
}
it { is_expected.to eq(expected_metadata) }
@@ -47,6 +48,16 @@ RSpec.describe Packages::Nuget::MetadataExtractionService do
end
end
+ context 'with package types' do
+ let(:nuspec_filepath) { 'packages/nuget/with_package_types.nuspec' }
+
+ it { is_expected.to have_key(:package_types) }
+
+ it 'extracts package types' do
+ expect(subject[:package_types]).to include('SymbolsPackage')
+ end
+ end
+
context 'with a nuspec file with metadata' do
let(:nuspec_filepath) { 'packages/nuget/with_metadata.nuspec' }
diff --git a/spec/services/packages/nuget/update_package_from_metadata_service_spec.rb b/spec/services/packages/nuget/update_package_from_metadata_service_spec.rb
index ffe1a5b7646..328484c3e5a 100644
--- a/spec/services/packages/nuget/update_package_from_metadata_service_spec.rb
+++ b/spec/services/packages/nuget/update_package_from_metadata_service_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_redis_shared_state do
include ExclusiveLeaseHelpers
- let(:package) { create(:nuget_package, :processing) }
+ let(:package) { create(:nuget_package, :processing, :with_symbol_package) }
let(:package_file) { package.package_files.first }
let(:service) { described_class.new(package_file) }
let(:package_name) { 'DummyProject.DummyPackage' }
@@ -201,6 +201,41 @@ RSpec.describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_
it_behaves_like 'raising an', ::Packages::Nuget::MetadataExtractionService::ExtractionError
end
+ context 'with a symbol package' do
+ let(:package_file) { package.package_files.last }
+ let(:package_file_name) { 'dummyproject.dummypackage.1.0.0.snupkg' }
+
+ context 'with no existing package' do
+ let(:package_id) { package.id }
+
+ it_behaves_like 'raising an', ::Packages::Nuget::UpdatePackageFromMetadataService::InvalidMetadataError
+ end
+
+ context 'with existing package' do
+ let!(:existing_package) { create(:nuget_package, project: package.project, name: package_name, version: package_version) }
+ let(:package_id) { existing_package.id }
+
+ it 'link existing package and updates package file', :aggregate_failures do
+ expect(service).to receive(:try_obtain_lease).and_call_original
+ expect(::Packages::Nuget::SyncMetadatumService).not_to receive(:new)
+ expect(::Packages::UpdateTagsService).not_to receive(:new)
+
+ expect { subject }
+ .to change { ::Packages::Package.count }.by(-1)
+ .and change { Packages::Dependency.count }.by(0)
+ .and change { Packages::DependencyLink.count }.by(0)
+ .and change { Packages::Nuget::DependencyLinkMetadatum.count }.by(0)
+ .and change { ::Packages::Nuget::Metadatum.count }.by(0)
+ expect(package_file.reload.file_name).to eq(package_file_name)
+ expect(package_file.package).to eq(existing_package)
+ end
+
+ it_behaves_like 'taking the lease'
+
+ it_behaves_like 'not updating the package if the lease is taken'
+ end
+ end
+
context 'with an invalid package name' do
invalid_names = [
'',
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 492b9a478fe..a71fc172ebd 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -271,7 +271,6 @@ RSpec.configure do |config|
# See https://gitlab.com/gitlab-org/gitlab/-/issues/33867
stub_feature_flags(file_identifier_hash: false)
- stub_feature_flags(unified_diff_components: false)
stub_feature_flags(diffs_virtual_scrolling: false)
# The following `vue_issues_list`/`vue_issuables_list` stubs can be removed
diff --git a/spec/support/helpers/merge_request_diff_helpers.rb b/spec/support/helpers/merge_request_diff_helpers.rb
index 49beecc6d4b..30afde7efed 100644
--- a/spec/support/helpers/merge_request_diff_helpers.rb
+++ b/spec/support/helpers/merge_request_diff_helpers.rb
@@ -3,8 +3,8 @@
module MergeRequestDiffHelpers
def click_diff_line(line_holder, diff_side = nil)
line = get_line_components(line_holder, diff_side)
- line[:content].hover
- line[:num].find('.js-add-diff-note-button', visible: false).send_keys(:return)
+ line_holder.hover
+ line[:num].find('.js-add-diff-note-button').click
end
def get_line_components(line_holder, diff_side = nil)
diff --git a/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb
index 617fdecbb5b..cdbb8f8c6e8 100644
--- a/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb
@@ -136,8 +136,8 @@ RSpec.shared_examples 'process nuget workhorse authorization' do |user_type, sta
end
end
-RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member = true|
- RSpec.shared_examples 'creates nuget package files' do
+RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member = true, symbol_package = false|
+ shared_examples 'creates nuget package files' do
it 'creates package files' do
expect(::Packages::Nuget::ExtractionWorker).to receive(:perform_async).once
expect { subject }
@@ -146,7 +146,7 @@ RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member =
expect(response).to have_gitlab_http_status(status)
package_file = target.packages.last.package_files.reload.last
- expect(package_file.file_name).to eq('package.nupkg')
+ expect(package_file.file_name).to eq(file_name)
end
end
@@ -169,7 +169,10 @@ RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member =
context 'with correct params' do
it_behaves_like 'package workhorse uploads'
it_behaves_like 'creates nuget package files'
- it_behaves_like 'a package tracking event', 'API::NugetPackages', 'push_package'
+
+ unless symbol_package
+ it_behaves_like 'a package tracking event', 'API::NugetPackages', 'push_package'
+ end
end
end
@@ -300,6 +303,16 @@ RSpec.shared_examples 'process nuget download content request' do |user_type, st
it_behaves_like 'rejects nuget packages access', :anonymous, :not_found
end
+ context 'with symbol package' do
+ let(:format) { 'snupkg' }
+
+ it 'returns a valid package archive' do
+ subject
+
+ expect(response.media_type).to eq('application/octet-stream')
+ end
+ end
+
context 'with lower case package name' do
let_it_be(:package_name) { 'dummy.package' }
@@ -407,3 +420,114 @@ RSpec.shared_examples 'rejects nuget access with unknown target id' do
end
end
end
+
+RSpec.shared_examples 'nuget authorize upload endpoint' do
+ using RSpec::Parameterized::TableSyntax
+
+ context 'with valid project' do
+ where(:visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
+ 'PUBLIC' | :developer | true | true | 'process nuget workhorse authorization' | :success
+ 'PUBLIC' | :guest | true | true | 'rejects nuget packages access' | :forbidden
+ 'PUBLIC' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :developer | false | true | 'rejects nuget packages access' | :forbidden
+ 'PUBLIC' | :guest | false | true | 'rejects nuget packages access' | :forbidden
+ 'PUBLIC' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :developer | true | true | 'process nuget workhorse authorization' | :success
+ 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
+ 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
+ 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
+ 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
+ end
+
+ with_them do
+ let(:token) { user_token ? personal_access_token.token : 'wrong' }
+ let(:user_headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
+ let(:headers) { user_headers.merge(workhorse_headers) }
+
+ before do
+ update_visibility_to(Gitlab::VisibilityLevel.const_get(visibility_level, false))
+ end
+
+ it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
+ end
+ end
+
+ it_behaves_like 'deploy token for package uploads'
+
+ it_behaves_like 'job token for package uploads', authorize_endpoint: true do
+ let_it_be(:job) { create(:ci_build, :running, user: user, project: project) }
+ end
+
+ it_behaves_like 'rejects nuget access with unknown target id'
+
+ it_behaves_like 'rejects nuget access with invalid target id'
+end
+
+RSpec.shared_examples 'nuget upload endpoint' do |symbol_package: false|
+ using RSpec::Parameterized::TableSyntax
+
+ context 'with valid project' do
+ where(:visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
+ 'PUBLIC' | :developer | true | true | 'process nuget upload' | :created
+ 'PUBLIC' | :guest | true | true | 'rejects nuget packages access' | :forbidden
+ 'PUBLIC' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :developer | false | true | 'rejects nuget packages access' | :forbidden
+ 'PUBLIC' | :guest | false | true | 'rejects nuget packages access' | :forbidden
+ 'PUBLIC' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :developer | true | true | 'process nuget upload' | :created
+ 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
+ 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
+ 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
+ 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
+ end
+
+ with_them do
+ let(:token) { user_token ? personal_access_token.token : 'wrong' }
+ let(:user_headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
+ let(:headers) { user_headers.merge(workhorse_headers) }
+ let(:snowplow_gitlab_standard_context) { { project: project, user: user, namespace: project.namespace } }
+
+ before do
+ update_visibility_to(Gitlab::VisibilityLevel.const_get(visibility_level, false))
+ end
+
+ it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member], symbol_package
+ end
+ end
+
+ it_behaves_like 'deploy token for package uploads'
+
+ it_behaves_like 'job token for package uploads' do
+ let_it_be(:job) { create(:ci_build, :running, user: user, project: project) }
+ end
+
+ it_behaves_like 'rejects nuget access with unknown target id'
+
+ it_behaves_like 'rejects nuget access with invalid target id'
+
+ context 'file size above maximum limit' do
+ let(:headers) { basic_auth_header(deploy_token.username, deploy_token.token).merge(workhorse_headers) }
+
+ before do
+ allow_next_instance_of(UploadedFile) do |uploaded_file|
+ allow(uploaded_file).to receive(:size).and_return(project.actual_limits.nuget_max_file_size + 1)
+ end
+ end
+
+ it_behaves_like 'returning response status', :bad_request
+ end
+end