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>2022-01-25 00:14:06 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-01-25 00:14:06 +0300
commit8e73c80c681d8b02633ae25dbd642ecff4864511 (patch)
tree5d33c6c8cd78c596f80997864b3558edf430b666 /spec
parentfdc98c3e3cfdca66222044fc6aa195011d227ee2 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/finders/autocomplete/users_finder_spec.rb9
-rw-r--r--spec/finders/ci/daily_build_group_report_results_finder_spec.rb2
-rw-r--r--spec/finders/merge_request_target_project_finder_spec.rb4
-rw-r--r--spec/frontend/__helpers__/mock_apollo_helper.js6
-rw-r--r--spec/frontend/admin/users/components/users_table_spec.js12
-rw-r--r--spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js17
-rw-r--r--spec/frontend/analytics/usage_trends/components/usage_trends_count_chart_spec.js9
-rw-r--r--spec/frontend/boards/board_list_spec.js5
-rw-r--r--spec/frontend/clusters/agents/components/activity_events_list_spec.js7
-rw-r--r--spec/frontend/clusters/agents/components/show_spec.js20
-rw-r--r--spec/frontend/clusters_list/components/agent_options_spec.js7
-rw-r--r--spec/frontend/clusters_list/components/install_agent_modal_spec.js42
-rw-r--r--spec/frontend/clusters_list/mocks/apollo.js5
-rw-r--r--spec/frontend/design_management/pages/index_spec.js18
-rw-r--r--spec/frontend/issues/list/components/issues_list_app_spec.js15
-rw-r--r--spec/frontend/issues/list/components/new_issue_dropdown_spec.js11
-rw-r--r--spec/frontend/jobs/bridge/app_spec.js8
-rw-r--r--spec/frontend/jobs/components/table/job_table_app_spec.js4
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_spec.js4
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js2
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/pages/list_spec.js9
-rw-r--r--spec/frontend/packages_and_registries/dependency_proxy/app_spec.js6
-rw-r--r--spec/frontend/packages_and_registries/package_registry/pages/list_spec.js15
-rw-r--r--spec/frontend/packages_and_registries/settings/project/settings/components/registry_settings_app_spec.js23
-rw-r--r--spec/frontend/pipeline_editor/components/header/pipeline_status_spec.js4
-rw-r--r--spec/frontend/pipeline_editor/components/header/pipline_editor_mini_graph_spec.js6
-rw-r--r--spec/frontend/pipelines/components/jobs/jobs_app_spec.js6
-rw-r--r--spec/frontend/pipelines/graph/graph_component_wrapper_spec.js58
-rw-r--r--spec/frontend/pipelines/graph/linked_pipelines_column_spec.js14
-rw-r--r--spec/frontend/pipelines/notification/deprecated_type_keyword_notification_spec.js16
-rw-r--r--spec/frontend/projects/new/components/new_project_url_select_spec.js5
-rw-r--r--spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js5
-rw-r--r--spec/frontend/releases/components/app_index_apollo_client_spec.js4
-rw-r--r--spec/frontend/releases/components/app_show_spec.js13
-rw-r--r--spec/frontend/repository/components/blob_content_viewer_spec.js4
-rw-r--r--spec/frontend/runner/admin_runners/admin_runners_app_spec.js4
-rw-r--r--spec/frontend/runner/components/cells/runner_actions_cell_spec.js15
-rw-r--r--spec/frontend/runner/components/registration/registration_dropdown_spec.js4
-rw-r--r--spec/frontend/runner/group_runners/group_runners_app_spec.js4
-rw-r--r--spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js3
-rw-r--r--spec/frontend/vue_shared/components/runner_instructions/runner_instructions_modal_spec.js18
-rw-r--r--spec/graphql/resolvers/merge_requests_resolver_spec.rb60
-rw-r--r--spec/lib/bulk_imports/common/extractors/graphql_extractor_spec.rb15
-rw-r--r--spec/lib/bulk_imports/common/graphql/get_members_query_spec.rb56
-rw-r--r--spec/lib/bulk_imports/common/pipelines/members_pipeline_spec.rb161
-rw-r--r--spec/lib/bulk_imports/groups/graphql/get_group_query_spec.rb27
-rw-r--r--spec/lib/bulk_imports/groups/graphql/get_members_query_spec.rb35
-rw-r--r--spec/lib/bulk_imports/groups/graphql/get_projects_query_spec.rb40
-rw-r--r--spec/lib/bulk_imports/groups/pipelines/members_pipeline_spec.rb119
-rw-r--r--spec/lib/bulk_imports/groups/stage_spec.rb2
-rw-r--r--spec/lib/bulk_imports/groups/transformers/member_attributes_transformer_spec.rb24
-rw-r--r--spec/lib/bulk_imports/projects/graphql/get_project_query_spec.rb27
-rw-r--r--spec/lib/bulk_imports/projects/graphql/get_repository_query_spec.rb32
-rw-r--r--spec/lib/bulk_imports/projects/graphql/get_snippet_repository_query_spec.rb78
-rw-r--r--spec/lib/gitlab/ci/build/artifacts/expire_in_parser_spec.rb21
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb2
-rw-r--r--spec/lib/gitlab/process_memory_cache/helper_spec.rb52
-rw-r--r--spec/models/merge_request_spec.rb15
-rw-r--r--spec/requests/api/merge_requests_spec.rb20
-rw-r--r--spec/support/shared_examples/graphql/mutations/security/ci_configuration_shared_examples.rb2
-rw-r--r--spec/workers/every_sidekiq_worker_spec.rb1
-rw-r--r--spec/workers/pages_update_configuration_worker_spec.rb12
62 files changed, 723 insertions, 521 deletions
diff --git a/spec/finders/autocomplete/users_finder_spec.rb b/spec/finders/autocomplete/users_finder_spec.rb
index 28bd7e12916..df7a14d440e 100644
--- a/spec/finders/autocomplete/users_finder_spec.rb
+++ b/spec/finders/autocomplete/users_finder_spec.rb
@@ -3,6 +3,9 @@
require 'spec_helper'
RSpec.describe Autocomplete::UsersFinder do
+ # TODO update when multiple owners are possible in projects
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/21432
+
describe '#execute' do
let!(:user1) { create(:user, username: 'johndoe') }
let!(:user2) { create(:user, :blocked, username: 'notsorandom') }
@@ -25,19 +28,19 @@ RSpec.describe Autocomplete::UsersFinder do
context 'when project passed' do
let(:project) { create(:project) }
- it { is_expected.to match_array([project.owner]) }
+ it { is_expected.to match_array([project.first_owner]) }
context 'when author_id passed' do
context 'and author is active' do
let(:params) { { author_id: user1.id } }
- it { is_expected.to match_array([project.owner, user1]) }
+ it { is_expected.to match_array([project.first_owner, user1]) }
end
context 'and author is blocked' do
let(:params) { { author_id: user2.id } }
- it { is_expected.to match_array([project.owner]) }
+ it { is_expected.to match_array([project.first_owner]) }
end
end
end
diff --git a/spec/finders/ci/daily_build_group_report_results_finder_spec.rb b/spec/finders/ci/daily_build_group_report_results_finder_spec.rb
index cf15a00323b..5352cfe5238 100644
--- a/spec/finders/ci/daily_build_group_report_results_finder_spec.rb
+++ b/spec/finders/ci/daily_build_group_report_results_finder_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe Ci::DailyBuildGroupReportResultsFinder do
describe '#execute' do
let_it_be(:project) { create(:project, :private) }
let(:user_without_permission) { create(:user) }
- let_it_be(:user_with_permission) { project.owner }
+ let_it_be(:user_with_permission) { project.first_owner }
let_it_be(:ref_path) { 'refs/heads/master' }
let(:limit) { nil }
let_it_be(:default_branch) { false }
diff --git a/spec/finders/merge_request_target_project_finder_spec.rb b/spec/finders/merge_request_target_project_finder_spec.rb
index 08fbfd7229a..bf735152d99 100644
--- a/spec/finders/merge_request_target_project_finder_spec.rb
+++ b/spec/finders/merge_request_target_project_finder_spec.rb
@@ -65,8 +65,8 @@ RSpec.describe MergeRequestTargetProjectFinder do
context 'private projects' do
let(:base_project) { create(:project, :private, path: 'base') }
- let(:forked_project) { fork_project(base_project, base_project.owner) }
- let(:other_fork) { fork_project(base_project, base_project.owner) }
+ let(:forked_project) { fork_project(base_project, base_project.first_owner) }
+ let(:other_fork) { fork_project(base_project, base_project.first_owner) }
context 'when the user is a member of all projects' do
before do
diff --git a/spec/frontend/__helpers__/mock_apollo_helper.js b/spec/frontend/__helpers__/mock_apollo_helper.js
index ee4bbd42b1e..2dff9acbc76 100644
--- a/spec/frontend/__helpers__/mock_apollo_helper.js
+++ b/spec/frontend/__helpers__/mock_apollo_helper.js
@@ -16,7 +16,11 @@ export function createMockClient(handlers = [], resolvers = {}, cacheOptions = {
const mockClient = createMockApolloClient({ cache, resolvers });
if (Array.isArray(handlers)) {
- handlers.forEach(([query, value]) => mockClient.setRequestHandler(query, value));
+ handlers.forEach(([query, value]) =>
+ mockClient.setRequestHandler(query, (...args) =>
+ Promise.resolve(value(...args)).then((r) => ({ ...r })),
+ ),
+ );
} else {
throw new Error('You should pass an array of handlers to mock Apollo client');
}
diff --git a/spec/frontend/admin/users/components/users_table_spec.js b/spec/frontend/admin/users/components/users_table_spec.js
index 9ff5961c7ec..dffe8d6893b 100644
--- a/spec/frontend/admin/users/components/users_table_spec.js
+++ b/spec/frontend/admin/users/components/users_table_spec.js
@@ -3,6 +3,7 @@ import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import AdminUserActions from '~/admin/users/components/user_actions.vue';
@@ -106,8 +107,9 @@ describe('AdminUsersTable component', () => {
});
describe('when the data has been fetched', () => {
- beforeEach(() => {
+ beforeEach(async () => {
initComponent();
+ await waitForPromises();
});
it("renders the user's group count", () => {
@@ -115,8 +117,9 @@ describe('AdminUsersTable component', () => {
});
describe("and a user's group count is null", () => {
- beforeEach(() => {
+ beforeEach(async () => {
initComponent({}, createFetchGroupCount([{ id: user.id, groupCount: null }]));
+ await waitForPromises();
});
it("renders the user's group count as 0", () => {
@@ -126,12 +129,13 @@ describe('AdminUsersTable component', () => {
});
describe('when there is an error while fetching the data', () => {
- beforeEach(() => {
+ beforeEach(async () => {
initComponent({}, fetchGroupCountsError);
+ await waitForPromises();
});
it('creates a flash message and captures the error', () => {
- expect(createFlash).toHaveBeenCalledTimes(1);
+ expect(createFlash).toHaveBeenCalledTimes(2);
expect(createFlash).toHaveBeenCalledWith({
message: 'Could not load user group counts. Please refresh the page to try again.',
captureError: true,
diff --git a/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js b/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js
index e6a6e01c41c..12fc08c303c 100644
--- a/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js
+++ b/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js
@@ -1,7 +1,6 @@
import { GlLoadingIcon, GlAlert } from '@gitlab/ui';
import { mount, createLocalVue } from '@vue/test-utils';
import AxiosMockAdapter from 'axios-mock-adapter';
-import { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import createHttpIntegrationMutation from 'ee_else_ce/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql';
import updateHttpIntegrationMutation from 'ee_else_ce/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql';
@@ -79,12 +78,6 @@ describe('AlertsSettingsWrapper', () => {
.vm.$emit('delete-integration', { id: integrationToDestroy.id });
}
- async function awaitApolloDomMock() {
- await nextTick(); // kick off the DOM update
- await jest.runOnlyPendingTimers(); // kick off the mocked GQL stuff (promises)
- await nextTick(); // kick off the DOM update for flash
- }
-
const createComponent = ({ data = {}, provide = {}, loading = false } = {}) => {
wrapper = extendedWrapper(
mount(AlertsSettingsWrapper, {
@@ -476,9 +469,7 @@ describe('AlertsSettingsWrapper', () => {
describe('with mocked Apollo client', () => {
it('has a selection of integrations loaded via the getIntegrationsQuery', async () => {
createComponentWithApollo();
-
- await jest.runOnlyPendingTimers();
- await nextTick();
+ await waitForPromises();
expect(findIntegrations()).toHaveLength(4);
});
@@ -490,7 +481,7 @@ describe('AlertsSettingsWrapper', () => {
expect(destroyIntegrationHandler).toHaveBeenCalled();
- await nextTick();
+ await waitForPromises();
expect(findIntegrations()).toHaveLength(3);
});
@@ -501,7 +492,7 @@ describe('AlertsSettingsWrapper', () => {
});
await destroyHttpIntegration(wrapper);
- await awaitApolloDomMock();
+ await waitForPromises();
expect(createFlash).toHaveBeenCalledWith({ message: 'Houston, we have a problem' });
});
@@ -512,7 +503,7 @@ describe('AlertsSettingsWrapper', () => {
});
await destroyHttpIntegration(wrapper);
- await awaitApolloDomMock();
+ await waitForPromises();
expect(createFlash).toHaveBeenCalledWith({
message: DELETE_INTEGRATION_ERROR,
diff --git a/spec/frontend/analytics/usage_trends/components/usage_trends_count_chart_spec.js b/spec/frontend/analytics/usage_trends/components/usage_trends_count_chart_spec.js
index 1a331100bb8..f86f2eacf50 100644
--- a/spec/frontend/analytics/usage_trends/components/usage_trends_count_chart_spec.js
+++ b/spec/frontend/analytics/usage_trends/components/usage_trends_count_chart_spec.js
@@ -4,6 +4,7 @@ import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
import UsageTrendsCountChart from '~/analytics/usage_trends/components/usage_trends_count_chart.vue';
import statsQuery from '~/analytics/usage_trends/graphql/queries/usage_count.query.graphql';
import ChartSkeletonLoader from '~/vue_shared/components/resizable_chart/skeleton_loader.vue';
@@ -77,9 +78,10 @@ describe('UsageTrendsCountChart', () => {
});
describe('without data', () => {
- beforeEach(() => {
+ beforeEach(async () => {
queryHandler = mockQueryResponse({ key: queryResponseDataKey, data: [] });
wrapper = createComponent({ responseHandler: queryHandler });
+ await waitForPromises();
});
it('renders an no data message', () => {
@@ -96,9 +98,10 @@ describe('UsageTrendsCountChart', () => {
});
describe('with data', () => {
- beforeEach(() => {
+ beforeEach(async () => {
queryHandler = mockQueryResponse({ key: queryResponseDataKey, data: mockCountsData1 });
wrapper = createComponent({ responseHandler: queryHandler });
+ await waitForPromises();
});
it('requests data', () => {
@@ -134,7 +137,7 @@ describe('UsageTrendsCountChart', () => {
});
wrapper = createComponent({ responseHandler: queryHandler });
- await wrapper.vm.$nextTick();
+ await waitForPromises();
});
it('requests data twice', () => {
diff --git a/spec/frontend/boards/board_list_spec.js b/spec/frontend/boards/board_list_spec.js
index 1981ed5ab7f..e29c19d6e97 100644
--- a/spec/frontend/boards/board_list_spec.js
+++ b/spec/frontend/boards/board_list_spec.js
@@ -1,6 +1,7 @@
import Draggable from 'vuedraggable';
import { DraggableItemTypes } from 'ee_else_ce/boards/constants';
import { useFakeRequestAnimationFrame } from 'helpers/fake_request_animation_frame';
+import waitForPromises from 'helpers/wait_for_promises';
import createComponent from 'jest/boards/board_list_helper';
import BoardCard from '~/boards/components/board_card.vue';
import eventHub from '~/boards/eventhub';
@@ -132,7 +133,6 @@ describe('Board list component', () => {
});
it('shows how many more issues to load', async () => {
- // wrapper.vm.showCount = true;
wrapper = createComponent({
data: {
showCount: true,
@@ -140,6 +140,9 @@ describe('Board list component', () => {
});
await wrapper.vm.$nextTick();
+ await waitForPromises();
+ await wrapper.vm.$nextTick();
+
expect(wrapper.find('.board-list-count').text()).toBe('Showing 1 of 20 issues');
});
});
diff --git a/spec/frontend/clusters/agents/components/activity_events_list_spec.js b/spec/frontend/clusters/agents/components/activity_events_list_spec.js
index 4abbd77dfb7..6b374b6620d 100644
--- a/spec/frontend/clusters/agents/components/activity_events_list_spec.js
+++ b/spec/frontend/clusters/agents/components/activity_events_list_spec.js
@@ -70,8 +70,9 @@ describe('ActivityEvents', () => {
});
describe('when there are no agentEvents', () => {
- beforeEach(() => {
+ beforeEach(async () => {
createWrapper({ queryResponse: jest.fn().mockResolvedValue(mockEmptyResponse) });
+ await waitForPromises();
});
it('displays an empty state with the correct illustration', () => {
@@ -83,9 +84,11 @@ describe('ActivityEvents', () => {
describe('when the agentEvents are present', () => {
const length = mockResponse.data?.project?.clusterAgent?.activityEvents?.nodes?.length;
- beforeEach(() => {
+ beforeEach(async () => {
createWrapper();
+ await waitForPromises();
});
+
it('renders an activity-history-item components for every event', () => {
expect(findAllActivityHistoryItems()).toHaveLength(length);
});
diff --git a/spec/frontend/clusters/agents/components/show_spec.js b/spec/frontend/clusters/agents/components/show_spec.js
index 4b40f9e7223..c92bf634a92 100644
--- a/spec/frontend/clusters/agents/components/show_spec.js
+++ b/spec/frontend/clusters/agents/components/show_spec.js
@@ -82,8 +82,9 @@ describe('ClusterAgentShow', () => {
});
describe('default behaviour', () => {
- beforeEach(() => {
- return createWrapper({ clusterAgent: defaultClusterAgent });
+ beforeEach(async () => {
+ createWrapper({ clusterAgent: defaultClusterAgent });
+ await waitForPromises();
});
it('sends expected params', () => {
@@ -131,8 +132,9 @@ describe('ClusterAgentShow', () => {
createdByUser: null,
};
- beforeEach(() => {
- return createWrapper({ clusterAgent: missingUser });
+ beforeEach(async () => {
+ createWrapper({ clusterAgent: missingUser });
+ await waitForPromises();
});
it('displays agent create information with unknown user', () => {
@@ -146,8 +148,9 @@ describe('ClusterAgentShow', () => {
tokens: null,
};
- beforeEach(() => {
- return createWrapper({ clusterAgent: missingTokens });
+ beforeEach(async () => {
+ createWrapper({ clusterAgent: missingTokens });
+ await waitForPromises();
});
it('displays token header with no count', () => {
@@ -171,8 +174,9 @@ describe('ClusterAgentShow', () => {
},
};
- beforeEach(() => {
- return createWrapper({ clusterAgent: tokenPagination });
+ beforeEach(async () => {
+ createWrapper({ clusterAgent: tokenPagination });
+ await waitForPromises();
});
it('should render pagination buttons', () => {
diff --git a/spec/frontend/clusters_list/components/agent_options_spec.js b/spec/frontend/clusters_list/components/agent_options_spec.js
index 05bab247816..f20a7b39a97 100644
--- a/spec/frontend/clusters_list/components/agent_options_spec.js
+++ b/spec/frontend/clusters_list/components/agent_options_spec.js
@@ -6,6 +6,7 @@ import { ENTER_KEY } from '~/lib/utils/keys';
import getAgentsQuery from '~/clusters_list/graphql/queries/get_agents.query.graphql';
import deleteAgentMutation from '~/clusters_list/graphql/mutations/delete_agent.mutation.graphql';
import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
import AgentOptions from '~/clusters_list/components/agent_options.vue';
import { MAX_LIST_COUNT } from '~/clusters_list/constants';
import { getAgentResponse, mockDeleteResponse, mockErrorDeleteResponse } from '../mocks/apollo';
@@ -83,6 +84,7 @@ describe('AgentOptions', () => {
findDeleteBtn().vm.$emit('click');
findInput().vm.$emit('input', agent.name);
await findModal().vm.$emit('primary');
+ await waitForPromises();
};
beforeEach(() => {
@@ -173,8 +175,7 @@ describe('AgentOptions', () => {
describe('when getting an error deleting agent', () => {
beforeEach(async () => {
await createWrapper({ mutationResponse: mockErrorDeleteResponse });
-
- submitAgentToDelete();
+ await submitAgentToDelete();
});
it('displays the error message', () => {
@@ -187,7 +188,7 @@ describe('AgentOptions', () => {
const loadingResponse = new Promise(() => {});
await createWrapper({ mutationResponse: loadingResponse });
- submitAgentToDelete();
+ await submitAgentToDelete();
});
it('reenables the options dropdown', async () => {
diff --git a/spec/frontend/clusters_list/components/install_agent_modal_spec.js b/spec/frontend/clusters_list/components/install_agent_modal_spec.js
index 4d1429c9e50..5edc504aecb 100644
--- a/spec/frontend/clusters_list/components/install_agent_modal_spec.js
+++ b/spec/frontend/clusters_list/components/install_agent_modal_spec.js
@@ -1,6 +1,7 @@
-import { GlAlert, GlButton, GlFormInputGroup } from '@gitlab/ui';
+import { GlAlert, GlButton, GlFormInputGroup, GlSprintf } from '@gitlab/ui';
import { createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
+import { sprintf } from '~/locale';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { mockTracking } from 'helpers/tracking_helper';
import AvailableAgentsDropdown from '~/clusters_list/components/available_agents_dropdown.vue';
@@ -27,6 +28,7 @@ import {
createAgentTokenResponse,
createAgentTokenErrorResponse,
getAgentResponse,
+ kasDisabledErrorResponse,
} from '../mocks/apollo';
import ModalStub from '../stubs';
@@ -35,7 +37,6 @@ localVue.use(VueApollo);
const projectPath = 'path/to/project';
const kasAddress = 'kas.example.com';
-const kasEnabled = true;
const emptyStateImage = 'path/to/image';
const defaultBranchName = 'default';
const maxAgents = MAX_LIST_COUNT;
@@ -80,7 +81,6 @@ describe('InstallAgentModal', () => {
const provide = {
projectPath,
kasAddress,
- kasEnabled,
emptyStateImage,
};
@@ -92,6 +92,7 @@ describe('InstallAgentModal', () => {
wrapper = shallowMountExtended(InstallAgentModal, {
attachTo: document.body,
stubs: {
+ GlSprintf,
GlModal: ModalStub,
},
localVue,
@@ -118,7 +119,7 @@ describe('InstallAgentModal', () => {
createWrapper();
writeQuery();
- await wrapper.vm.$nextTick();
+ await waitForPromises();
wrapper.vm.setAgentName('agent-name');
findActionButton().vm.$emit('click');
@@ -126,11 +127,12 @@ describe('InstallAgentModal', () => {
return waitForPromises();
};
- beforeEach(() => {
+ beforeEach(async () => {
apolloProvider = createMockApollo([
[getAgentConfigurations, jest.fn().mockResolvedValue(apolloQueryResponse)],
]);
createWrapper();
+ await waitForPromises();
trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
});
@@ -306,4 +308,34 @@ describe('InstallAgentModal', () => {
});
});
});
+
+ describe('when KAS is disabled', () => {
+ const i18n = I18N_AGENT_MODAL.empty_state;
+ beforeEach(() => {
+ apolloProvider = createMockApollo([
+ [getAgentConfigurations, jest.fn().mockResolvedValue(kasDisabledErrorResponse)],
+ ]);
+
+ return mockSelectedAgentResponse();
+ });
+
+ it('renders empty state image', () => {
+ expect(findImage().attributes('src')).toBe(emptyStateImage);
+ });
+
+ it('renders an instruction to enable the KAS', () => {
+ expect(findModal().text()).toContain(
+ sprintf(i18n.enableKasText, { linkStart: '', linkEnd: '' }),
+ );
+ });
+
+ it('renders a cancel button', () => {
+ expect(findActionButton().isVisible()).toBe(true);
+ expect(findActionButton().text()).toBe(i18n.done);
+ });
+
+ it("doesn't render a secondary button", () => {
+ expect(findSecondaryButton().exists()).toBe(false);
+ });
+ });
});
diff --git a/spec/frontend/clusters_list/mocks/apollo.js b/spec/frontend/clusters_list/mocks/apollo.js
index c4a31ed4394..4e863e1c1e9 100644
--- a/spec/frontend/clusters_list/mocks/apollo.js
+++ b/spec/frontend/clusters_list/mocks/apollo.js
@@ -76,6 +76,11 @@ export const getAgentResponse = {
},
};
+export const kasDisabledErrorResponse = {
+ data: {},
+ errors: [{ message: 'Gitlab::Kas::Client::ConfigurationError' }],
+};
+
export const mockDeleteResponse = {
data: { clusterAgentDelete: { errors: [] } },
};
diff --git a/spec/frontend/design_management/pages/index_spec.js b/spec/frontend/design_management/pages/index_spec.js
index dd0f7972553..ce74569f219 100644
--- a/spec/frontend/design_management/pages/index_spec.js
+++ b/spec/frontend/design_management/pages/index_spec.js
@@ -5,6 +5,7 @@ import VueApollo, { ApolloMutation } from 'vue-apollo';
import VueRouter from 'vue-router';
import VueDraggable from 'vuedraggable';
import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import permissionsQuery from 'shared_queries/design_management/design_permissions.query.graphql';
import getDesignListQuery from 'shared_queries/design_management/get_design_list.query.graphql';
@@ -115,8 +116,7 @@ describe('Design management index page', () => {
const findDesignToolbarWrapper = () => wrapper.find('[data-testid="design-toolbar-wrapper"]');
async function moveDesigns(localWrapper) {
- await jest.runOnlyPendingTimers();
- await nextTick();
+ await waitForPromises();
localWrapper.find(VueDraggable).vm.$emit('input', reorderedDesigns);
localWrapper.find(VueDraggable).vm.$emit('change', {
@@ -746,9 +746,7 @@ describe('Design management index page', () => {
describe('with mocked Apollo client', () => {
it('has a design with id 1 as a first one', async () => {
createComponentWithApollo({});
-
- await jest.runOnlyPendingTimers();
- await nextTick();
+ await waitForPromises();
expect(findDesigns()).toHaveLength(3);
expect(findDesigns().at(0).props('id')).toBe('1');
@@ -773,9 +771,7 @@ describe('Design management index page', () => {
expect(draggableAttributes().disabled).toBe(true);
- await jest.runOnlyPendingTimers(); // kick off the mocked GQL stuff (promises)
- await nextTick(); // kick off the DOM update
- await nextTick(); // kick off the DOM update for finally block
+ await waitForPromises();
expect(draggableAttributes().disabled).toBe(false);
});
@@ -787,7 +783,7 @@ describe('Design management index page', () => {
await moveDesigns(wrapper);
- await nextTick();
+ await waitForPromises();
expect(createFlash).toHaveBeenCalledWith({ message: 'Houston, we have a problem' });
});
@@ -799,9 +795,7 @@ describe('Design management index page', () => {
await moveDesigns(wrapper);
- await nextTick(); // kick off the DOM update
- await jest.runOnlyPendingTimers(); // kick off the mocked GQL stuff (promises)
- await nextTick(); // kick off the DOM update for flash
+ await waitForPromises();
expect(createFlash).toHaveBeenCalledWith({
message: 'Something went wrong when reordering designs. Please try again',
diff --git a/spec/frontend/issues/list/components/issues_list_app_spec.js b/spec/frontend/issues/list/components/issues_list_app_spec.js
index 66428ee0492..7730c69641e 100644
--- a/spec/frontend/issues/list/components/issues_list_app_spec.js
+++ b/spec/frontend/issues/list/components/issues_list_app_spec.js
@@ -131,9 +131,10 @@ describe('CE IssuesListApp component', () => {
});
describe('IssuableList', () => {
- beforeEach(() => {
+ beforeEach(async () => {
wrapper = mountComponent();
jest.runOnlyPendingTimers();
+ await waitForPromises();
});
it('renders', () => {
@@ -167,8 +168,9 @@ describe('CE IssuesListApp component', () => {
});
describe('header action buttons', () => {
- it('renders rss button', () => {
+ it('renders rss button', async () => {
wrapper = mountComponent({ mountFn: mount });
+ await waitForPromises();
expect(findGlButtonAt(0).props('icon')).toBe('rss');
expect(findGlButtonAt(0).attributes()).toMatchObject({
@@ -177,8 +179,9 @@ describe('CE IssuesListApp component', () => {
});
});
- it('renders calendar button', () => {
+ it('renders calendar button', async () => {
wrapper = mountComponent({ mountFn: mount });
+ await waitForPromises();
expect(findGlButtonAt(1).props('icon')).toBe('calendar');
expect(findGlButtonAt(1).attributes()).toMatchObject({
@@ -191,12 +194,13 @@ describe('CE IssuesListApp component', () => {
describe('when user is signed in', () => {
const search = '?search=refactor&sort=created_date&state=opened';
- beforeEach(() => {
+ beforeEach(async () => {
setWindowLocation(search);
wrapper = mountComponent({ provide: { isSignedIn: true }, mountFn: mount });
jest.runOnlyPendingTimers();
+ await waitForPromises();
});
it('renders', () => {
@@ -585,11 +589,12 @@ describe('CE IssuesListApp component', () => {
${'fetching issues'} | ${'issuesQueryResponse'} | ${IssuesListApp.i18n.errorFetchingIssues}
${'fetching issue counts'} | ${'issuesCountsQueryResponse'} | ${IssuesListApp.i18n.errorFetchingCounts}
`('when there is an error $error', ({ mountOption, message }) => {
- beforeEach(() => {
+ beforeEach(async () => {
wrapper = mountComponent({
[mountOption]: jest.fn().mockRejectedValue(new Error('ERROR')),
});
jest.runOnlyPendingTimers();
+ await waitForPromises();
});
it('shows an error message', () => {
diff --git a/spec/frontend/issues/list/components/new_issue_dropdown_spec.js b/spec/frontend/issues/list/components/new_issue_dropdown_spec.js
index 0c52e66ff14..7bec3d13d44 100644
--- a/spec/frontend/issues/list/components/new_issue_dropdown_spec.js
+++ b/spec/frontend/issues/list/components/new_issue_dropdown_spec.js
@@ -2,9 +2,11 @@ import { GlDropdown, GlDropdownItem, GlSearchBoxByType } from '@gitlab/ui';
import { createLocalVue, mount, shallowMount } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
import NewIssueDropdown from '~/issues/list/components/new_issue_dropdown.vue';
import searchProjectsQuery from '~/issues/list/queries/search_projects.query.graphql';
import { DASH_SCOPE, joinPaths } from '~/lib/utils/url_utility';
+import { DEBOUNCE_DELAY } from '~/vue_shared/components/filtered_search_bar/constants';
import {
emptySearchProjectsQueryResponse,
project1,
@@ -42,8 +44,9 @@ describe('NewIssueDropdown component', () => {
const findInput = () => wrapper.findComponent(GlSearchBoxByType);
const showDropdown = async () => {
findDropdown().vm.$emit('shown');
- await wrapper.vm.$apollo.queries.projects.refetch();
- jest.runOnlyPendingTimers();
+ await waitForPromises();
+ jest.advanceTimersByTime(DEBOUNCE_DELAY);
+ await waitForPromises();
};
afterEach(() => {
@@ -74,7 +77,6 @@ describe('NewIssueDropdown component', () => {
it('renders projects with issues enabled', async () => {
wrapper = mountComponent({ mountFn: mount });
-
await showDropdown();
const listItems = wrapper.findAll('li');
@@ -112,10 +114,11 @@ describe('NewIssueDropdown component', () => {
describe('when a project is selected', () => {
beforeEach(async () => {
wrapper = mountComponent({ mountFn: mount });
-
+ await waitForPromises();
await showDropdown();
wrapper.findComponent(GlDropdownItem).vm.$emit('click', project1);
+ await waitForPromises();
});
it('dropdown button is a link', () => {
diff --git a/spec/frontend/jobs/bridge/app_spec.js b/spec/frontend/jobs/bridge/app_spec.js
index c0faab90552..cd7c2fd5322 100644
--- a/spec/frontend/jobs/bridge/app_spec.js
+++ b/spec/frontend/jobs/bridge/app_spec.js
@@ -81,10 +81,10 @@ describe('Bridge Show Page', () => {
});
describe('after pipeline query is loaded', () => {
- beforeEach(() => {
+ beforeEach(async () => {
mockPipelineQuery.mockResolvedValue(mockPipelineQueryResponse);
createComponentWithApollo();
- waitForPromises();
+ await waitForPromises();
});
it('query is called with correct variables', async () => {
@@ -109,10 +109,10 @@ describe('Bridge Show Page', () => {
});
describe('sidebar expansion', () => {
- beforeEach(() => {
+ beforeEach(async () => {
mockPipelineQuery.mockResolvedValue(mockPipelineQueryResponse);
createComponentWithApollo();
- waitForPromises();
+ await waitForPromises();
});
describe('on resize', () => {
diff --git a/spec/frontend/jobs/components/table/job_table_app_spec.js b/spec/frontend/jobs/components/table/job_table_app_spec.js
index 05988eecb10..389bcf670c9 100644
--- a/spec/frontend/jobs/components/table/job_table_app_spec.js
+++ b/spec/frontend/jobs/components/table/job_table_app_spec.js
@@ -112,7 +112,7 @@ describe('Job table app', () => {
},
});
- await wrapper.vm.$nextTick();
+ await waitForPromises();
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// eslint-disable-next-line no-restricted-syntax
@@ -150,7 +150,7 @@ describe('Job table app', () => {
},
});
- await wrapper.vm.$nextTick();
+ await waitForPromises();
expect(findPrevious().exists()).toBe(true);
expect(findPrevious().classes('disabled')).toBe(true);
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_spec.js
index 917d0fccf3f..7d9f89518d5 100644
--- a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_spec.js
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_spec.js
@@ -108,6 +108,7 @@ describe('Tags List', () => {
describe('events', () => {
it('prev-page fetch the previous page', async () => {
findRegistryList().vm.$emit('prev-page');
+ await waitForPromises();
expect(resolver).toHaveBeenCalledWith({
first: null,
@@ -119,8 +120,9 @@ describe('Tags List', () => {
});
});
- it('next-page fetch the previous page', () => {
+ it('next-page fetch the previous page', async () => {
findRegistryList().vm.$emit('next-page');
+ await waitForPromises();
expect(resolver).toHaveBeenCalledWith({
after: tagsPageInfo.endCursor,
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js
index 9e9d61a1241..1f8cda55f39 100644
--- a/spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js
@@ -522,7 +522,7 @@ describe('Details Page', () => {
findDeleteImage().vm.$emit('start');
- await nextTick();
+ await waitForPromises();
expect(findTagsLoader().exists()).toBe(true);
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/pages/list_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/pages/list_spec.js
index ec00e7f9319..c4984d1b6ae 100644
--- a/spec/frontend/packages_and_registries/container_registry/explorer/pages/list_spec.js
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/pages/list_spec.js
@@ -361,7 +361,7 @@ describe('List Page', () => {
findRegistrySearch().vm.$emit('filter:submit');
- await nextTick();
+ await waitForPromises();
};
it('has a search box element', async () => {
@@ -429,7 +429,7 @@ describe('List Page', () => {
await waitForApolloRequestRender();
findImageList().vm.$emit('prev-page');
- await nextTick();
+ await waitForPromises();
expect(resolver).toHaveBeenCalledWith(
expect.objectContaining({ before: pageInfo.startCursor }),
@@ -449,7 +449,7 @@ describe('List Page', () => {
await waitForApolloRequestRender();
findImageList().vm.$emit('next-page');
- await nextTick();
+ await waitForPromises();
expect(resolver).toHaveBeenCalledWith(
expect.objectContaining({ after: pageInfo.endCursor }),
@@ -471,8 +471,9 @@ describe('List Page', () => {
});
it('contains a description with the path of the item to delete', async () => {
+ await waitForPromises();
findImageList().vm.$emit('delete', { path: 'foo' });
- await nextTick();
+ await waitForPromises();
expect(findDeleteModal().html()).toContain('foo');
});
});
diff --git a/spec/frontend/packages_and_registries/dependency_proxy/app_spec.js b/spec/frontend/packages_and_registries/dependency_proxy/app_spec.js
index 44a7186904d..842b63ac0d5 100644
--- a/spec/frontend/packages_and_registries/dependency_proxy/app_spec.js
+++ b/spec/frontend/packages_and_registries/dependency_proxy/app_spec.js
@@ -195,8 +195,9 @@ describe('DependencyProxyApp', () => {
});
});
- it('prev-page event on list fetches the previous page', () => {
+ it('prev-page event on list fetches the previous page', async () => {
findManifestList().vm.$emit('prev-page');
+ await waitForPromises();
expect(resolver).toHaveBeenCalledWith({
before: pagination().startCursor,
@@ -206,8 +207,9 @@ describe('DependencyProxyApp', () => {
});
});
- it('next-page event on list fetches the next page', () => {
+ it('next-page event on list fetches the next page', async () => {
findManifestList().vm.$emit('next-page');
+ await waitForPromises();
expect(resolver).toHaveBeenCalledWith({
after: pagination().endCursor,
diff --git a/spec/frontend/packages_and_registries/package_registry/pages/list_spec.js b/spec/frontend/packages_and_registries/package_registry/pages/list_spec.js
index 2ac2a6455ef..8121d9c7dbd 100644
--- a/spec/frontend/packages_and_registries/package_registry/pages/list_spec.js
+++ b/spec/frontend/packages_and_registries/package_registry/pages/list_spec.js
@@ -85,7 +85,7 @@ describe('PackagesListApp', () => {
wrapper.destroy();
});
- const waitForFirstRequest = () => {
+ const waitForFirstRequest = async () => {
// emit a search update so the query is executed
findSearch().vm.$emit('update', { sort: 'NAME_DESC', filters: [] });
return waitForPromises();
@@ -149,11 +149,10 @@ describe('PackagesListApp', () => {
beforeEach(() => {
resolver = jest.fn().mockResolvedValue(packagesListQuery());
mountComponent({ resolver });
-
- return waitForFirstRequest();
});
- it('exists and has the right props', () => {
+ it('exists and has the right props', async () => {
+ await waitForFirstRequest();
expect(findListComponent().props()).toMatchObject({
list: expect.arrayContaining([expect.objectContaining({ id: packageData().id })]),
isLoading: false,
@@ -161,16 +160,20 @@ describe('PackagesListApp', () => {
});
});
- it('when list emits next-page fetches the next set of records', () => {
+ it('when list emits next-page fetches the next set of records', async () => {
+ await waitForFirstRequest();
findListComponent().vm.$emit('next-page');
+ await waitForPromises();
expect(resolver).toHaveBeenCalledWith(
expect.objectContaining({ after: pagination().endCursor, first: GRAPHQL_PAGE_SIZE }),
);
});
- it('when list emits prev-page fetches the prev set of records', () => {
+ it('when list emits prev-page fetches the prev set of records', async () => {
+ await waitForFirstRequest();
findListComponent().vm.$emit('prev-page');
+ await waitForPromises();
expect(resolver).toHaveBeenCalledWith(
expect.objectContaining({ before: pagination().startCursor, last: GRAPHQL_PAGE_SIZE }),
diff --git a/spec/frontend/packages_and_registries/settings/project/settings/components/registry_settings_app_spec.js b/spec/frontend/packages_and_registries/settings/project/settings/components/registry_settings_app_spec.js
index 8266f9bee89..cad5d4db033 100644
--- a/spec/frontend/packages_and_registries/settings/project/settings/components/registry_settings_app_spec.js
+++ b/spec/frontend/packages_and_registries/settings/project/settings/components/registry_settings_app_spec.js
@@ -2,6 +2,7 @@ import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
import component from '~/packages_and_registries/settings/project/components/registry_settings_app.vue';
import SettingsForm from '~/packages_and_registries/settings/project/components/settings_form.vue';
import {
@@ -64,8 +65,6 @@ describe('Registry Settings App', () => {
localVue,
apolloProvider: fakeApollo,
});
-
- return requestHandlers.map((request) => request[1]);
};
afterEach(() => {
@@ -101,25 +100,25 @@ describe('Registry Settings App', () => {
${'response and changes'} | ${expirationPolicyPayload()} | ${{ ...containerExpirationPolicyData(), nameRegex: '12345' }} | ${true}
${'response and empty'} | ${expirationPolicyPayload()} | ${{}} | ${true}
`('$description', async ({ apiResponse, workingCopy, result }) => {
- const requests = mountComponentWithApollo({
+ mountComponentWithApollo({
provide: { ...defaultProvidedValues, enableHistoricEntries: true },
resolver: jest.fn().mockResolvedValue(apiResponse),
});
- await Promise.all(requests);
+ await waitForPromises();
findSettingsComponent().vm.$emit('input', workingCopy);
- await wrapper.vm.$nextTick();
+ await waitForPromises();
expect(findSettingsComponent().props('isEdited')).toBe(result);
});
});
it('renders the setting form', async () => {
- const requests = mountComponentWithApollo({
+ mountComponentWithApollo({
resolver: jest.fn().mockResolvedValue(expirationPolicyPayload()),
});
- await Promise.all(requests);
+ await waitForPromises();
expect(findSettingsComponent().exists()).toBe(true);
});
@@ -153,11 +152,11 @@ describe('Registry Settings App', () => {
});
describe('fetchSettingsError', () => {
- beforeEach(() => {
- const requests = mountComponentWithApollo({
+ beforeEach(async () => {
+ mountComponentWithApollo({
resolver: jest.fn().mockRejectedValue(new Error('GraphQL error')),
});
- return Promise.all(requests);
+ await waitForPromises();
});
it('the form is hidden', () => {
@@ -175,14 +174,14 @@ describe('Registry Settings App', () => {
${true} | ${true}
${false} | ${false}
`('is $isShown that the form is shown', async ({ enableHistoricEntries, isShown }) => {
- const requests = mountComponentWithApollo({
+ mountComponentWithApollo({
provide: {
...defaultProvidedValues,
enableHistoricEntries,
},
resolver: jest.fn().mockResolvedValue(emptyExpirationPolicyPayload()),
});
- await Promise.all(requests);
+ await waitForPromises();
expect(findSettingsComponent().exists()).toBe(isShown);
});
diff --git a/spec/frontend/pipeline_editor/components/header/pipeline_status_spec.js b/spec/frontend/pipeline_editor/components/header/pipeline_status_spec.js
index c101b1d21c7..d813d67b94b 100644
--- a/spec/frontend/pipeline_editor/components/header/pipeline_status_spec.js
+++ b/spec/frontend/pipeline_editor/components/header/pipeline_status_spec.js
@@ -70,13 +70,13 @@ describe('Pipeline Status', () => {
describe('when querying data', () => {
describe('when data is set', () => {
- beforeEach(() => {
+ beforeEach(async () => {
mockPipelineQuery.mockResolvedValue({
data: { project: mockProjectPipeline() },
});
createComponentWithApollo();
- waitForPromises();
+ await waitForPromises();
});
it('query is called with correct variables', async () => {
diff --git a/spec/frontend/pipeline_editor/components/header/pipline_editor_mini_graph_spec.js b/spec/frontend/pipeline_editor/components/header/pipline_editor_mini_graph_spec.js
index 6b9f576917f..2f26b4d145c 100644
--- a/spec/frontend/pipeline_editor/components/header/pipline_editor_mini_graph_spec.js
+++ b/spec/frontend/pipeline_editor/components/header/pipline_editor_mini_graph_spec.js
@@ -1,6 +1,7 @@
import { shallowMount, createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
import PipelineEditorMiniGraph from '~/pipeline_editor/components/header/pipeline_editor_mini_graph.vue';
import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue';
import getLinkedPipelinesQuery from '~/projects/commit_box/info/graphql/queries/get_linked_pipelines.query.graphql';
@@ -89,13 +90,14 @@ describe('Pipeline Status', () => {
});
describe('when query fails', () => {
- beforeEach(() => {
+ beforeEach(async () => {
mockLinkedPipelinesQuery.mockRejectedValue(new Error());
createComponentWithApollo();
+ await waitForPromises();
});
it('should emit an error event when query fails', async () => {
- expect(wrapper.emitted('showError')).toHaveLength(1);
+ expect(wrapper.emitted('showError')).toHaveLength(2);
expect(wrapper.emitted('showError')[0]).toEqual([
{
type: PIPELINE_FAILURE,
diff --git a/spec/frontend/pipelines/components/jobs/jobs_app_spec.js b/spec/frontend/pipelines/components/jobs/jobs_app_spec.js
index 1ea6096c922..547c0cdda5e 100644
--- a/spec/frontend/pipelines/components/jobs/jobs_app_spec.js
+++ b/spec/frontend/pipelines/components/jobs/jobs_app_spec.js
@@ -74,16 +74,16 @@ describe('Jobs app', () => {
await waitForPromises();
expect(createFlash).toHaveBeenCalledWith({
- message: 'An error occured while fetching the pipelines jobs.',
+ message: 'An error occurred while fetching the pipelines jobs.',
});
});
it('handles infinite scrolling by calling fetchMore', async () => {
createComponent(resolverSpy);
-
await waitForPromises();
triggerInfiniteScroll();
+ await waitForPromises();
expect(resolverSpy).toHaveBeenCalledWith({
after: 'eyJpZCI6Ijg0NyJ9',
@@ -96,10 +96,10 @@ describe('Jobs app', () => {
createComponent(resolverSpy);
expect(findSkeletonLoader().exists()).toBe(true);
-
await waitForPromises();
triggerInfiniteScroll();
+ await waitForPromises();
expect(findSkeletonLoader().exists()).toBe(false);
});
diff --git a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
index 04e004dc6c1..8bc6c086b9d 100644
--- a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
+++ b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
@@ -1,10 +1,11 @@
import { GlAlert, GlButton, GlButtonGroup, GlLoadingIcon } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
-import Vue, { nextTick } from 'vue';
+import Vue from 'vue';
import VueApollo from 'vue-apollo';
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
import getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql';
import getUserCallouts from '~/graphql_shared/queries/get_user_callouts.query.graphql';
import axios from '~/lib/utils/axios_utils';
@@ -100,15 +101,6 @@ describe('Pipeline graph wrapper', () => {
wrapper.destroy();
});
- beforeAll(() => {
- jest.useFakeTimers();
- });
-
- afterAll(() => {
- jest.runOnlyPendingTimers();
- jest.useRealTimers();
- });
-
describe('when data is loading', () => {
it('displays the loading icon', () => {
createComponentWithApollo();
@@ -134,8 +126,7 @@ describe('Pipeline graph wrapper', () => {
describe('when data has loaded', () => {
beforeEach(async () => {
createComponentWithApollo();
- jest.runOnlyPendingTimers();
- await nextTick();
+ await waitForPromises();
});
it('does not display the loading icon', () => {
@@ -163,8 +154,7 @@ describe('Pipeline graph wrapper', () => {
createComponentWithApollo({
getPipelineDetailsHandler: jest.fn().mockRejectedValue(new Error('GraphQL error')),
});
- jest.runOnlyPendingTimers();
- await nextTick();
+ await waitForPromises();
});
it('does not display the loading icon', () => {
@@ -187,8 +177,7 @@ describe('Pipeline graph wrapper', () => {
pipelineIid: '',
},
});
- jest.runOnlyPendingTimers();
- await nextTick();
+ await waitForPromises();
});
it('does not display the loading icon', () => {
@@ -210,7 +199,7 @@ describe('Pipeline graph wrapper', () => {
createComponentWithApollo();
jest.spyOn(wrapper.vm.$apollo.queries.headerPipeline, 'refetch');
jest.spyOn(wrapper.vm.$apollo.queries.pipeline, 'refetch');
- await nextTick();
+ await waitForPromises();
getGraph().vm.$emit('refreshPipelineGraph');
});
@@ -224,8 +213,7 @@ describe('Pipeline graph wrapper', () => {
describe('when query times out', () => {
const advanceApolloTimers = async () => {
jest.runOnlyPendingTimers();
- await nextTick();
- await nextTick();
+ await waitForPromises();
};
beforeEach(async () => {
@@ -245,7 +233,7 @@ describe('Pipeline graph wrapper', () => {
.mockResolvedValueOnce(errorData);
createComponentWithApollo({ getPipelineDetailsHandler: failSucceedFail });
- await nextTick();
+ await waitForPromises();
});
it('shows correct errors and does not overwrite populated data when data is empty', async () => {
@@ -274,8 +262,7 @@ describe('Pipeline graph wrapper', () => {
mountFn: mount,
});
- jest.runOnlyPendingTimers();
- await nextTick();
+ await waitForPromises();
});
it('appears when pipeline uses needs', () => {
@@ -318,7 +305,7 @@ describe('Pipeline graph wrapper', () => {
});
jest.runOnlyPendingTimers();
- await nextTick();
+ await waitForPromises();
});
it('sets showLinks to true', async () => {
@@ -327,8 +314,9 @@ describe('Pipeline graph wrapper', () => {
expect(getLinksLayer().props('showLinks')).toBe(false);
expect(getViewSelector().props('type')).toBe(LAYER_VIEW);
await getDependenciesToggle().vm.$emit('change', true);
+
jest.runOnlyPendingTimers();
- await nextTick();
+ await waitForPromises();
expect(wrapper.findComponent(LinksLayer).props('showLinks')).toBe(true);
});
});
@@ -343,8 +331,7 @@ describe('Pipeline graph wrapper', () => {
mountFn: mount,
});
- jest.runOnlyPendingTimers();
- await nextTick();
+ await waitForPromises();
});
it('shows the hover tip in the view selector', async () => {
@@ -365,7 +352,7 @@ describe('Pipeline graph wrapper', () => {
});
jest.runOnlyPendingTimers();
- await nextTick();
+ await waitForPromises();
});
it('does not show the hover tip', async () => {
@@ -382,8 +369,7 @@ describe('Pipeline graph wrapper', () => {
mountFn: mount,
});
- jest.runOnlyPendingTimers();
- await nextTick();
+ await waitForPromises();
});
afterEach(() => {
@@ -411,8 +397,7 @@ describe('Pipeline graph wrapper', () => {
getPipelineDetailsHandler: jest.fn().mockResolvedValue(nonNeedsResponse),
});
- jest.runOnlyPendingTimers();
- await nextTick();
+ await waitForPromises();
});
afterEach(() => {
@@ -435,7 +420,7 @@ describe('Pipeline graph wrapper', () => {
});
jest.runOnlyPendingTimers();
- await nextTick();
+ await waitForPromises();
});
it('does not appear when pipeline does not use needs', () => {
@@ -461,8 +446,7 @@ describe('Pipeline graph wrapper', () => {
describe('with no metrics path', () => {
beforeEach(async () => {
createComponentWithApollo();
- jest.runOnlyPendingTimers();
- await nextTick();
+ await waitForPromises();
});
it('is not called', () => {
@@ -505,8 +489,7 @@ describe('Pipeline graph wrapper', () => {
},
});
- jest.runOnlyPendingTimers();
- await nextTick();
+ await waitForPromises();
});
it('attempts to collect metrics', () => {
@@ -517,7 +500,7 @@ describe('Pipeline graph wrapper', () => {
});
describe('with duration and no error', () => {
- beforeEach(() => {
+ beforeEach(async () => {
mock = new MockAdapter(axios);
mock.onPost(metricsPath).reply(200, {});
@@ -536,6 +519,7 @@ describe('Pipeline graph wrapper', () => {
currentViewType: LAYER_VIEW,
},
});
+ await waitForPromises();
});
afterEach(() => {
diff --git a/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js b/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
index 2f03b846525..5897da4b8c2 100644
--- a/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
+++ b/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
@@ -1,6 +1,7 @@
import { mount, shallowMount, createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
import getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql';
import {
DOWNSTREAM,
@@ -87,13 +88,12 @@ describe('Linked Pipelines Column', () => {
describe('click action', () => {
const clickExpandButton = async () => {
await findExpandButton().trigger('click');
- await wrapper.vm.$nextTick();
+ await waitForPromises();
};
const clickExpandButtonAndAwaitTimers = async () => {
await clickExpandButton();
jest.runOnlyPendingTimers();
- await wrapper.vm.$nextTick();
};
describe('layer type rendering', () => {
@@ -162,7 +162,10 @@ describe('Linked Pipelines Column', () => {
it('emits the error', async () => {
await clickExpandButton();
- expect(wrapper.emitted().error).toEqual([[{ type: LOAD_FAILURE, skipSentry: true }]]);
+ expect(wrapper.emitted().error).toEqual([
+ [{ type: LOAD_FAILURE, skipSentry: true }],
+ [{ type: LOAD_FAILURE, skipSentry: true }],
+ ]);
});
it('does not show the pipeline', async () => {
@@ -213,7 +216,10 @@ describe('Linked Pipelines Column', () => {
it('emits the error', async () => {
await clickExpandButton();
- expect(wrapper.emitted().error).toEqual([[{ type: LOAD_FAILURE, skipSentry: true }]]);
+ expect(wrapper.emitted().error).toEqual([
+ [{ type: LOAD_FAILURE, skipSentry: true }],
+ [{ type: LOAD_FAILURE, skipSentry: true }],
+ ]);
});
it('does not show the pipeline', async () => {
diff --git a/spec/frontend/pipelines/notification/deprecated_type_keyword_notification_spec.js b/spec/frontend/pipelines/notification/deprecated_type_keyword_notification_spec.js
index 10ee55818ac..f626652a944 100644
--- a/spec/frontend/pipelines/notification/deprecated_type_keyword_notification_spec.js
+++ b/spec/frontend/pipelines/notification/deprecated_type_keyword_notification_spec.js
@@ -2,6 +2,7 @@ import VueApollo from 'vue-apollo';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { GlAlert, GlSprintf } from '@gitlab/ui';
import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
import DeprecatedTypeKeywordNotification from '~/pipelines/components/notification/deprecated_type_keyword_notification.vue';
import getPipelineWarnings from '~/pipelines/graphql/queries/get_pipeline_warnings.query.graphql';
import {
@@ -77,9 +78,10 @@ describe('Deprecated keyword notification', () => {
});
describe('if there is an error in the query', () => {
- beforeEach(() => {
+ beforeEach(async () => {
mockWarnings.mockResolvedValue({ errors: ['It didnt work'] });
wrapper = createComponentWithApollo();
+ await waitForPromises();
});
it('does not display the notification', () => {
@@ -89,9 +91,10 @@ describe('Deprecated keyword notification', () => {
describe('with a valid query result', () => {
describe('if there are no deprecation warnings', () => {
- beforeEach(() => {
+ beforeEach(async () => {
mockWarnings.mockResolvedValue(mockWarningsWithoutDeprecation);
wrapper = createComponentWithApollo();
+ await waitForPromises();
});
it('does not show the notification', () => {
expect(findAlert().exists()).toBe(false);
@@ -99,9 +102,10 @@ describe('Deprecated keyword notification', () => {
});
describe('with a root type deprecation message', () => {
- beforeEach(() => {
+ beforeEach(async () => {
mockWarnings.mockResolvedValue(mockWarningsRootType);
wrapper = createComponentWithApollo();
+ await waitForPromises();
});
it('shows the notification with one item', () => {
expect(findAlert().exists()).toBe(true);
@@ -111,9 +115,10 @@ describe('Deprecated keyword notification', () => {
});
describe('with a job type deprecation message', () => {
- beforeEach(() => {
+ beforeEach(async () => {
mockWarnings.mockResolvedValue(mockWarningsType);
wrapper = createComponentWithApollo();
+ await waitForPromises();
});
it('shows the notification with one item', () => {
expect(findAlert().exists()).toBe(true);
@@ -124,9 +129,10 @@ describe('Deprecated keyword notification', () => {
});
describe('with both the root types and job type deprecation message', () => {
- beforeEach(() => {
+ beforeEach(async () => {
mockWarnings.mockResolvedValue(mockWarningsTypesAll);
wrapper = createComponentWithApollo();
+ await waitForPromises();
});
it('shows the notification with two items', () => {
expect(findAlert().exists()).toBe(true);
diff --git a/spec/frontend/projects/new/components/new_project_url_select_spec.js b/spec/frontend/projects/new/components/new_project_url_select_spec.js
index 258fa7636d4..be6592530fc 100644
--- a/spec/frontend/projects/new/components/new_project_url_select_spec.js
+++ b/spec/frontend/projects/new/components/new_project_url_select_spec.js
@@ -9,6 +9,7 @@ import { mount, shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import eventHub from '~/projects/new/event_hub';
@@ -101,6 +102,7 @@ describe('NewProjectUrlSelect component', () => {
findDropdown().vm.$emit('shown');
await wrapper.vm.$apollo.queries.currentUser.refetch();
jest.runOnlyPendingTimers();
+ await waitForPromises();
};
afterEach(() => {
@@ -235,8 +237,7 @@ describe('NewProjectUrlSelect component', () => {
};
wrapper = mountComponent({ search: 'no matches', queryResponse, mountFn: mount });
- jest.runOnlyPendingTimers();
- await wrapper.vm.$nextTick();
+ await waitForPromises();
expect(wrapper.find('li').text()).toBe('No matches found');
});
diff --git a/spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js b/spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js
index 6ef49390c47..2ee2ffdb96b 100644
--- a/spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js
+++ b/spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js
@@ -2,6 +2,7 @@ import { GlColumnChart } from '@gitlab/ui/dist/charts';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
import PipelineCharts from '~/projects/pipelines/charts/components/pipeline_charts.vue';
import StatisticsList from '~/projects/pipelines/charts/components/statistics_list.vue';
import getPipelineCountByStatus from '~/projects/pipelines/charts/graphql/queries/get_pipeline_count_by_status.query.graphql';
@@ -25,7 +26,7 @@ describe('~/projects/pipelines/charts/components/pipeline_charts.vue', () => {
return createMockApollo(requestHandlers);
}
- beforeEach(() => {
+ beforeEach(async () => {
wrapper = shallowMount(PipelineCharts, {
provide: {
projectPath,
@@ -33,6 +34,8 @@ describe('~/projects/pipelines/charts/components/pipeline_charts.vue', () => {
localVue,
apolloProvider: createMockApolloProvider(),
});
+
+ await waitForPromises();
});
afterEach(() => {
diff --git a/spec/frontend/releases/components/app_index_apollo_client_spec.js b/spec/frontend/releases/components/app_index_apollo_client_spec.js
index 32bbfd386f5..53ee5b223ad 100644
--- a/spec/frontend/releases/components/app_index_apollo_client_spec.js
+++ b/spec/frontend/releases/components/app_index_apollo_client_spec.js
@@ -4,6 +4,7 @@ import VueApollo from 'vue-apollo';
import originalAllReleasesQueryResponse from 'test_fixtures/graphql/releases/graphql/queries/all_releases.query.graphql.json';
import createMockApollo from 'helpers/mock_apollo_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import waitForPromises from 'helpers/wait_for_promises';
import allReleasesQuery from 'shared_queries/releases/all_releases.query.graphql';
import createFlash from '~/flash';
import { historyPushState } from '~/lib/utils/common_utils';
@@ -141,7 +142,8 @@ describe('app_index_apollo_client.vue', () => {
});
});
- it(`${toDescription(loadingIndicator)} render a loading indicator`, () => {
+ it(`${toDescription(loadingIndicator)} render a loading indicator`, async () => {
+ await waitForPromises();
expect(findLoadingIndicator().exists()).toBe(loadingIndicator);
});
diff --git a/spec/frontend/releases/components/app_show_spec.js b/spec/frontend/releases/components/app_show_spec.js
index a60b9bda66a..41c9746a363 100644
--- a/spec/frontend/releases/components/app_show_spec.js
+++ b/spec/frontend/releases/components/app_show_spec.js
@@ -3,6 +3,7 @@ import Vue from 'vue';
import VueApollo from 'vue-apollo';
import oneReleaseQueryResponse from 'test_fixtures/graphql/releases/graphql/queries/one_release.query.graphql.json';
import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash';
import ReleaseShowApp from '~/releases/components/app_show.vue';
import ReleaseBlock from '~/releases/components/release_block.vue';
@@ -111,12 +112,13 @@ describe('Release show component', () => {
});
describe('when the component has successfully loaded the release', () => {
- beforeEach(() => {
+ beforeEach(async () => {
const apolloProvider = createMockApollo([
[oneReleaseQuery, jest.fn().mockResolvedValueOnce(oneReleaseQueryResponse)],
]);
createComponent({ apolloProvider });
+ await waitForPromises();
});
expectNoLoadingIndicator();
@@ -125,12 +127,13 @@ describe('Release show component', () => {
});
describe('when the request succeeded, but the returned "project" key was null', () => {
- beforeEach(() => {
+ beforeEach(async () => {
const apolloProvider = createMockApollo([
[oneReleaseQuery, jest.fn().mockResolvedValueOnce({ data: { project: null } })],
]);
createComponent({ apolloProvider });
+ await waitForPromises();
});
expectNoLoadingIndicator();
@@ -139,7 +142,7 @@ describe('Release show component', () => {
});
describe('when the request succeeded, but the returned "project.release" key was null', () => {
- beforeEach(() => {
+ beforeEach(async () => {
const apolloProvider = createMockApollo([
[
oneReleaseQuery,
@@ -148,6 +151,7 @@ describe('Release show component', () => {
]);
createComponent({ apolloProvider });
+ await waitForPromises();
});
expectNoLoadingIndicator();
@@ -156,12 +160,13 @@ describe('Release show component', () => {
});
describe('when an error occurs while loading the release', () => {
- beforeEach(() => {
+ beforeEach(async () => {
const apolloProvider = createMockApollo([
[oneReleaseQuery, jest.fn().mockRejectedValueOnce('An error occurred!')],
]);
createComponent({ apolloProvider });
+ await waitForPromises();
});
expectNoLoadingIndicator();
diff --git a/spec/frontend/repository/components/blob_content_viewer_spec.js b/spec/frontend/repository/components/blob_content_viewer_spec.js
index d3b60ec3768..3384fd599b4 100644
--- a/spec/frontend/repository/components/blob_content_viewer_spec.js
+++ b/spec/frontend/repository/components/blob_content_viewer_spec.js
@@ -209,7 +209,7 @@ describe('Blob content viewer component', () => {
it('does not render a BlobContent component if a Blob viewer is available', async () => {
loadViewer.mockReturnValue(() => true);
await createComponent({ blob: richViewerMock });
-
+ await waitForPromises();
expect(findBlobContent().exists()).toBe(false);
});
@@ -235,7 +235,7 @@ describe('Blob content viewer component', () => {
},
});
- await nextTick();
+ await waitForPromises();
expect(loadViewer).toHaveBeenCalledWith(viewer);
expect(wrapper.findComponent(loadViewerReturnValue).exists()).toBe(true);
diff --git a/spec/frontend/runner/admin_runners/admin_runners_app_spec.js b/spec/frontend/runner/admin_runners/admin_runners_app_spec.js
index ac7ecf32938..35df27e02ea 100644
--- a/spec/frontend/runner/admin_runners/admin_runners_app_spec.js
+++ b/spec/frontend/runner/admin_runners/admin_runners_app_spec.js
@@ -281,6 +281,7 @@ describe('AdminRunnersApp', () => {
},
});
createComponent();
+ await waitForPromises();
});
it('shows a message for no results', async () => {
@@ -289,9 +290,10 @@ describe('AdminRunnersApp', () => {
});
describe('when runners query fails', () => {
- beforeEach(() => {
+ beforeEach(async () => {
mockRunnersQuery = jest.fn().mockRejectedValue(new Error('Error!'));
createComponent();
+ await waitForPromises();
});
it('error is shown to the user', async () => {
diff --git a/spec/frontend/runner/components/cells/runner_actions_cell_spec.js b/spec/frontend/runner/components/cells/runner_actions_cell_spec.js
index 89961fdb162..69838ab95fa 100644
--- a/spec/frontend/runner/components/cells/runner_actions_cell_spec.js
+++ b/spec/frontend/runner/components/cells/runner_actions_cell_spec.js
@@ -186,7 +186,8 @@ describe('RunnerTypeCell', () => {
beforeEach(async () => {
runnerActionsUpdateMutationHandler.mockRejectedValueOnce(new Error(mockErrorMsg));
- await findToggleActiveBtn().vm.$emit('click');
+ findToggleActiveBtn().vm.$emit('click');
+ await waitForPromises();
});
it('error is reported to sentry', () => {
@@ -215,7 +216,8 @@ describe('RunnerTypeCell', () => {
},
});
- await findToggleActiveBtn().vm.$emit('click');
+ findToggleActiveBtn().vm.$emit('click');
+ await waitForPromises();
});
it('error is reported to sentry', () => {
@@ -305,8 +307,9 @@ describe('RunnerTypeCell', () => {
});
describe('When delete is clicked', () => {
- beforeEach(() => {
+ beforeEach(async () => {
findRunnerDeleteModal().vm.$emit('primary');
+ await waitForPromises();
});
it('The delete mutation is called correctly', () => {
@@ -333,10 +336,11 @@ describe('RunnerTypeCell', () => {
describe('On a network error', () => {
const mockErrorMsg = 'Delete error!';
- beforeEach(() => {
+ beforeEach(async () => {
runnerDeleteMutationHandler.mockRejectedValueOnce(new Error(mockErrorMsg));
findRunnerDeleteModal().vm.$emit('primary');
+ await waitForPromises();
});
it('error is reported to sentry', () => {
@@ -359,7 +363,7 @@ describe('RunnerTypeCell', () => {
const mockErrorMsg = 'Runner not found!';
const mockErrorMsg2 = 'User not allowed!';
- beforeEach(() => {
+ beforeEach(async () => {
runnerDeleteMutationHandler.mockResolvedValue({
data: {
runnerDelete: {
@@ -369,6 +373,7 @@ describe('RunnerTypeCell', () => {
});
findRunnerDeleteModal().vm.$emit('primary');
+ await waitForPromises();
});
it('error is reported to sentry', () => {
diff --git a/spec/frontend/runner/components/registration/registration_dropdown_spec.js b/spec/frontend/runner/components/registration/registration_dropdown_spec.js
index 8c08f3560f8..2685d387ef2 100644
--- a/spec/frontend/runner/components/registration/registration_dropdown_spec.js
+++ b/spec/frontend/runner/components/registration/registration_dropdown_spec.js
@@ -82,7 +82,7 @@ describe('RegistrationDropdown', () => {
const findModalInBody = () =>
createWrapper(document.body).find('[data-testid="runner-instructions-modal"]');
- beforeEach(() => {
+ beforeEach(async () => {
createComponent(
{
localVue,
@@ -94,7 +94,7 @@ describe('RegistrationDropdown', () => {
mountExtended,
);
- findRegistrationInstructionsDropdownItem().trigger('click');
+ await findRegistrationInstructionsDropdownItem().trigger('click');
});
afterEach(() => {
diff --git a/spec/frontend/runner/group_runners/group_runners_app_spec.js b/spec/frontend/runner/group_runners/group_runners_app_spec.js
index 71706767092..6becc1ba743 100644
--- a/spec/frontend/runner/group_runners/group_runners_app_spec.js
+++ b/spec/frontend/runner/group_runners/group_runners_app_spec.js
@@ -218,6 +218,7 @@ describe('GroupRunnersApp', () => {
},
});
createComponent();
+ await waitForPromises();
});
it('shows a message for no results', async () => {
@@ -226,9 +227,10 @@ describe('GroupRunnersApp', () => {
});
describe('when runners query fails', () => {
- beforeEach(() => {
+ beforeEach(async () => {
mockGroupRunnersQuery = jest.fn().mockRejectedValue(new Error('Error!'));
createComponent();
+ await waitForPromises();
});
it('error is shown to the user', async () => {
diff --git a/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js b/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js
index 07da4acef8c..76a00f5a826 100644
--- a/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js
+++ b/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js
@@ -322,9 +322,10 @@ describe('Sidebar assignees widget', () => {
});
describe('when user is not signed in', () => {
- beforeEach(() => {
+ beforeEach(async () => {
gon.current_username = undefined;
createComponent();
+ await waitForPromises();
});
it('passes signedIn prop as false to IssuableAssignees', () => {
diff --git a/spec/frontend/vue_shared/components/runner_instructions/runner_instructions_modal_spec.js b/spec/frontend/vue_shared/components/runner_instructions/runner_instructions_modal_spec.js
index e74a867ec97..7808bdb8db3 100644
--- a/spec/frontend/vue_shared/components/runner_instructions/runner_instructions_modal_spec.js
+++ b/spec/frontend/vue_shared/components/runner_instructions/runner_instructions_modal_spec.js
@@ -77,8 +77,6 @@ describe('RunnerInstructionsModal component', () => {
runnerSetupInstructionsHandler = jest.fn().mockResolvedValue(mockGraphqlInstructions);
createComponent();
-
- await nextTick();
});
afterEach(() => {
@@ -113,13 +111,15 @@ describe('RunnerInstructionsModal component', () => {
});
});
- it('binary instructions are shown', () => {
+ it('binary instructions are shown', async () => {
+ await waitForPromises();
const instructions = findBinaryInstructions().text();
expect(instructions).toBe(installInstructions);
});
- it('register command is shown with a replaced token', () => {
+ it('register command is shown with a replaced token', async () => {
+ await waitForPromises();
const instructions = findRegisterCommand().text();
expect(instructions).toBe(
@@ -130,7 +130,7 @@ describe('RunnerInstructionsModal component', () => {
describe('when a register token is not shown', () => {
beforeEach(async () => {
createComponent({ props: { registrationToken: undefined } });
- await nextTick();
+ await waitForPromises();
});
it('register command is shown without a defined registration token', () => {
@@ -198,16 +198,16 @@ describe('RunnerInstructionsModal component', () => {
expect(findSkeletonLoader().exists()).toBe(true);
expect(findGlLoadingIcon().exists()).toBe(false);
- await nextTick(); // wait for platforms
+ await nextTick();
+ await jest.runOnlyPendingTimers();
+ await nextTick();
expect(findGlLoadingIcon().exists()).toBe(true);
});
it('once loaded, should not show a loading state', async () => {
createComponent();
-
- await nextTick(); // wait for platforms
- await nextTick(); // wait for architectures
+ await waitForPromises();
expect(findSkeletonLoader().exists()).toBe(false);
expect(findGlLoadingIcon().exists()).toBe(false);
diff --git a/spec/graphql/resolvers/merge_requests_resolver_spec.rb b/spec/graphql/resolvers/merge_requests_resolver_spec.rb
index 1d0eac30a23..e4eaeb9bc3c 100644
--- a/spec/graphql/resolvers/merge_requests_resolver_spec.rb
+++ b/spec/graphql/resolvers/merge_requests_resolver_spec.rb
@@ -321,7 +321,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do
end
describe 'sorting' do
- let(:mrs) do
+ let_it_be(:mrs) do
[
merge_request_with_milestone, merge_request_6, merge_request_5, merge_request_4,
merge_request_3, merge_request_2, merge_request_1
@@ -363,28 +363,44 @@ RSpec.describe Resolvers::MergeRequestsResolver do
def merged_at(mr)
nils_last(mr.metrics.merged_at)
end
+ end
+
+ context 'when sorting by closed at' do
+ before do
+ merge_request_1.metrics.update!(latest_closed_at: 10.days.ago)
+ merge_request_3.metrics.update!(latest_closed_at: 5.days.ago)
+ end
+
+ it 'sorts merge requests ascending' do
+ expect(resolve_mr(project, sort: :closed_at_asc))
+ .to match_array(mrs)
+ .and be_sorted(->(mr) { [closed_at(mr), -mr.id] })
+ end
+
+ it 'sorts merge requests descending' do
+ expect(resolve_mr(project, sort: :closed_at_desc))
+ .to match_array(mrs)
+ .and be_sorted(->(mr) { [-closed_at(mr), -mr.id] })
+ end
+
+ def closed_at(mr)
+ nils_last(mr.metrics.latest_closed_at)
+ end
+ end
+
+ context 'when sorting by title' do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:mr1) { create(:merge_request, :unique_branches, title: 'foo', source_project: project) }
+ let_it_be(:mr2) { create(:merge_request, :unique_branches, title: 'bar', source_project: project) }
+ let_it_be(:mr3) { create(:merge_request, :unique_branches, title: 'baz', source_project: project) }
+ let_it_be(:mr4) { create(:merge_request, :unique_branches, title: 'Baz 2', source_project: project) }
+
+ it 'sorts issues ascending' do
+ expect(resolve_mr(project, sort: :title_asc).to_a).to eq [mr2, mr3, mr4, mr1]
+ end
- context 'when sorting by closed at' do
- before do
- merge_request_1.metrics.update!(latest_closed_at: 10.days.ago)
- merge_request_3.metrics.update!(latest_closed_at: 5.days.ago)
- end
-
- it 'sorts merge requests ascending' do
- expect(resolve_mr(project, sort: :closed_at_asc))
- .to match_array(mrs)
- .and be_sorted(->(mr) { [closed_at(mr), -mr.id] })
- end
-
- it 'sorts merge requests descending' do
- expect(resolve_mr(project, sort: :closed_at_desc))
- .to match_array(mrs)
- .and be_sorted(->(mr) { [-closed_at(mr), -mr.id] })
- end
-
- def closed_at(mr)
- nils_last(mr.metrics.latest_closed_at)
- end
+ it 'sorts issues descending' do
+ expect(resolve_mr(project, sort: :title_desc).to_a).to eq [mr1, mr4, mr3, mr2]
end
end
end
diff --git a/spec/lib/bulk_imports/common/extractors/graphql_extractor_spec.rb b/spec/lib/bulk_imports/common/extractors/graphql_extractor_spec.rb
index 80607485b6e..50c54a7b47f 100644
--- a/spec/lib/bulk_imports/common/extractors/graphql_extractor_spec.rb
+++ b/spec/lib/bulk_imports/common/extractors/graphql_extractor_spec.rb
@@ -8,12 +8,15 @@ RSpec.describe BulkImports::Common::Extractors::GraphqlExtractor do
let(:response) { double(original_hash: { 'data' => { 'foo' => 'bar' }, 'page_info' => {} }) }
let(:options) do
{
- query: double(
- to_s: 'test',
- variables: {},
- data_path: %w[data foo],
- page_info_path: %w[data page_info]
- )
+ query:
+ double(
+ new: double(
+ to_s: 'test',
+ variables: {},
+ data_path: %w[data foo],
+ page_info_path: %w[data page_info]
+ )
+ )
}
end
diff --git a/spec/lib/bulk_imports/common/graphql/get_members_query_spec.rb b/spec/lib/bulk_imports/common/graphql/get_members_query_spec.rb
new file mode 100644
index 00000000000..e3a7335a238
--- /dev/null
+++ b/spec/lib/bulk_imports/common/graphql/get_members_query_spec.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe BulkImports::Common::Graphql::GetMembersQuery do
+ let(:entity) { create(:bulk_import_entity, :group_entity) }
+ let(:tracker) { create(:bulk_import_tracker, entity: entity) }
+ let(:context) { BulkImports::Pipeline::Context.new(tracker) }
+
+ subject(:query) { described_class.new(context: context) }
+
+ it 'has a valid query' do
+ parsed_query = GraphQL::Query.new(
+ GitlabSchema,
+ query.to_s,
+ variables: query.variables
+ )
+ result = GitlabSchema.static_validator.validate(parsed_query)
+
+ expect(result[:errors]).to be_empty
+ end
+
+ describe '#data_path' do
+ it 'returns data path' do
+ expected = %w[data portable members nodes]
+
+ expect(query.data_path).to eq(expected)
+ end
+ end
+
+ describe '#page_info_path' do
+ it 'returns pagination information path' do
+ expected = %w[data portable members page_info]
+
+ expect(query.page_info_path).to eq(expected)
+ end
+ end
+
+ describe '#to_s' do
+ context 'when entity is group' do
+ it 'queries group & group members' do
+ expect(query.to_s).to include('group')
+ expect(query.to_s).to include('groupMembers')
+ end
+ end
+
+ context 'when entity is project' do
+ let(:entity) { create(:bulk_import_entity, :project_entity) }
+
+ it 'queries project & project members' do
+ expect(query.to_s).to include('project')
+ expect(query.to_s).to include('projectMembers')
+ end
+ end
+ end
+end
diff --git a/spec/lib/bulk_imports/common/pipelines/members_pipeline_spec.rb b/spec/lib/bulk_imports/common/pipelines/members_pipeline_spec.rb
new file mode 100644
index 00000000000..f9b95f79104
--- /dev/null
+++ b/spec/lib/bulk_imports/common/pipelines/members_pipeline_spec.rb
@@ -0,0 +1,161 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe BulkImports::Common::Pipelines::MembersPipeline do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:bulk_import) { create(:bulk_import, user: user) }
+ let_it_be(:member_user1) { create(:user, email: 'email1@email.com') }
+ let_it_be(:member_user2) { create(:user, email: 'email2@email.com') }
+ let_it_be(:member_data) do
+ {
+ user_id: member_user1.id,
+ created_by_id: member_user2.id,
+ access_level: 30,
+ created_at: '2020-01-01T00:00:00Z',
+ updated_at: '2020-01-01T00:00:00Z',
+ expires_at: nil
+ }
+ end
+
+ let(:parent) { create(:group) }
+ let(:tracker) { create(:bulk_import_tracker, entity: entity) }
+ let(:context) { BulkImports::Pipeline::Context.new(tracker) }
+ let(:members) { portable.members.map { |m| m.slice(:user_id, :access_level) } }
+
+ subject(:pipeline) { described_class.new(context) }
+
+ def extracted_data(email:, has_next_page: false)
+ data = {
+ 'created_at' => '2020-01-01T00:00:00Z',
+ 'updated_at' => '2020-01-02T00:00:00Z',
+ 'expires_at' => nil,
+ 'access_level' => {
+ 'integer_value' => 30
+ },
+ 'user' => {
+ 'public_email' => email
+ }
+ }
+
+ page_info = {
+ 'has_next_page' => has_next_page,
+ 'next_page' => has_next_page ? 'cursor' : nil
+ }
+
+ BulkImports::Pipeline::ExtractedData.new(data: data, page_info: page_info)
+ end
+
+ shared_examples 'members import' do
+ before do
+ portable.members.delete_all
+ end
+
+ describe '#run' do
+ it 'creates memberships for existing users' do
+ first_page = extracted_data(email: member_user1.email, has_next_page: true)
+ last_page = extracted_data(email: member_user2.email)
+
+ allow_next_instance_of(BulkImports::Common::Extractors::GraphqlExtractor) do |extractor|
+ allow(extractor).to receive(:extract).and_return(first_page, last_page)
+ end
+
+ expect { pipeline.run }.to change(portable.members, :count).by(2)
+
+ expect(members).to contain_exactly(
+ { user_id: member_user1.id, access_level: 30 },
+ { user_id: member_user2.id, access_level: 30 }
+ )
+ end
+ end
+
+ describe '#load' do
+ it 'creates new membership' do
+ expect { subject.load(context, member_data) }.to change(portable.members, :count).by(1)
+
+ member = portable.members.find_by_user_id(member_user1.id)
+
+ expect(member.user).to eq(member_user1)
+ expect(member.created_by).to eq(member_user2)
+ expect(member.access_level).to eq(30)
+ expect(member.created_at).to eq('2020-01-01T00:00:00Z')
+ expect(member.updated_at).to eq('2020-01-01T00:00:00Z')
+ expect(member.expires_at).to eq(nil)
+ end
+
+ context 'when user_id is current user id' do
+ it 'does not create new membership' do
+ data = { user_id: user.id }
+
+ expect { pipeline.load(context, data) }.not_to change(portable.members, :count)
+ end
+ end
+
+ context 'when data is nil' do
+ it 'does not create new membership' do
+ expect { pipeline.load(context, nil) }.not_to change(portable.members, :count)
+ end
+ end
+
+ context 'when user membership already exists with the same access level' do
+ it 'does not create new membership' do
+ portable.members.create!(member_data)
+
+ expect { pipeline.load(context, member_data) }.not_to change(portable.members, :count)
+ end
+ end
+
+ context 'when portable is in a parent group' do
+ let(:tracker) { create(:bulk_import_tracker, entity: entity_with_parent) }
+
+ before do
+ parent.members.create!(member_data)
+ end
+
+ context 'when the same membership exists in parent group' do
+ it 'does not create new membership' do
+ expect { pipeline.load(context, member_data) }.not_to change(portable_with_parent.members, :count)
+ end
+ end
+
+ context 'when membership with higher access level exists in parent group' do
+ it 'creates new direct membership' do
+ data = member_data.merge(access_level: Gitlab::Access::MAINTAINER)
+
+ expect { pipeline.load(context, data) }.to change(portable_with_parent.members, :count)
+
+ member = portable_with_parent.members.find_by_user_id(member_user1.id)
+
+ expect(member.access_level).to eq(Gitlab::Access::MAINTAINER)
+ end
+ end
+
+ context 'when membership with lower access level exists in parent group' do
+ it 'does not create new membership' do
+ data = member_data.merge(access_level: Gitlab::Access::GUEST)
+
+ expect { pipeline.load(context, data) }.not_to change(portable_with_parent.members, :count)
+ end
+ end
+ end
+ end
+ end
+
+ context 'when importing to group' do
+ let(:portable) { create(:group) }
+ let(:portable_with_parent) { create(:group, parent: parent) }
+ let(:entity) { create(:bulk_import_entity, :group_entity, group: portable, bulk_import: bulk_import) }
+ let(:entity_with_parent) { create(:bulk_import_entity, :group_entity, group: portable_with_parent, bulk_import: bulk_import) }
+
+ include_examples 'members import'
+ end
+
+ context 'when importing to project' do
+ let(:portable) { create(:project) }
+ let(:portable_with_parent) { create(:project, namespace: parent) }
+ let(:entity) { create(:bulk_import_entity, :project_entity, project: portable, bulk_import: bulk_import) }
+ let(:entity_with_parent) { create(:bulk_import_entity, :project_entity, project: portable_with_parent, bulk_import: bulk_import) }
+
+ include_examples 'members import'
+ end
+end
diff --git a/spec/lib/bulk_imports/groups/graphql/get_group_query_spec.rb b/spec/lib/bulk_imports/groups/graphql/get_group_query_spec.rb
index b0f8f74783b..d03b8d8b5b2 100644
--- a/spec/lib/bulk_imports/groups/graphql/get_group_query_spec.rb
+++ b/spec/lib/bulk_imports/groups/graphql/get_group_query_spec.rb
@@ -3,14 +3,27 @@
require 'spec_helper'
RSpec.describe BulkImports::Groups::Graphql::GetGroupQuery do
+ let_it_be(:tracker) { create(:bulk_import_tracker) }
+ let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
+
+ subject(:query) { described_class.new(context: context) }
+
+ it 'has a valid query' do
+ parsed_query = GraphQL::Query.new(
+ GitlabSchema,
+ query.to_s,
+ variables: query.variables
+ )
+ result = GitlabSchema.static_validator.validate(parsed_query)
+
+ expect(result[:errors]).to be_empty
+ end
+
describe '#variables' do
it 'returns query variables based on entity information' do
- entity = double(source_full_path: 'test', bulk_import: nil)
- tracker = double(entity: entity)
- context = BulkImports::Pipeline::Context.new(tracker)
- expected = { full_path: entity.source_full_path }
+ expected = { full_path: tracker.entity.source_full_path }
- expect(described_class.variables(context)).to eq(expected)
+ expect(subject.variables).to eq(expected)
end
end
@@ -18,7 +31,7 @@ RSpec.describe BulkImports::Groups::Graphql::GetGroupQuery do
it 'returns data path' do
expected = %w[data group]
- expect(described_class.data_path).to eq(expected)
+ expect(subject.data_path).to eq(expected)
end
end
@@ -26,7 +39,7 @@ RSpec.describe BulkImports::Groups::Graphql::GetGroupQuery do
it 'returns pagination information path' do
expected = %w[data group page_info]
- expect(described_class.page_info_path).to eq(expected)
+ expect(subject.page_info_path).to eq(expected)
end
end
end
diff --git a/spec/lib/bulk_imports/groups/graphql/get_members_query_spec.rb b/spec/lib/bulk_imports/groups/graphql/get_members_query_spec.rb
deleted file mode 100644
index d0c4bb817b2..00000000000
--- a/spec/lib/bulk_imports/groups/graphql/get_members_query_spec.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe BulkImports::Groups::Graphql::GetMembersQuery do
- it 'has a valid query' do
- tracker = create(:bulk_import_tracker)
- context = BulkImports::Pipeline::Context.new(tracker)
-
- query = GraphQL::Query.new(
- GitlabSchema,
- described_class.to_s,
- variables: described_class.variables(context)
- )
- result = GitlabSchema.static_validator.validate(query)
-
- expect(result[:errors]).to be_empty
- end
-
- describe '#data_path' do
- it 'returns data path' do
- expected = %w[data group group_members nodes]
-
- expect(described_class.data_path).to eq(expected)
- end
- end
-
- describe '#page_info_path' do
- it 'returns pagination information path' do
- expected = %w[data group group_members page_info]
-
- expect(described_class.page_info_path).to eq(expected)
- end
- end
-end
diff --git a/spec/lib/bulk_imports/groups/graphql/get_projects_query_spec.rb b/spec/lib/bulk_imports/groups/graphql/get_projects_query_spec.rb
index 1a7c5a4993c..fe28e3959a0 100644
--- a/spec/lib/bulk_imports/groups/graphql/get_projects_query_spec.rb
+++ b/spec/lib/bulk_imports/groups/graphql/get_projects_query_spec.rb
@@ -3,25 +3,25 @@
require 'spec_helper'
RSpec.describe BulkImports::Groups::Graphql::GetProjectsQuery do
- describe '#variables' do
- it 'returns valid variables based on entity information' do
- tracker = create(:bulk_import_tracker)
- context = BulkImports::Pipeline::Context.new(tracker)
-
- query = GraphQL::Query.new(
- GitlabSchema,
- described_class.to_s,
- variables: described_class.variables(context)
- )
- result = GitlabSchema.static_validator.validate(query)
-
- expect(result[:errors]).to be_empty
- end
+ let_it_be(:tracker) { create(:bulk_import_tracker) }
+ let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
+
+ subject(:query) { described_class.new(context: context) }
+
+ it 'has a valid query' do
+ parsed_query = GraphQL::Query.new(
+ GitlabSchema,
+ query.to_s,
+ variables: query.variables
+ )
+ result = GitlabSchema.static_validator.validate(parsed_query)
+
+ expect(result[:errors]).to be_empty
+ end
- context 'with invalid variables' do
- it 'raises an error' do
- expect { GraphQL::Query.new(GitlabSchema, described_class.to_s, variables: 'invalid') }.to raise_error(ArgumentError)
- end
+ context 'with invalid variables' do
+ it 'raises an error' do
+ expect { GraphQL::Query.new(GitlabSchema, subject.to_s, variables: 'invalid') }.to raise_error(ArgumentError)
end
end
@@ -29,7 +29,7 @@ RSpec.describe BulkImports::Groups::Graphql::GetProjectsQuery do
it 'returns data path' do
expected = %w[data group projects nodes]
- expect(described_class.data_path).to eq(expected)
+ expect(subject.data_path).to eq(expected)
end
end
@@ -37,7 +37,7 @@ RSpec.describe BulkImports::Groups::Graphql::GetProjectsQuery do
it 'returns pagination information path' do
expected = %w[data group projects page_info]
- expect(described_class.page_info_path).to eq(expected)
+ expect(subject.page_info_path).to eq(expected)
end
end
end
diff --git a/spec/lib/bulk_imports/groups/pipelines/members_pipeline_spec.rb b/spec/lib/bulk_imports/groups/pipelines/members_pipeline_spec.rb
deleted file mode 100644
index 0126acb320b..00000000000
--- a/spec/lib/bulk_imports/groups/pipelines/members_pipeline_spec.rb
+++ /dev/null
@@ -1,119 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe BulkImports::Groups::Pipelines::MembersPipeline do
- let_it_be(:member_user1) { create(:user, email: 'email1@email.com') }
- let_it_be(:member_user2) { create(:user, email: 'email2@email.com') }
-
- let_it_be(:user) { create(:user) }
- let_it_be(:group) { create(:group) }
- let_it_be(:bulk_import) { create(:bulk_import, user: user) }
- let_it_be(:entity) { create(:bulk_import_entity, bulk_import: bulk_import, group: group) }
- let_it_be(:tracker) { create(:bulk_import_tracker, entity: entity) }
- let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
-
- subject { described_class.new(context) }
-
- describe '#run' do
- it 'maps existing users to the imported group' do
- first_page = extracted_data(email: member_user1.email, has_next_page: true)
- last_page = extracted_data(email: member_user2.email)
-
- allow_next_instance_of(BulkImports::Common::Extractors::GraphqlExtractor) do |extractor|
- allow(extractor)
- .to receive(:extract)
- .and_return(first_page, last_page)
- end
-
- expect { subject.run }.to change(GroupMember, :count).by(2)
-
- members = group.members.map { |m| m.slice(:user_id, :access_level) }
-
- expect(members).to contain_exactly(
- { user_id: member_user1.id, access_level: 30 },
- { user_id: member_user2.id, access_level: 30 }
- )
- end
- end
-
- describe '#load' do
- it 'does nothing when there is no data' do
- expect { subject.load(context, nil) }.not_to change(GroupMember, :count)
- end
-
- it 'creates the member' do
- data = {
- 'user_id' => member_user1.id,
- 'created_by_id' => member_user2.id,
- 'access_level' => 30,
- 'created_at' => '2020-01-01T00:00:00Z',
- 'updated_at' => '2020-01-01T00:00:00Z',
- 'expires_at' => nil
- }
-
- expect { subject.load(context, data) }.to change(GroupMember, :count).by(1)
-
- member = group.members.last
-
- expect(member.user).to eq(member_user1)
- expect(member.created_by).to eq(member_user2)
- expect(member.access_level).to eq(30)
- expect(member.created_at).to eq('2020-01-01T00:00:00Z')
- expect(member.updated_at).to eq('2020-01-01T00:00:00Z')
- expect(member.expires_at).to eq(nil)
- end
-
- context 'when user_id is current user id' do
- it 'does not create new member' do
- data = { 'user_id' => user.id }
-
- expect { subject.load(context, data) }.not_to change(GroupMember, :count)
- end
- end
- end
-
- describe 'pipeline parts' do
- it { expect(described_class).to include_module(BulkImports::Pipeline) }
- it { expect(described_class).to include_module(BulkImports::Pipeline::Runner) }
-
- it 'has extractors' do
- expect(described_class.get_extractor)
- .to eq(
- klass: BulkImports::Common::Extractors::GraphqlExtractor,
- options: {
- query: BulkImports::Groups::Graphql::GetMembersQuery
- }
- )
- end
-
- it 'has transformers' do
- expect(described_class.transformers)
- .to contain_exactly(
- { klass: BulkImports::Common::Transformers::ProhibitedAttributesTransformer, options: nil },
- { klass: BulkImports::Groups::Transformers::MemberAttributesTransformer, options: nil }
- )
- end
- end
-
- def extracted_data(email:, has_next_page: false)
- data = {
- 'created_at' => '2020-01-01T00:00:00Z',
- 'updated_at' => '2020-01-01T00:00:00Z',
- 'expires_at' => nil,
- 'access_level' => {
- 'integer_value' => 30
- },
- 'user' => {
- 'public_email' => email
- }
- }
-
- page_info = {
- 'has_next_page' => has_next_page,
- 'next_page' => has_next_page ? 'cursor' : nil
- }
-
- BulkImports::Pipeline::ExtractedData.new(data: data, page_info: page_info)
- end
-end
diff --git a/spec/lib/bulk_imports/groups/stage_spec.rb b/spec/lib/bulk_imports/groups/stage_spec.rb
index 55a8e40f480..b6bb8a7d195 100644
--- a/spec/lib/bulk_imports/groups/stage_spec.rb
+++ b/spec/lib/bulk_imports/groups/stage_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe BulkImports::Groups::Stage do
[
[0, BulkImports::Groups::Pipelines::GroupPipeline],
[1, BulkImports::Groups::Pipelines::SubgroupEntitiesPipeline],
- [1, BulkImports::Groups::Pipelines::MembersPipeline],
+ [1, BulkImports::Common::Pipelines::MembersPipeline],
[1, BulkImports::Common::Pipelines::LabelsPipeline],
[1, BulkImports::Common::Pipelines::MilestonesPipeline],
[1, BulkImports::Common::Pipelines::BadgesPipeline],
diff --git a/spec/lib/bulk_imports/groups/transformers/member_attributes_transformer_spec.rb b/spec/lib/bulk_imports/groups/transformers/member_attributes_transformer_spec.rb
index af99428e0c1..c8935f71f10 100644
--- a/spec/lib/bulk_imports/groups/transformers/member_attributes_transformer_spec.rb
+++ b/spec/lib/bulk_imports/groups/transformers/member_attributes_transformer_spec.rb
@@ -48,12 +48,12 @@ RSpec.describe BulkImports::Groups::Transformers::MemberAttributesTransformer do
data = member_data(email: user.email)
expect(subject.transform(context, data)).to eq(
- 'access_level' => 30,
- 'user_id' => user.id,
- 'created_by_id' => user.id,
- 'created_at' => '2020-01-01T00:00:00Z',
- 'updated_at' => '2020-01-01T00:00:00Z',
- 'expires_at' => nil
+ access_level: 30,
+ user_id: user.id,
+ created_by_id: user.id,
+ created_at: '2020-01-01T00:00:00Z',
+ updated_at: '2020-01-01T00:00:00Z',
+ expires_at: nil
)
end
@@ -62,12 +62,12 @@ RSpec.describe BulkImports::Groups::Transformers::MemberAttributesTransformer do
data = member_data(email: secondary_email)
expect(subject.transform(context, data)).to eq(
- 'access_level' => 30,
- 'user_id' => user.id,
- 'created_by_id' => user.id,
- 'created_at' => '2020-01-01T00:00:00Z',
- 'updated_at' => '2020-01-01T00:00:00Z',
- 'expires_at' => nil
+ access_level: 30,
+ user_id: user.id,
+ created_by_id: user.id,
+ created_at: '2020-01-01T00:00:00Z',
+ updated_at: '2020-01-01T00:00:00Z',
+ expires_at: nil
)
end
diff --git a/spec/lib/bulk_imports/projects/graphql/get_project_query_spec.rb b/spec/lib/bulk_imports/projects/graphql/get_project_query_spec.rb
new file mode 100644
index 00000000000..6593aa56506
--- /dev/null
+++ b/spec/lib/bulk_imports/projects/graphql/get_project_query_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe BulkImports::Projects::Graphql::GetProjectQuery do
+ let_it_be(:tracker) { create(:bulk_import_tracker) }
+ let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
+
+ subject(:query) { described_class.new(context: context) }
+
+ it 'has a valid query' do
+ parsed_query = GraphQL::Query.new(
+ GitlabSchema,
+ query.to_s,
+ variables: query.variables
+ )
+ result = GitlabSchema.static_validator.validate(parsed_query)
+
+ expect(result[:errors]).to be_empty
+ end
+
+ it 'queries project based on source_full_path' do
+ expected = { full_path: tracker.entity.source_full_path }
+
+ expect(subject.variables).to eq(expected)
+ end
+end
diff --git a/spec/lib/bulk_imports/projects/graphql/get_repository_query_spec.rb b/spec/lib/bulk_imports/projects/graphql/get_repository_query_spec.rb
index 4dba81dc0d2..8ed105bc0c9 100644
--- a/spec/lib/bulk_imports/projects/graphql/get_repository_query_spec.rb
+++ b/spec/lib/bulk_imports/projects/graphql/get_repository_query_spec.rb
@@ -3,19 +3,29 @@
require 'spec_helper'
RSpec.describe BulkImports::Projects::Graphql::GetRepositoryQuery do
- describe 'query repository based on full_path' do
- let(:entity) { double(source_full_path: 'test', bulk_import: nil) }
- let(:tracker) { double(entity: entity) }
- let(:context) { BulkImports::Pipeline::Context.new(tracker) }
+ let_it_be(:tracker) { create(:bulk_import_tracker) }
+ let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
- it 'returns project repository url' do
- expect(described_class.to_s).to include('httpUrlToRepo')
- end
+ subject(:query) { described_class.new(context: context) }
- it 'queries project based on source_full_path' do
- expected = { full_path: entity.source_full_path }
+ it 'has a valid query' do
+ parsed_query = GraphQL::Query.new(
+ GitlabSchema,
+ query.to_s,
+ variables: query.variables
+ )
+ result = GitlabSchema.static_validator.validate(parsed_query)
- expect(described_class.variables(context)).to eq(expected)
- end
+ expect(result[:errors]).to be_empty
+ end
+
+ it 'returns project repository url' do
+ expect(subject.to_s).to include('httpUrlToRepo')
+ end
+
+ it 'queries project based on source_full_path' do
+ expected = { full_path: tracker.entity.source_full_path }
+
+ expect(subject.variables).to eq(expected)
end
end
diff --git a/spec/lib/bulk_imports/projects/graphql/get_snippet_repository_query_spec.rb b/spec/lib/bulk_imports/projects/graphql/get_snippet_repository_query_spec.rb
index b680fa5cbfc..1bd4106297d 100644
--- a/spec/lib/bulk_imports/projects/graphql/get_snippet_repository_query_spec.rb
+++ b/spec/lib/bulk_imports/projects/graphql/get_snippet_repository_query_spec.rb
@@ -3,56 +3,56 @@
require 'spec_helper'
RSpec.describe BulkImports::Projects::Graphql::GetSnippetRepositoryQuery do
- describe 'query repository based on full_path' do
- let_it_be(:entity) { create(:bulk_import_entity) }
- let_it_be(:tracker) { create(:bulk_import_tracker, entity: entity) }
- let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
-
- it 'has a valid query' do
- query = GraphQL::Query.new(
- GitlabSchema,
- described_class.to_s,
- variables: described_class.variables(context)
- )
- result = GitlabSchema.static_validator.validate(query)
-
- expect(result[:errors]).to be_empty
- end
+ let_it_be(:entity) { create(:bulk_import_entity) }
+ let_it_be(:tracker) { create(:bulk_import_tracker, entity: entity) }
+ let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
- it 'returns snippet httpUrlToRepo' do
- expect(described_class.to_s).to include('httpUrlToRepo')
- end
+ subject(:query) { described_class.new(context: context) }
- it 'returns snippet createdAt' do
- expect(described_class.to_s).to include('createdAt')
- end
+ it 'has a valid query' do
+ parsed_query = GraphQL::Query.new(
+ GitlabSchema,
+ query.to_s,
+ variables: query.variables
+ )
+ result = GitlabSchema.static_validator.validate(parsed_query)
- it 'returns snippet title' do
- expect(described_class.to_s).to include('title')
- end
+ expect(result[:errors]).to be_empty
+ end
- describe '.variables' do
- it 'queries project based on source_full_path and pagination' do
- expected = { full_path: entity.source_full_path, cursor: nil, per_page: 500 }
+ it 'returns snippet httpUrlToRepo' do
+ expect(subject.to_s).to include('httpUrlToRepo')
+ end
- expect(described_class.variables(context)).to eq(expected)
- end
+ it 'returns snippet createdAt' do
+ expect(subject.to_s).to include('createdAt')
+ end
+
+ it 'returns snippet title' do
+ expect(subject.to_s).to include('title')
+ end
+
+ describe '.variables' do
+ it 'queries project based on source_full_path and pagination' do
+ expected = { full_path: entity.source_full_path, cursor: nil, per_page: 500 }
+
+ expect(subject.variables).to eq(expected)
end
+ end
- describe '.data_path' do
- it '.data_path returns data path' do
- expected = %w[data project snippets nodes]
+ describe '.data_path' do
+ it '.data_path returns data path' do
+ expected = %w[data project snippets nodes]
- expect(described_class.data_path).to eq(expected)
- end
+ expect(subject.data_path).to eq(expected)
end
+ end
- describe '.page_info_path' do
- it '.page_info_path returns pagination information path' do
- expected = %w[data project snippets page_info]
+ describe '.page_info_path' do
+ it '.page_info_path returns pagination information path' do
+ expected = %w[data project snippets page_info]
- expect(described_class.page_info_path).to eq(expected)
- end
+ expect(subject.page_info_path).to eq(expected)
end
end
end
diff --git a/spec/lib/gitlab/ci/build/artifacts/expire_in_parser_spec.rb b/spec/lib/gitlab/ci/build/artifacts/expire_in_parser_spec.rb
index 0e26a9fa571..889878cf3ef 100644
--- a/spec/lib/gitlab/ci/build/artifacts/expire_in_parser_spec.rb
+++ b/spec/lib/gitlab/ci/build/artifacts/expire_in_parser_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Ci::Build::Artifacts::ExpireInParser do
- describe '.validate_duration' do
+ describe '.validate_duration', :request_store do
subject { described_class.validate_duration(value) }
context 'with never' do
@@ -20,14 +20,33 @@ RSpec.describe Gitlab::Ci::Build::Artifacts::ExpireInParser do
context 'with a duration' do
let(:value) { '1 Day' }
+ let(:other_value) { '30 seconds' }
it { is_expected.to be_truthy }
+
+ it 'caches data' do
+ expect(ChronicDuration).to receive(:parse).with(value).once.and_call_original
+ expect(ChronicDuration).to receive(:parse).with(other_value).once.and_call_original
+
+ 2.times do
+ expect(described_class.validate_duration(value)).to eq(86400)
+ expect(described_class.validate_duration(other_value)).to eq(30)
+ end
+ end
end
context 'without a duration' do
let(:value) { 'something' }
it { is_expected.to be_falsy }
+
+ it 'caches data' do
+ expect(ChronicDuration).to receive(:parse).with(value).once.and_call_original
+
+ 2.times do
+ expect(described_class.validate_duration(value)).to be_falsey
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb
index 687bb82a8ef..7b69d23ce90 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb
@@ -252,7 +252,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do
extra_jobs = 2
non_handled_sql_queries = 2
- # 1. Ci::InstanceVariable Load => `Ci::InstanceVariable#cached_data` => already cached with `fetch_memory_cache`
+ # 1. Ci::InstanceVariable Load => `Ci::InstanceVariable#cached_data` => already cached with `ProcessMemoryCache`
# 2. Ci::Variable Load => `Project#ci_variables_for` => already cached with `Gitlab::SafeRequestStore`
extra_jobs * non_handled_sql_queries
diff --git a/spec/lib/gitlab/process_memory_cache/helper_spec.rb b/spec/lib/gitlab/process_memory_cache/helper_spec.rb
deleted file mode 100644
index 27d7fd0bdcf..00000000000
--- a/spec/lib/gitlab/process_memory_cache/helper_spec.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::ProcessMemoryCache::Helper, :use_clean_rails_memory_store_caching do
- let(:minimal_test_class) do
- Class.new do
- include Gitlab::ProcessMemoryCache::Helper
-
- def cached_content
- fetch_memory_cache(:cached_content_instance_key) { expensive_computation }
- end
-
- def clear_cached_content
- invalidate_memory_cache(:cached_content_instance_key)
- end
- end
- end
-
- before do
- stub_const("MinimalTestClass", minimal_test_class)
- end
-
- subject { MinimalTestClass.new }
-
- describe '.fetch_memory_cache' do
- it 'memoizes the result' do
- is_expected.to receive(:expensive_computation).once.and_return(1)
-
- 2.times do
- expect(subject.cached_content).to eq(1)
- end
- end
-
- it 'resets the cache when the shared key is missing', :aggregate_failures do
- expect(Rails.cache).to receive(:read).with(:cached_content_instance_key).twice.and_return(nil)
- is_expected.to receive(:expensive_computation).thrice.and_return(1, 2, 3)
-
- 3.times do |index|
- expect(subject.cached_content).to eq(index + 1)
- end
- end
- end
-
- describe '.invalidate_memory_cache' do
- it 'invalidates the cache' do
- is_expected.to receive(:expensive_computation).twice.and_return(1, 2)
-
- expect { subject.clear_cached_content }.to change { subject.cached_content }
- end
- end
-end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 5839bc414cc..0b3fb80f97f 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -585,6 +585,21 @@ RSpec.describe MergeRequest, factory_default: :keep do
expect(merge_requests).to eq([older_mr, newer_mr])
end
end
+
+ context 'title' do
+ let_it_be(:first_mr) { create(:merge_request, :closed, title: 'One') }
+ let_it_be(:second_mr) { create(:merge_request, :closed, title: 'Two') }
+
+ it 'sorts asc' do
+ merge_requests = described_class.sort_by_attribute(:title_asc)
+ expect(merge_requests).to eq([first_mr, second_mr])
+ end
+
+ it 'sorts desc' do
+ merge_requests = described_class.sort_by_attribute(:title_desc)
+ expect(merge_requests).to eq([second_mr, first_mr])
+ end
+ end
end
describe 'time to merge calculations' do
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index a751f785913..0a90be4a068 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -436,6 +436,26 @@ RSpec.describe API::MergeRequests do
response_dates = json_response.map { |merge_request| merge_request['created_at'] }
expect(response_dates).to eq(response_dates.sort)
end
+
+ context 'returns an array of merge_requests ordered by title' do
+ it 'asc when requested' do
+ path = endpoint_path + '?order_by=title&sort=asc'
+
+ get api(path, user)
+
+ response_titles = json_response.map { |merge_request| merge_request['title'] }
+ expect(response_titles).to eq(response_titles.sort)
+ end
+
+ it 'desc when requested' do
+ path = endpoint_path + '?order_by=title&sort=desc'
+
+ get api(path, user)
+
+ response_titles = json_response.map { |merge_request| merge_request['title'] }
+ expect(response_titles).to eq(response_titles.sort.reverse)
+ end
+ end
end
context 'NOT params' do
diff --git a/spec/support/shared_examples/graphql/mutations/security/ci_configuration_shared_examples.rb b/spec/support/shared_examples/graphql/mutations/security/ci_configuration_shared_examples.rb
index 2bb3d807aa7..14b2663a72c 100644
--- a/spec/support/shared_examples/graphql/mutations/security/ci_configuration_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/mutations/security/ci_configuration_shared_examples.rb
@@ -18,7 +18,7 @@ RSpec.shared_examples_for 'graphql mutations security ci configuration' do
ServiceResponse.success(payload: { branch: branch, success_path: success_path })
end
- let(:error) { "An error occured!" }
+ let(:error) { "An error occurred!" }
let(:service_error_response) do
ServiceResponse.error(message: error)
diff --git a/spec/workers/every_sidekiq_worker_spec.rb b/spec/workers/every_sidekiq_worker_spec.rb
index bb4e2981070..18ce853dbef 100644
--- a/spec/workers/every_sidekiq_worker_spec.rb
+++ b/spec/workers/every_sidekiq_worker_spec.rb
@@ -370,7 +370,6 @@ RSpec.describe 'Every Sidekiq worker' do
'PagesDomainSslRenewalWorker' => 3,
'PagesDomainVerificationWorker' => 3,
'PagesTransferWorker' => 3,
- 'PagesUpdateConfigurationWorker' => 1,
'PagesWorker' => 3,
'PersonalAccessTokens::Groups::PolicyWorker' => 3,
'PersonalAccessTokens::Instance::PolicyWorker' => 3,
diff --git a/spec/workers/pages_update_configuration_worker_spec.rb b/spec/workers/pages_update_configuration_worker_spec.rb
deleted file mode 100644
index af71f6b3cca..00000000000
--- a/spec/workers/pages_update_configuration_worker_spec.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-# frozen_string_literal: true
-require "spec_helper"
-
-RSpec.describe PagesUpdateConfigurationWorker do
- let_it_be(:project) { create(:project) }
-
- describe "#perform" do
- it "does not break" do
- expect { subject.perform(-1) }.not_to raise_error
- end
- end
-end