Commit dbc2e70a authored by Natalia Tepluhina's avatar Natalia Tepluhina Committed by Vitaly Slobodin

Added mock client helper modification

Added `waitForPromises` to unit tests to work with modified helper
parent f0814a53
...@@ -1081,55 +1081,6 @@ If you use the RubyMine IDE, and have marked the `tmp` directory as ...@@ -1081,55 +1081,6 @@ If you use the RubyMine IDE, and have marked the `tmp` directory as
`gitlab/tmp/tests/graphql`. This will allow the **JS GraphQL** plugin to `gitlab/tmp/tests/graphql`. This will allow the **JS GraphQL** plugin to
automatically find and index the schema. automatically find and index the schema.
#### Mocking response as component data
<!-- vale gitlab.Spelling = NO -->
With [Vue Test Utils](https://vue-test-utils.vuejs.org/) one can quickly test components that
fetch GraphQL queries. The simplest way is to use `shallowMount` and then set
the data on the component:
<!-- vale gitlab.Spelling = YES -->
```javascript
it('tests apollo component', () => {
const vm = shallowMount(App);
vm.setData({
...mockData
});
});
```
#### Testing loading state
To test how a component renders when results from the GraphQL API are still loading, mock a loading state into respective Apollo queries/mutations:
```javascript
function createComponent({
loading = false,
} = {}) {
const $apollo = {
queries: {
designs: {
loading,
},
},
};
wrapper = shallowMount(Index, {
sync: false,
mocks: { $apollo }
});
}
it('renders loading icon', () => {
createComponent({ loading: true });
expect(wrapper.element).toMatchSnapshot();
})
```
#### Testing Apollo components #### Testing Apollo components
If we use `ApolloQuery` or `ApolloMutation` in our components, in order to test their functionality we need to add a stub first: If we use `ApolloQuery` or `ApolloMutation` in our components, in order to test their functionality we need to add a stub first:
...@@ -1197,11 +1148,9 @@ it('calls mutation on submitting form ', () => { ...@@ -1197,11 +1148,9 @@ it('calls mutation on submitting form ', () => {
}); });
``` ```
### Testing with mocked Apollo Client #### Mocking Apollo Client
To test the logic of Apollo cache updates, we might want to mock an Apollo Client in our unit tests. We use [`mock-apollo-client`](https://www.npmjs.com/package/mock-apollo-client) library to mock Apollo client and [`createMockApollo` helper](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/frontend/__helpers__/mock_apollo_helper.js) we created on top of it.
To separate tests with mocked client from 'usual' unit tests, create an additional factory and pass the created `mockApollo` as an option to the `createComponent`-factory. This way we only create Apollo Client instance when it's necessary. To test the components with Apollo operations, we need to mock an Apollo Client in our unit tests. We use [`mock-apollo-client`](https://www.npmjs.com/package/mock-apollo-client) library to mock Apollo client and [`createMockApollo` helper](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/frontend/__helpers__/mock_apollo_helper.js) we created on top of it.
We need to inject `VueApollo` into the Vue instance by calling `Vue.use(VueApollo)`. This will install `VueApollo` globally for all the tests in the file. It is recommended to call `Vue.use(VueApollo)` just after the imports. We need to inject `VueApollo` into the Vue instance by calling `Vue.use(VueApollo)`. This will install `VueApollo` globally for all the tests in the file. It is recommended to call `Vue.use(VueApollo)` just after the imports.
...@@ -1320,8 +1269,7 @@ it('renders designs list', async () => { ...@@ -1320,8 +1269,7 @@ it('renders designs list', async () => {
const mockApollo = createMockApolloProvider(); const mockApollo = createMockApolloProvider();
const wrapper = createComponent({ mockApollo }); const wrapper = createComponent({ mockApollo });
jest.runOnlyPendingTimers(); await waitForPromises()
await wrapper.vm.$nextTick();
expect(findDesigns()).toHaveLength(3); expect(findDesigns()).toHaveLength(3);
}); });
...@@ -1342,8 +1290,7 @@ function createMockApolloProvider() { ...@@ -1342,8 +1290,7 @@ function createMockApolloProvider() {
it('renders error if query fails', async () => { it('renders error if query fails', async () => {
const wrapper = createComponent(); const wrapper = createComponent();
jest.runOnlyPendingTimers(); await waitForPromises()
await wrapper.vm.$nextTick();
expect(wrapper.find('.test-error').exists()).toBe(true) expect(wrapper.find('.test-error').exists()).toBe(true)
}) })
...@@ -1351,7 +1298,7 @@ it('renders error if query fails', async () => { ...@@ -1351,7 +1298,7 @@ it('renders error if query fails', async () => {
Request handlers can also be passed to component factory as a parameter. Request handlers can also be passed to component factory as a parameter.
Mutations could be tested the same way with a few additional `nextTick`s to get the updated result: Mutations could be tested the same way:
```javascript ```javascript
function createMockApolloProvider({ function createMockApolloProvider({
...@@ -1391,7 +1338,7 @@ it('calls a mutation with correct parameters and reorders designs', async () => ...@@ -1391,7 +1338,7 @@ it('calls a mutation with correct parameters and reorders designs', async () =>
expect(moveDesignHandler).toHaveBeenCalled(); expect(moveDesignHandler).toHaveBeenCalled();
await wrapper.vm.$nextTick(); await waitForPromises();
expect( expect(
findDesigns() findDesigns()
...@@ -1407,8 +1354,7 @@ To mock multiple query response states, success and failure, Apollo Client's nat ...@@ -1407,8 +1354,7 @@ To mock multiple query response states, success and failure, Apollo Client's nat
describe('when query times out', () => { describe('when query times out', () => {
const advanceApolloTimers = async () => { const advanceApolloTimers = async () => {
jest.runOnlyPendingTimers(); jest.runOnlyPendingTimers();
await wrapper.vm.$nextTick(); await waitForPromises()
await wrapper.vm.$nextTick();
}; };
beforeEach(async () => { beforeEach(async () => {
...@@ -1419,7 +1365,7 @@ describe('when query times out', () => { ...@@ -1419,7 +1365,7 @@ describe('when query times out', () => {
.mockResolvedValueOnce({ errors: [{ message: 'timeout' }] }); .mockResolvedValueOnce({ errors: [{ message: 'timeout' }] });
createComponentWithApollo(failSucceedFail); createComponentWithApollo(failSucceedFail);
await wrapper.vm.$nextTick(); await waitForPromises();
}); });
it('shows correct errors and does not overwrite populated data when data is empty', async () => { it('shows correct errors and does not overwrite populated data when data is empty', async () => {
......
...@@ -181,7 +181,7 @@ describe('SubscriptionManagementApp', () => { ...@@ -181,7 +181,7 @@ describe('SubscriptionManagementApp', () => {
}); });
describe('activating the license', () => { describe('activating the license', () => {
beforeEach(() => { beforeEach(async () => {
currentSubscriptionResolver = jest currentSubscriptionResolver = jest
.fn() .fn()
.mockResolvedValue({ data: { currentLicense: license.ULTIMATE } }); .mockResolvedValue({ data: { currentLicense: license.ULTIMATE } });
...@@ -192,6 +192,7 @@ describe('SubscriptionManagementApp', () => { ...@@ -192,6 +192,7 @@ describe('SubscriptionManagementApp', () => {
currentSubscriptionResolver, currentSubscriptionResolver,
subscriptionHistoryResolver, subscriptionHistoryResolver,
]); ]);
await waitForPromises();
}); });
it('passes the correct data to the subscription breakdown', () => { it('passes the correct data to the subscription breakdown', () => {
...@@ -223,7 +224,7 @@ describe('SubscriptionManagementApp', () => { ...@@ -223,7 +224,7 @@ describe('SubscriptionManagementApp', () => {
}); });
describe('with active license', () => { describe('with active license', () => {
beforeEach(() => { beforeEach(async () => {
currentSubscriptionResolver = jest currentSubscriptionResolver = jest
.fn() .fn()
.mockResolvedValue({ data: { currentLicense: license.ULTIMATE } }); .mockResolvedValue({ data: { currentLicense: license.ULTIMATE } });
...@@ -234,6 +235,7 @@ describe('SubscriptionManagementApp', () => { ...@@ -234,6 +235,7 @@ describe('SubscriptionManagementApp', () => {
currentSubscriptionResolver, currentSubscriptionResolver,
subscriptionHistoryResolver, subscriptionHistoryResolver,
]); ]);
await waitForPromises();
}); });
it('queries for the current license', () => { it('queries for the current license', () => {
......
...@@ -104,7 +104,7 @@ describe('SubscriptionActivationForm', () => { ...@@ -104,7 +104,7 @@ describe('SubscriptionActivationForm', () => {
}); });
describe('form validation', () => { describe('form validation', () => {
const mutationMock = jest.fn(); const mutationMock = jest.fn().mockRejectedValue({});
beforeEach(async () => { beforeEach(async () => {
createComponentWithApollo({ mutationMock, mountMethod: mount }); createComponentWithApollo({ mutationMock, mountMethod: mount });
await findAgreementCheckbox().vm.$emit('input', false); await findAgreementCheckbox().vm.$emit('input', false);
......
...@@ -13,6 +13,7 @@ import { ...@@ -13,6 +13,7 @@ import {
import bulkEnableDevopsAdoptionNamespacesMutation from 'ee/analytics/devops_reports/devops_adoption/graphql/mutations/bulk_enable_devops_adoption_namespaces.mutation.graphql'; import bulkEnableDevopsAdoptionNamespacesMutation from 'ee/analytics/devops_reports/devops_adoption/graphql/mutations/bulk_enable_devops_adoption_namespaces.mutation.graphql';
import disableDevopsAdoptionNamespaceMutation from 'ee/analytics/devops_reports/devops_adoption/graphql/mutations/disable_devops_adoption_namespace.mutation.graphql'; import disableDevopsAdoptionNamespaceMutation from 'ee/analytics/devops_reports/devops_adoption/graphql/mutations/disable_devops_adoption_namespace.mutation.graphql';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { import {
...@@ -203,7 +204,8 @@ describe('DevopsAdoptionAddDropdown', () => { ...@@ -203,7 +204,8 @@ describe('DevopsAdoptionAddDropdown', () => {
clickFirstRow(); clickFirstRow();
}); });
it('calls sentry', () => { it('calls sentry', async () => {
await waitForPromises();
expect(Sentry.captureException.mock.calls[0][0].networkError).toBe( expect(Sentry.captureException.mock.calls[0][0].networkError).toBe(
genericDeleteErrorMessage, genericDeleteErrorMessage,
); );
......
...@@ -127,12 +127,13 @@ describe('DevopsAdoptionDeleteModal', () => { ...@@ -127,12 +127,13 @@ describe('DevopsAdoptionDeleteModal', () => {
}); });
describe('successful submission', () => { describe('successful submission', () => {
beforeEach(() => { beforeEach(async () => {
createComponent(); createComponent();
wrapper.vm.$refs.modal.hide = jest.fn(); wrapper.vm.$refs.modal.hide = jest.fn();
findModal().vm.$emit('primary', mockEvent); findModal().vm.$emit('primary', mockEvent);
await waitForPromises();
}); });
it('submits the correct request variables', () => { it('submits the correct request variables', () => {
......
...@@ -6,6 +6,7 @@ import ChartSkeletonLoader from '~/vue_shared/components/resizable_chart/skeleto ...@@ -6,6 +6,7 @@ import ChartSkeletonLoader from '~/vue_shared/components/resizable_chart/skeleto
import DevopsAdoptionOverviewChart from 'ee/analytics/devops_reports/devops_adoption/components/devops_adoption_overview_chart.vue'; import DevopsAdoptionOverviewChart from 'ee/analytics/devops_reports/devops_adoption/components/devops_adoption_overview_chart.vue';
import getSnapshotsQuery from 'ee/analytics/devops_reports/devops_adoption/graphql/queries/devops_adoption_overview_chart.query.graphql'; import getSnapshotsQuery from 'ee/analytics/devops_reports/devops_adoption/graphql/queries/devops_adoption_overview_chart.query.graphql';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { namespaceWithSnapotsData } from '../mock_data'; import { namespaceWithSnapotsData } from '../mock_data';
Vue.use(VueApollo); Vue.use(VueApollo);
...@@ -38,8 +39,9 @@ describe('DevopsAdoptionOverviewChart', () => { ...@@ -38,8 +39,9 @@ describe('DevopsAdoptionOverviewChart', () => {
}; };
describe('default state', () => { describe('default state', () => {
beforeEach(() => { beforeEach(async () => {
createComponent(); createComponent();
await waitForPromises();
}); });
it('does not display the chart loader', () => { it('does not display the chart loader', () => {
...@@ -68,7 +70,7 @@ describe('DevopsAdoptionOverviewChart', () => { ...@@ -68,7 +70,7 @@ describe('DevopsAdoptionOverviewChart', () => {
}); });
describe('chart tooltip', () => { describe('chart tooltip', () => {
beforeEach(() => { beforeEach(async () => {
const mockParams = { const mockParams = {
value: 'Jan', value: 'Jan',
seriesData: [{ dataIndex: 0 }], seriesData: [{ dataIndex: 0 }],
...@@ -94,6 +96,7 @@ describe('DevopsAdoptionOverviewChart', () => { ...@@ -94,6 +96,7 @@ describe('DevopsAdoptionOverviewChart', () => {
}, },
}, },
}); });
await waitForPromises();
}); });
it('displays the tooltip information correctly', () => { it('displays the tooltip information correctly', () => {
......
...@@ -6,6 +6,7 @@ import VueApollo from 'vue-apollo'; ...@@ -6,6 +6,7 @@ import VueApollo from 'vue-apollo';
import ReleaseStatsCard from 'ee/analytics/group_ci_cd_analytics/components/release_stats_card.vue'; import ReleaseStatsCard from 'ee/analytics/group_ci_cd_analytics/components/release_stats_card.vue';
import groupReleaseStatsQuery from 'ee/analytics/group_ci_cd_analytics/graphql/group_release_stats.query.graphql'; import groupReleaseStatsQuery from 'ee/analytics/group_ci_cd_analytics/graphql/group_release_stats.query.graphql';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { groupReleaseStatsQueryResponse } from './mock_data'; import { groupReleaseStatsQueryResponse } from './mock_data';
Vue.use(VueApollo); Vue.use(VueApollo);
...@@ -52,12 +53,13 @@ describe('Release stats card', () => { ...@@ -52,12 +53,13 @@ describe('Release stats card', () => {
}); });
describe('when the data has successfully loaded', () => { describe('when the data has successfully loaded', () => {
beforeEach(() => { beforeEach(async () => {
const apolloProvider = createMockApollo([ const apolloProvider = createMockApollo([
[groupReleaseStatsQuery, jest.fn().mockResolvedValueOnce(groupReleaseStatsQueryResponse)], [groupReleaseStatsQuery, jest.fn().mockResolvedValueOnce(groupReleaseStatsQueryResponse)],
]); ]);
createComponent({ apolloProvider }); createComponent({ apolloProvider });
await waitForPromises();
}); });
it('does not render loading indicators', () => { it('does not render loading indicators', () => {
...@@ -77,7 +79,7 @@ describe('Release stats card', () => { ...@@ -77,7 +79,7 @@ describe('Release stats card', () => {
}); });
describe('when the data is successfully returned, but the stats are all 0', () => { describe('when the data is successfully returned, but the stats are all 0', () => {
beforeEach(() => { beforeEach(async () => {
const responseWithZeros = merge({}, groupReleaseStatsQueryResponse, { const responseWithZeros = merge({}, groupReleaseStatsQueryResponse, {
data: { data: {
group: { group: {
...@@ -96,6 +98,7 @@ describe('Release stats card', () => { ...@@ -96,6 +98,7 @@ describe('Release stats card', () => {
]); ]);
createComponent({ apolloProvider }); createComponent({ apolloProvider });
await waitForPromises();
}); });
it('renders the statistics', () => { it('renders the statistics', () => {
...@@ -104,12 +107,13 @@ describe('Release stats card', () => { ...@@ -104,12 +107,13 @@ describe('Release stats card', () => {
}); });
describe('when an error occurs while loading data', () => { describe('when an error occurs while loading data', () => {
beforeEach(() => { beforeEach(async () => {
const apolloProvider = createMockApollo([ const apolloProvider = createMockApollo([
[groupReleaseStatsQuery, jest.fn().mockRejectedValueOnce(new Error('network error'))], [groupReleaseStatsQuery, jest.fn().mockRejectedValueOnce(new Error('network error'))],
]); ]);
createComponent({ apolloProvider }); createComponent({ apolloProvider });
await waitForPromises();
}); });
it('does not render loading indicators', () => { it('does not render loading indicators', () => {
......
...@@ -4,6 +4,7 @@ import VueApollo from 'vue-apollo'; ...@@ -4,6 +4,7 @@ import VueApollo from 'vue-apollo';
import SharedRunnerUsage from 'ee/analytics/group_ci_cd_analytics/components/shared_runner_usage.vue'; import SharedRunnerUsage from 'ee/analytics/group_ci_cd_analytics/components/shared_runner_usage.vue';
import getCiMinutesUsageByNamespace from 'ee/analytics/group_ci_cd_analytics/graphql/ci_minutes.query.graphql'; import getCiMinutesUsageByNamespace from 'ee/analytics/group_ci_cd_analytics/graphql/ci_minutes.query.graphql';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { ciMinutesUsageNamespace } from './mock_data'; import { ciMinutesUsageNamespace } from './mock_data';
const localVue = createLocalVue(); const localVue = createLocalVue();
...@@ -56,8 +57,9 @@ describe('Shared runner usage tab', () => { ...@@ -56,8 +57,9 @@ describe('Shared runner usage tab', () => {
const findAreaChart = () => wrapper.findComponent(GlAreaChart); const findAreaChart = () => wrapper.findComponent(GlAreaChart);
describe('when the data has successfully loaded', () => { describe('when the data has successfully loaded', () => {
beforeEach(() => { beforeEach(async () => {
createComponentWithApollo(); createComponentWithApollo();
await waitForPromises();
}); });
it('should display the chart', () => { it('should display the chart', () => {
......
import { GlTable } from '@gitlab/ui'; import { GlTable } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils'; import { mount, shallowMount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue'; import Vue from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import SelectProjectsDropdown from 'ee/analytics/repository_analytics/components/select_projects_dropdown.vue'; import SelectProjectsDropdown from 'ee/analytics/repository_analytics/components/select_projects_dropdown.vue';
import TestCoverageTable from 'ee/analytics/repository_analytics/components/test_coverage_table.vue'; import TestCoverageTable from 'ee/analytics/repository_analytics/components/test_coverage_table.vue';
import getGroupProjects from 'ee/analytics/repository_analytics/graphql/queries/get_group_projects.query.graphql'; import getGroupProjects from 'ee/analytics/repository_analytics/graphql/queries/get_group_projects.query.graphql';
import getProjectsTestCoverage from 'ee/analytics/repository_analytics/graphql/queries/get_projects_test_coverage.query.graphql'; import getProjectsTestCoverage from 'ee/analytics/repository_analytics/graphql/queries/get_projects_test_coverage.query.graphql';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import Api from '~/api'; import Api from '~/api';
import { getTimeago } from '~/lib/utils/datetime_utility'; import { getTimeago } from '~/lib/utils/datetime_utility';
...@@ -32,11 +33,9 @@ describe('Test coverage table component', () => { ...@@ -32,11 +33,9 @@ describe('Test coverage table component', () => {
const findProjectDateById = (id) => wrapper.findByTestId(`${id}-date`); const findProjectDateById = (id) => wrapper.findByTestId(`${id}-date`);
const clickSelectAllProjects = async () => { const clickSelectAllProjects = async () => {
findProjectsDropdown().vm.$emit('select-all-projects'); await findProjectsDropdown().vm.$emit('select-all-projects');
await nextTick();
jest.runOnlyPendingTimers(); jest.runOnlyPendingTimers();
await nextTick(); await waitForPromises();
}; };
const createComponent = ({ glFeatures = {}, mockData = {}, mountFn = shallowMount } = {}) => { const createComponent = ({ glFeatures = {}, mockData = {}, mountFn = shallowMount } = {}) => {
......
...@@ -105,7 +105,7 @@ describe('Assignee select component', () => { ...@@ -105,7 +105,7 @@ describe('Assignee select component', () => {
findEditButton().vm.$emit('click'); findEditButton().vm.$emit('click');
await waitForPromises(); await waitForPromises();
jest.advanceTimersByTime(ASSIGNEES_DEBOUNCE_DELAY); jest.advanceTimersByTime(ASSIGNEES_DEBOUNCE_DELAY);
await nextTick(); await waitForPromises();
expect(usersQueryHandlerSuccess).toHaveBeenCalled(); expect(usersQueryHandlerSuccess).toHaveBeenCalled();
expect(findDropdown().isVisible()).toBe(true); expect(findDropdown().isVisible()).toBe(true);
......
...@@ -50,8 +50,9 @@ describe('Codequality report app', () => { ...@@ -50,8 +50,9 @@ describe('Codequality report app', () => {
}); });
describe('on error', () => { describe('on error', () => {
beforeEach(() => { beforeEach(async () => {
createComponent(jest.fn().mockRejectedValueOnce(new Error('Error!'))); createComponent(jest.fn().mockRejectedValueOnce(new Error('Error!')));
await waitForPromises();
}); });
it('shows a warning icon and error message', () => { it('shows a warning icon and error message', () => {
...@@ -61,8 +62,9 @@ describe('Codequality report app', () => { ...@@ -61,8 +62,9 @@ describe('Codequality report app', () => {
}); });
describe('when there are codequality issues', () => { describe('when there are codequality issues', () => {
beforeEach(() => { beforeEach(async () => {
createComponent(jest.fn().mockResolvedValue(mockGetCodeQualityViolationsResponse)); createComponent(jest.fn().mockResolvedValue(mockGetCodeQualityViolationsResponse));
await waitForPromises();
}); });
it('renders the codequality issues', () => { it('renders the codequality issues', () => {
...@@ -104,7 +106,7 @@ describe('Codequality report app', () => { ...@@ -104,7 +106,7 @@ describe('Codequality report app', () => {
}); });
describe('when there are no codequality issues', () => { describe('when there are no codequality issues', () => {
beforeEach(() => { beforeEach(async () => {
const emptyResponse = { const emptyResponse = {
data: { data: {
project: { project: {
...@@ -122,6 +124,7 @@ describe('Codequality report app', () => { ...@@ -122,6 +124,7 @@ describe('Codequality report app', () => {
}; };
createComponent(jest.fn().mockResolvedValue(emptyResponse)); createComponent(jest.fn().mockResolvedValue(emptyResponse));
await waitForPromises();
}); });
it('shows a message that no codequality issues were found', () => { it('shows a message that no codequality issues were found', () => {
......
...@@ -96,12 +96,6 @@ describe('DeleteEscalationPolicyModal', () => { ...@@ -96,12 +96,6 @@ describe('DeleteEscalationPolicyModal', () => {
const findModal = () => wrapper.findComponent(GlModal); const findModal = () => wrapper.findComponent(GlModal);
const findAlert = () => wrapper.findComponent(GlAlert); const findAlert = () => wrapper.findComponent(GlAlert);
async function awaitApolloDomMock() {
await wrapper.vm.$nextTick(); // kick off the DOM update
await jest.runOnlyPendingTimers(); // kick off the mocked GQL stuff (promises)
await wrapper.vm.$nextTick(); // kick off the DOM update
}
async function deleteEscalationPolicy(localWrapper) { async function deleteEscalationPolicy(localWrapper) {
localWrapper.findComponent(GlModal).vm.$emit('primary', { preventDefault: jest.fn() }); localWrapper.findComponent(GlModal).vm.$emit('primary', { preventDefault: jest.fn() });
} }
...@@ -186,7 +180,7 @@ describe('DeleteEscalationPolicyModal', () => { ...@@ -186,7 +180,7 @@ describe('DeleteEscalationPolicyModal', () => {
}); });
await deleteEscalationPolicy(wrapper); await deleteEscalationPolicy(wrapper);
await awaitApolloDomMock(); await waitForPromises();
const alert = findAlert(); const alert = findAlert();
expect(alert.exists()).toBe(true); expect(alert.exists()).toBe(true);
......
...@@ -80,8 +80,10 @@ describe('List', () => { ...@@ -80,8 +80,10 @@ describe('List', () => {
}); });
describe('fetching error', () => { describe('fetching error', () => {
beforeEach(() => { beforeEach(async () => {
wrapper = createComponentWithApollo(fetchWithErrors); wrapper = createComponentWithApollo(fetchWithErrors);
jest.spyOn(Sentry, 'captureException');
await waitForPromises();
}); });
it('shows the alert', () => { it('shows the alert', () => {
...@@ -104,17 +106,14 @@ describe('List', () => { ...@@ -104,17 +106,14 @@ describe('List', () => {
}); });
it('sends the error to Sentry', async () => { it('sends the error to Sentry', async () => {
jest.spyOn(Sentry, 'captureException');
await waitForPromises();
expect(Sentry.captureException.mock.calls[0][0].networkError).toBe(sentryError); expect(Sentry.captureException.mock.calls[0][0].networkError).toBe(sentryError);
}); });
}); });
describe('empty state', () => { describe('empty state', () => {
beforeEach(() => { beforeEach(async () => {
wrapper = createComponentWithApollo(fetchEmpty); wrapper = createComponentWithApollo(fetchEmpty);
await waitForPromises();
}); });
it('shows the empty state', () => { it('shows the empty state', () => {
...@@ -133,8 +132,9 @@ describe('List', () => { ...@@ -133,8 +132,9 @@ describe('List', () => {
}); });
describe('content', () => { describe('content', () => {
beforeEach(() => { beforeEach(async () => {
wrapper = createComponentWithApollo(fetch); wrapper = createComponentWithApollo(fetch);
await waitForPromises();
}); });
it('does not show the other parts of the app', () => { it('does not show the other parts of the app', () => {
......
...@@ -5,6 +5,7 @@ import VueApollo from 'vue-apollo'; ...@@ -5,6 +5,7 @@ import VueApollo from 'vue-apollo';
import getIssuesQuery from 'ee_else_ce/issues/list/queries/get_issues.query.graphql'; import getIssuesQuery from 'ee_else_ce/issues/list/queries/get_issues.query.graphql';
import getIssuesCountsQuery from 'ee_else_ce/issues/list/queries/get_issues_counts.query.graphql'; import getIssuesCountsQuery from 'ee_else_ce/issues/list/queries/get_issues_counts.query.graphql';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { getIssuesCountsQueryResponse, getIssuesQueryResponse } from 'jest/issues/list/mock_data'; import { getIssuesCountsQueryResponse, getIssuesQueryResponse } from 'jest/issues/list/mock_data';
import { convertToGraphQLId } from '~/graphql_shared/utils'; import { convertToGraphQLId } from '~/graphql_shared/utils';
import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue'; import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue';
...@@ -63,9 +64,10 @@ describe('EE IssuesListApp component', () => { ...@@ -63,9 +64,10 @@ describe('EE IssuesListApp component', () => {
}); });
describe('template', () => { describe('template', () => {
beforeEach(() => { beforeEach(async () => {
wrapper = mountComponent(); wrapper = mountComponent();
jest.runOnlyPendingTimers(); jest.runOnlyPendingTimers();
await waitForPromises();
}); });
it('shows blocking issues count', () => { it('shows blocking issues count', () => {
......
...@@ -182,7 +182,7 @@ describe('Iteration Form', () => { ...@@ -182,7 +182,7 @@ describe('Iteration Form', () => {
groupIterationsSuccess: emptyGroupIterationsSuccess, groupIterationsSuccess: emptyGroupIterationsSuccess,
}); });
await nextTick(); await waitForPromises();
}); });
it('uses cadence start date', () => { it('uses cadence start date', () => {
...@@ -229,7 +229,7 @@ describe('Iteration Form', () => { ...@@ -229,7 +229,7 @@ describe('Iteration Form', () => {
expect(findSaveButton().text()).toBe('Update iteration'); expect(findSaveButton().text()).toBe('Update iteration');
}); });
it('triggers mutation with form data', () => { it('triggers mutation with form data', async () => {
const resolverMock = jest.fn().mockResolvedValue(updateMutationSuccess); const resolverMock = jest.fn().mockResolvedValue(updateMutationSuccess);
createComponent({ mutationQuery: updateIteration, resolverMock }); createComponent({ mutationQuery: updateIteration, resolverMock });
...@@ -244,6 +244,7 @@ describe('Iteration Form', () => { ...@@ -244,6 +244,7 @@ describe('Iteration Form', () => {
findDueDate().vm.$emit('input', dueDate); findDueDate().vm.$emit('input', dueDate);
clickSave(); clickSave();
await waitForPromises();
expect(resolverMock).toHaveBeenCalledWith({ expect(resolverMock).toHaveBeenCalledWith({
input: { input: {
...@@ -265,10 +266,11 @@ describe('Iteration Form', () => { ...@@ -265,10 +266,11 @@ describe('Iteration Form', () => {
}); });
clickSave(); clickSave();
await nextTick(); await nextTick();
expect(findSaveButton().props('loading')).toBe(true); expect(findSaveButton().props('loading')).toBe(true);
await waitForPromises();
expect(resolverMock).toHaveBeenCalledWith({ expect(resolverMock).toHaveBeenCalledWith({
input: { input: {
groupPath, groupPath,
......
...@@ -281,7 +281,7 @@ describe('Iterations report', () => { ...@@ -281,7 +281,7 @@ describe('Iterations report', () => {
`( `(
'when user $description and they are viewing an iteration within a $namespaceType', 'when user $description and they are viewing an iteration within a $namespaceType',
({ canEdit, namespaceType, canEditIteration }) => { ({ canEdit, namespaceType, canEditIteration }) => {
beforeEach(() => { beforeEach(async () => {
const mockQueryResponse = { const mockQueryResponse = {
[Namespace.Group]: mockGroupIterations, [Namespace.Group]: mockGroupIterations,
[Namespace.Project]: mockProjectIterations, [Namespace.Project]: mockProjectIterations,
...@@ -295,6 +295,7 @@ describe('Iterations report', () => { ...@@ -295,6 +295,7 @@ describe('Iterations report', () => {
}, },
mockQueryResponse, mockQueryResponse,
}); });
await waitForPromises();
}); });
it(`${canEditIteration ? 'is shown' : 'is hidden'}`, () => { it(`${canEditIteration ? 'is shown' : 'is hidden'}`, () => {
......
...@@ -65,7 +65,7 @@ describe('BaseTab', () => { ...@@ -65,7 +65,7 @@ describe('BaseTab', () => {
jest.advanceTimersByTime(PIPELINES_POLL_INTERVAL); jest.advanceTimersByTime(PIPELINES_POLL_INTERVAL);
}; };
const triggerActionError = (errorMessage) => { const triggerActionError = async (errorMessage) => {
findActions().vm.$emit('error', errorMessage); findActions().vm.$emit('error', errorMessage);
return nextTick(); return nextTick();
}; };
...@@ -161,7 +161,7 @@ describe('BaseTab', () => { ...@@ -161,7 +161,7 @@ describe('BaseTab', () => {
expect(requestHandler).toHaveBeenCalledTimes(1); expect(requestHandler).toHaveBeenCalledTimes(1);
await nextTick(); await waitForPromises();
advanceToNextFetch(); advanceToNextFetch();
expect(requestHandler).toHaveBeenCalledTimes(2); expect(requestHandler).toHaveBeenCalledTimes(2);
...@@ -201,12 +201,13 @@ describe('BaseTab', () => { ...@@ -201,12 +201,13 @@ describe('BaseTab', () => {
}); });
describe('when there are pipelines', () => { describe('when there are pipelines', () => {
beforeEach(() => { beforeEach(async () => {
createComponent({ createComponent({
propsData: { propsData: {
itemsCount: 30, itemsCount: 30,
}, },
}); });
await waitForPromises();
}); });
it('renders the title with the item count', async () => { it('renders the title with the item count', async () => {
...@@ -273,7 +274,7 @@ describe('BaseTab', () => { ...@@ -273,7 +274,7 @@ describe('BaseTab', () => {
}); });
describe('rendered cells', () => { describe('rendered cells', () => {
beforeEach(() => { beforeEach(async () => {
createFullComponent({ createFullComponent({
propsData: { propsData: {
itemsCount: 30, itemsCount: 30,
...@@ -282,6 +283,7 @@ describe('BaseTab', () => { ...@@ -282,6 +283,7 @@ describe('BaseTab', () => {
GlTable: false, GlTable: false,
}, },
}); });
await waitForPromises();
}); });
it('renders the status badge', () => { it('renders the status badge', () => {
...@@ -366,9 +368,10 @@ describe('BaseTab', () => { ...@@ -366,9 +368,10 @@ describe('BaseTab', () => {
}); });
describe('when there are no pipelines', () => { describe('when there are no pipelines', () => {
beforeEach(() => { beforeEach(async () => {
requestHandler = jest.fn().mockResolvedValue(allPipelinesWithoutPipelinesMock); requestHandler = jest.fn().mockResolvedValue(allPipelinesWithoutPipelinesMock);
createComponent(); createComponent();
await waitForPromises();
}); });
it('renders an empty state', () => { it('renders an empty state', () => {
...@@ -435,7 +438,7 @@ describe('BaseTab', () => { ...@@ -435,7 +438,7 @@ describe('BaseTab', () => {
expect(wrapper.text()).toContain(errorMessage); expect(wrapper.text()).toContain(errorMessage);
findActions().vm.$emit('action'); findActions().vm.$emit('action');
await nextTick(); await waitForPromises();
expect(wrapper.text()).not.toContain(errorMessage); expect(wrapper.text()).not.toContain(errorMessage);
}); });
......
...@@ -6,6 +6,7 @@ import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_help ...@@ -6,6 +6,7 @@ import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_help
import SavedTab from 'ee/on_demand_scans/components/tabs/saved.vue'; import SavedTab from 'ee/on_demand_scans/components/tabs/saved.vue';
import BaseTab from 'ee/on_demand_scans/components/tabs/base_tab.vue'; import BaseTab from 'ee/on_demand_scans/components/tabs/base_tab.vue';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import dastProfilesQuery from 'ee/on_demand_scans/graphql/dast_profiles.query.graphql'; import dastProfilesQuery from 'ee/on_demand_scans/graphql/dast_profiles.query.graphql';
import dastProfileRunMutation from 'ee/on_demand_scans/graphql/dast_profile_run.mutation.graphql'; import dastProfileRunMutation from 'ee/on_demand_scans/graphql/dast_profile_run.mutation.graphql';
import dastProfileDeleteMutation from 'ee/on_demand_scans/graphql/dast_profile_delete.mutation.graphql'; import dastProfileDeleteMutation from 'ee/on_demand_scans/graphql/dast_profile_delete.mutation.graphql';
...@@ -141,8 +142,9 @@ describe('Saved tab', () => { ...@@ -141,8 +142,9 @@ describe('Saved tab', () => {
}); });
describe('custom table cells', () => { describe('custom table cells', () => {
beforeEach(() => { beforeEach(async () => {
createFullComponent(); createFullComponent();
await waitForPromises();
}); });
it('renders the branch name in the name cell', () => { it('renders the branch name in the name cell', () => {
...@@ -230,8 +232,8 @@ describe('Saved tab', () => { ...@@ -230,8 +232,8 @@ describe('Saved tab', () => {
GlTable: GlTableMock, GlTable: GlTableMock,
}, },
}); });
await flushPromises();
findRunScanButton().vm.$emit('click'); findRunScanButton().vm.$emit('click');
await waitForPromises();
}); });
it('shows the error message', () => { it('shows the error message', () => {
...@@ -289,7 +291,7 @@ describe('Saved tab', () => { ...@@ -289,7 +291,7 @@ describe('Saved tab', () => {
expect(wrapper.vm.$refs['delete-scan-modal'].show).toHaveBeenCalled(); expect(wrapper.vm.$refs['delete-scan-modal'].show).toHaveBeenCalled();
}); });
it('confirming the deletion in the modal triggers the delete mutation with the profile ID', () => { it('confirming the deletion in the modal triggers the delete mutation with the profile ID', async () => {
deleteButton.vm.$emit('click'); deleteButton.vm.$emit('click');
findDeleteModal().vm.$emit('ok'); findDeleteModal().vm.$emit('ok');
...@@ -315,8 +317,9 @@ describe('Saved tab', () => { ...@@ -315,8 +317,9 @@ describe('Saved tab', () => {
GlTable: GlTableMock, GlTable: GlTableMock,
}, },
}); });
await flushPromises(); await waitForPromises();
findDeleteModal().vm.$emit('ok'); findDeleteModal().vm.$emit('ok');
await waitForPromises();
}); });
it('shows the error message', () => { it('shows the error message', () => {
......
...@@ -7,6 +7,7 @@ import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_help ...@@ -7,6 +7,7 @@ import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_help
import ScheduledTab from 'ee/on_demand_scans/components/tabs/scheduled.vue'; import ScheduledTab from 'ee/on_demand_scans/components/tabs/scheduled.vue';
import BaseTab from 'ee/on_demand_scans/components/tabs/base_tab.vue'; import BaseTab from 'ee/on_demand_scans/components/tabs/base_tab.vue';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import scheduledDastProfilesQuery from 'ee/on_demand_scans/graphql/scheduled_dast_profiles.query.graphql'; import scheduledDastProfilesQuery from 'ee/on_demand_scans/graphql/scheduled_dast_profiles.query.graphql';
import { createRouter } from 'ee/on_demand_scans/router'; import { createRouter } from 'ee/on_demand_scans/router';
import { SCHEDULED_TAB_TABLE_FIELDS, LEARN_MORE_TEXT } from 'ee/on_demand_scans/constants'; import { SCHEDULED_TAB_TABLE_FIELDS, LEARN_MORE_TEXT } from 'ee/on_demand_scans/constants';
...@@ -105,8 +106,9 @@ describe('Scheduled tab', () => { ...@@ -105,8 +106,9 @@ describe('Scheduled tab', () => {
describe('custom table cells', () => { describe('custom table cells', () => {
const [firstProfile] = scheduledDastProfilesMock.data.project.pipelines.nodes; const [firstProfile] = scheduledDastProfilesMock.data.project.pipelines.nodes;
beforeEach(() => { beforeEach(async () => {
createFullComponent(); createFullComponent();
await waitForPromises();
}); });
it('renders the next run cell', () => { it('renders the next run cell', () => {
......
...@@ -57,12 +57,6 @@ describe('AddScheduleModal', () => { ...@@ -57,12 +57,6 @@ describe('AddScheduleModal', () => {
wrapper.vm.$refs.addUpdateScheduleModal.hide = mockHideModal; wrapper.vm.$refs.addUpdateScheduleModal.hide = mockHideModal;
}; };
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
}
async function updateSchedule(localWrapper) { async function updateSchedule(localWrapper) {
localWrapper.findComponent(GlModal).vm.$emit('primary', { preventDefault: jest.fn() }); localWrapper.findComponent(GlModal).vm.$emit('primary', { preventDefault: jest.fn() });
} }
...@@ -287,7 +281,7 @@ describe('AddScheduleModal', () => { ...@@ -287,7 +281,7 @@ describe('AddScheduleModal', () => {
}); });
await updateSchedule(wrapper); await updateSchedule(wrapper);
await awaitApolloDomMock(); await waitForPromises();
const alert = findAlert(); const alert = findAlert();
expect(alert.exists()).toBe(true); expect(alert.exists()).toBe(true);
......
...@@ -31,12 +31,6 @@ describe('DeleteScheduleModal', () => { ...@@ -31,12 +31,6 @@ describe('DeleteScheduleModal', () => {
const findModalText = () => wrapper.findComponent(GlSprintf); const findModalText = () => wrapper.findComponent(GlSprintf);
const findAlert = () => wrapper.findComponent(GlAlert); const findAlert = () => wrapper.findComponent(GlAlert);
async function awaitApolloDomMock() {
await wrapper.vm.$nextTick(); // kick off the DOM update
await jest.runOnlyPendingTimers(); // kick off the mocked GQL stuff (promises)
await wrapper.vm.$nextTick(); // kick off the DOM update
}
async function destroySchedule(localWrapper) { async function destroySchedule(localWrapper) {
localWrapper.findComponent(GlModal).vm.$emit('primary', { preventDefault: jest.fn() }); localWrapper.findComponent(GlModal).vm.$emit('primary', { preventDefault: jest.fn() });
} }
...@@ -172,7 +166,7 @@ describe('DeleteScheduleModal', () => { ...@@ -172,7 +166,7 @@ describe('DeleteScheduleModal', () => {
}); });
await destroySchedule(wrapper); await destroySchedule(wrapper);
await awaitApolloDomMock(); await waitForPromises();
const alert = findAlert(); const alert = findAlert();
expect(alert.exists()).toBe(true); expect(alert.exists()).toBe(true);
......
...@@ -146,7 +146,8 @@ describe('On-call schedule', () => { ...@@ -146,7 +146,8 @@ describe('On-call schedule', () => {
}); });
}); });
it('renders rotations list', () => { it('renders rotations list', async () => {
await waitForPromises();
const rotationsList = findRotationsList(); const rotationsList = findRotationsList();
expect(rotationsList.exists()).toBe(true); expect(rotationsList.exists()).toBe(true);
expect(rotationsList.props()).toEqual({ expect(rotationsList.props()).toEqual({
...@@ -290,7 +291,6 @@ describe('On-call schedule', () => { ...@@ -290,7 +291,6 @@ describe('On-call schedule', () => {
it('does not renders rotations list from API response when skipped', async () => { it('does not renders rotations list from API response when skipped', async () => {
createComponent({ scheduleIndex: 1 }); createComponent({ scheduleIndex: 1 });
await nextTick();
await waitForPromises(); await waitForPromises();
expect(findRotationsList().props('rotations')).toHaveLength(0); expect(findRotationsList().props('rotations')).toHaveLength(0);
......
...@@ -9,6 +9,7 @@ import OnCallScheduleWrapper, { ...@@ -9,6 +9,7 @@ import OnCallScheduleWrapper, {
} from 'ee/oncall_schedules/components/oncall_schedules_wrapper.vue'; } from 'ee/oncall_schedules/components/oncall_schedules_wrapper.vue';
import getOncallSchedulesWithRotationsQuery from 'ee/oncall_schedules/graphql/queries/get_oncall_schedules.query.graphql'; import getOncallSchedulesWithRotationsQuery from 'ee/oncall_schedules/graphql/queries/get_oncall_schedules.query.graphql';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { preExistingSchedule, newlyCreatedSchedule } from './mocks/apollo_mock'; import { preExistingSchedule, newlyCreatedSchedule } from './mocks/apollo_mock';
...@@ -169,8 +170,7 @@ describe('On-call schedule wrapper', () => { ...@@ -169,8 +170,7 @@ describe('On-call schedule wrapper', () => {
it('should render newly created schedule', async () => { it('should render newly created schedule', async () => {
mountComponentWithApollo(); mountComponentWithApollo();
jest.runOnlyPendingTimers(); await waitForPromises();
await nextTick();
const schedule = findSchedules().at(1); const schedule = findSchedules().at(1);
expect(schedule.props('schedule')).toEqual(newlyCreatedSchedule); expect(schedule.props('schedule')).toEqual(newlyCreatedSchedule);
}); });
......
...@@ -35,12 +35,6 @@ describe('AddEditRotationModal', () => { ...@@ -35,12 +35,6 @@ describe('AddEditRotationModal', () => {
let userSearchQueryHandler; let userSearchQueryHandler;
let createRotationHandler; let createRotationHandler;
async function awaitApolloDomMock() {
await wrapper.vm.$nextTick(); // kick off the DOM update
await jest.runOnlyPendingTimers(); // kick off the mocked GQL stuff (promises)
await wrapper.vm.$nextTick(); // kick off the DOM update for flash
}
async function createRotation(localWrapper) { async function createRotation(localWrapper) {
localWrapper.findComponent(GlModal).vm.$emit('primary', { preventDefault: jest.fn() }); localWrapper.findComponent(GlModal).vm.$emit('primary', { preventDefault: jest.fn() });
} }
...@@ -322,7 +316,7 @@ describe('AddEditRotationModal', () => { ...@@ -322,7 +316,7 @@ describe('AddEditRotationModal', () => {
}, },
}); });
createComponentWithApollo({ search: 'root' }); createComponentWithApollo({ search: 'root' });
await awaitApolloDomMock(); await waitForPromises();
expect(userSearchQueryHandler).toHaveBeenCalledWith({ expect(userSearchQueryHandler).toHaveBeenCalledWith({
search: 'root', search: 'root',
fullPath: projectPath, fullPath: projectPath,
...@@ -334,7 +328,7 @@ describe('AddEditRotationModal', () => { ...@@ -334,7 +328,7 @@ describe('AddEditRotationModal', () => {
expect(wrapper.emitted('rotation-updated')).toBeUndefined(); expect(wrapper.emitted('rotation-updated')).toBeUndefined();
await createRotation(wrapper); await createRotation(wrapper);
await awaitApolloDomMock(); await waitForPromises();
expect(mockHideModal).toHaveBeenCalled(); expect(mockHideModal).toHaveBeenCalled();
expect(createRotationHandler).toHaveBeenCalled(); expect(createRotationHandler).toHaveBeenCalled();
...@@ -350,7 +344,7 @@ describe('AddEditRotationModal', () => { ...@@ -350,7 +344,7 @@ describe('AddEditRotationModal', () => {
}); });
await createRotation(wrapper); await createRotation(wrapper);
await awaitApolloDomMock(); await waitForPromises();
const alert = findAlert(); const alert = findAlert();
expect(alert.exists()).toBe(true); expect(alert.exists()).toBe(true);
...@@ -361,7 +355,7 @@ describe('AddEditRotationModal', () => { ...@@ -361,7 +355,7 @@ describe('AddEditRotationModal', () => {
describe('edit mode', () => { describe('edit mode', () => {
beforeEach(async () => { beforeEach(async () => {
await createComponentWithApollo({ props: { isEditMode: true } }); await createComponentWithApollo({ props: { isEditMode: true } });
await awaitApolloDomMock(); await waitForPromises();
findModal().vm.$emit('show'); findModal().vm.$emit('show');
}); });
......
...@@ -33,12 +33,6 @@ describe('DeleteRotationModal', () => { ...@@ -33,12 +33,6 @@ describe('DeleteRotationModal', () => {
const findModalText = () => wrapper.findComponent(GlSprintf); const findModalText = () => wrapper.findComponent(GlSprintf);
const findAlert = () => wrapper.findComponent(GlAlert); const findAlert = () => wrapper.findComponent(GlAlert);
async function awaitApolloDomMock() {
await wrapper.vm.$nextTick(); // kick off the DOM update
await jest.runOnlyPendingTimers(); // kick off the mocked GQL stuff (promises)
await wrapper.vm.$nextTick(); // kick off the DOM update
}
async function destroyRotation(localWrapper) { async function destroyRotation(localWrapper) {
localWrapper.findComponent(GlModal).vm.$emit('primary', { preventDefault: jest.fn() }); localWrapper.findComponent(GlModal).vm.$emit('primary', { preventDefault: jest.fn() });
} }
...@@ -178,7 +172,7 @@ describe('DeleteRotationModal', () => { ...@@ -178,7 +172,7 @@ describe('DeleteRotationModal', () => {
}); });
await destroyRotation(wrapper); await destroyRotation(wrapper);
await awaitApolloDomMock(); await waitForPromises();
const alert = findAlert(); const alert = findAlert();
expect(alert.exists()).toBe(true); expect(alert.exists()).toBe(true);
......
...@@ -3,6 +3,7 @@ import { createLocalVue } from '@vue/test-utils'; ...@@ -3,6 +3,7 @@ import { createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import mockProjectQualityResponse from 'test_fixtures/graphql/project_quality_summary/graphql/queries/get_project_quality.query.graphql.json'; import mockProjectQualityResponse from 'test_fixtures/graphql/project_quality_summary/graphql/queries/get_project_quality.query.graphql.json';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { mountExtended } from 'helpers/vue_test_utils_helper'; import { mountExtended } from 'helpers/vue_test_utils_helper';
...@@ -52,8 +53,9 @@ describe('Project quality summary app component', () => { ...@@ -52,8 +53,9 @@ describe('Project quality summary app component', () => {
}); });
describe('on error', () => { describe('on error', () => {
beforeEach(() => { beforeEach(async () => {
createComponent(jest.fn().mockRejectedValueOnce(new Error('Error!'))); createComponent(jest.fn().mockRejectedValueOnce(new Error('Error!')));
await waitForPromises();
}); });
it('shows a flash message', () => { it('shows a flash message', () => {
...@@ -62,8 +64,9 @@ describe('Project quality summary app component', () => { ...@@ -62,8 +64,9 @@ describe('Project quality summary app component', () => {
}); });
describe('with data', () => { describe('with data', () => {
beforeEach(() => { beforeEach(async () => {
createComponent(); createComponent();
await waitForPromises();
}); });
describe('test runs card', () => { describe('test runs card', () => {
......
...@@ -92,6 +92,7 @@ describe('LockButton component', () => { ...@@ -92,6 +92,7 @@ describe('LockButton component', () => {
}); });
it('executes a lock mutation once lock is confirmed', async () => { it('executes a lock mutation once lock is confirmed', async () => {
lockMutationMock = jest.fn().mockRejectedValue('Test');
createComponent({}, lockMutationMock); createComponent({}, lockMutationMock);
findLockButton().vm.$emit('click'); findLockButton().vm.$emit('click');
clickSubmit(); clickSubmit();
......
...@@ -8,6 +8,7 @@ import dastFailedSiteValidationsQuery from 'ee/security_configuration/dast_profi ...@@ -8,6 +8,7 @@ import dastFailedSiteValidationsQuery from 'ee/security_configuration/dast_profi
import dastSiteValidationRevokeMutation from 'ee/security_configuration/dast_site_validation/graphql/dast_site_validation_revoke.mutation.graphql'; import dastSiteValidationRevokeMutation from 'ee/security_configuration/dast_site_validation/graphql/dast_site_validation_revoke.mutation.graphql';
import createApolloProvider from 'helpers/mock_apollo_helper'; import createApolloProvider from 'helpers/mock_apollo_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { dastSiteValidationRevoke as dastSiteValidationRevokeResponse } from '../dast_site_validation/mock_data/apollo_mock'; import { dastSiteValidationRevoke as dastSiteValidationRevokeResponse } from '../dast_site_validation/mock_data/apollo_mock';
import { dastSiteValidations as dastSiteValidationsResponse } from '../mocks/apollo_mock'; import { dastSiteValidations as dastSiteValidationsResponse } from '../mocks/apollo_mock';
import { failedSiteValidations } from '../mocks/mock_data'; import { failedSiteValidations } from '../mocks/mock_data';
...@@ -72,7 +73,7 @@ describe('EE - DastFailedSiteValidations', () => { ...@@ -72,7 +73,7 @@ describe('EE - DastFailedSiteValidations', () => {
}); });
describe('with failed site validations', () => { describe('with failed site validations', () => {
beforeEach(() => { beforeEach(async () => {
createFullComponent( createFullComponent(
{}, {},
{ {
...@@ -82,6 +83,7 @@ describe('EE - DastFailedSiteValidations', () => { ...@@ -82,6 +83,7 @@ describe('EE - DastFailedSiteValidations', () => {
dastSiteValidationRevoke: jest.fn().mockResolvedValue(dastSiteValidationRevokeResponse()), dastSiteValidationRevoke: jest.fn().mockResolvedValue(dastSiteValidationRevokeResponse()),
}, },
); );
await waitForPromises();
}); });
it('triggers the dastSiteValidations query', () => { it('triggers the dastSiteValidations query', () => {
......
...@@ -4,6 +4,7 @@ import VueApollo from 'vue-apollo'; ...@@ -4,6 +4,7 @@ import VueApollo from 'vue-apollo';
import ProjectList from 'ee/security_dashboard/components/instance/project_list.vue'; import ProjectList from 'ee/security_dashboard/components/instance/project_list.vue';
import projectsQuery from 'ee/security_dashboard/graphql/queries/instance_projects.query.graphql'; import projectsQuery from 'ee/security_dashboard/graphql/queries/instance_projects.query.graphql';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import ProjectAvatar from '~/vue_shared/components/deprecated_project_avatar/default.vue'; import ProjectAvatar from '~/vue_shared/components/deprecated_project_avatar/default.vue';
...@@ -57,7 +58,7 @@ describe('Project List component', () => { ...@@ -57,7 +58,7 @@ describe('Project List component', () => {
it('shows an empty state if there are no projects', async () => { it('shows an empty state if there are no projects', async () => {
createWrapper({ projects: [] }); createWrapper({ projects: [] });
await wrapper.vm.$nextTick(); await waitForPromises();
expect(wrapper.findByTestId('empty-message').exists()).toBe(true); expect(wrapper.findByTestId('empty-message').exists()).toBe(true);
}); });
...@@ -71,7 +72,7 @@ describe('Project List component', () => { ...@@ -71,7 +72,7 @@ describe('Project List component', () => {
it('hides the loading indicator when query is not loading', async () => { it('hides the loading indicator when query is not loading', async () => {
createWrapper({ projects: [] }); createWrapper({ projects: [] });
await wrapper.vm.$nextTick(); await waitForPromises();
expect(getLoadingIcon().exists()).toBe(false); expect(getLoadingIcon().exists()).toBe(false);
}); });
...@@ -81,7 +82,7 @@ describe('Project List component', () => { ...@@ -81,7 +82,7 @@ describe('Project List component', () => {
'renders a list of projects and displays the correct count for %s projects', 'renders a list of projects and displays the correct count for %s projects',
async (projectsCount) => { async (projectsCount) => {
createWrapper({ projects: generateMockProjects(projectsCount) }); createWrapper({ projects: generateMockProjects(projectsCount) });
await wrapper.vm.$nextTick(); await waitForPromises();
expect(getAllProjectItems()).toHaveLength(projectsCount); expect(getAllProjectItems()).toHaveLength(projectsCount);
expect(wrapper.find(GlBadge).text()).toBe(projectsCount.toString()); expect(wrapper.find(GlBadge).text()).toBe(projectsCount.toString());
...@@ -91,8 +92,9 @@ describe('Project List component', () => { ...@@ -91,8 +92,9 @@ describe('Project List component', () => {
describe('project item', () => { describe('project item', () => {
const projects = generateMockProjects(1); const projects = generateMockProjects(1);
beforeEach(() => { beforeEach(async () => {
createWrapper({ projects }); createWrapper({ projects });
await waitForPromises();
}); });
it('renders a project item with an avatar', () => { it('renders a project item with an avatar', () => {
......
...@@ -7,6 +7,7 @@ import FindingModal from 'ee/security_dashboard/components/pipeline/vulnerabilit ...@@ -7,6 +7,7 @@ import FindingModal from 'ee/security_dashboard/components/pipeline/vulnerabilit
import VulnerabilityList from 'ee/security_dashboard/components/shared/vulnerability_list.vue'; import VulnerabilityList from 'ee/security_dashboard/components/shared/vulnerability_list.vue';
import pipelineFindingsQuery from 'ee/security_dashboard/graphql/queries/pipeline_findings.query.graphql'; import pipelineFindingsQuery from 'ee/security_dashboard/graphql/queries/pipeline_findings.query.graphql';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { mockPipelineFindingsResponse } from '../../mock_data'; import { mockPipelineFindingsResponse } from '../../mock_data';
describe('Pipeline findings', () => { describe('Pipeline findings', () => {
...@@ -70,8 +71,9 @@ describe('Pipeline findings', () => { ...@@ -70,8 +71,9 @@ describe('Pipeline findings', () => {
}); });
describe('with findings', () => { describe('with findings', () => {
beforeEach(() => { beforeEach(async () => {
createWrapperWithApollo(jest.fn().mockResolvedValue(mockPipelineFindingsResponse())); createWrapperWithApollo(jest.fn().mockResolvedValue(mockPipelineFindingsResponse()));
await waitForPromises();
}); });
it('passes false as the loading state prop', () => { it('passes false as the loading state prop', () => {
...@@ -113,9 +115,10 @@ describe('Pipeline findings', () => { ...@@ -113,9 +115,10 @@ describe('Pipeline findings', () => {
}); });
describe('with multiple page findings', () => { describe('with multiple page findings', () => {
beforeEach(() => { beforeEach(async () => {
createWrapperWithApollo( createWrapperWithApollo(
jest.fn().mockResolvedValue(mockPipelineFindingsResponse({ hasNextPage: true })), jest.fn().mockResolvedValue(mockPipelineFindingsResponse({ hasNextPage: true })),
await waitForPromises(),
); );
}); });
...@@ -125,8 +128,9 @@ describe('Pipeline findings', () => { ...@@ -125,8 +128,9 @@ describe('Pipeline findings', () => {
}); });
describe('with failed query', () => { describe('with failed query', () => {
beforeEach(() => { beforeEach(async () => {
createWrapperWithApollo(jest.fn().mockRejectedValue(new Error('GraphQL error'))); createWrapperWithApollo(jest.fn().mockRejectedValue(new Error('GraphQL error')));
await waitForPromises();
}); });
it('does not show the vulnerability list', () => { it('does not show the vulnerability list', () => {
......
import { GlEmptyState } from '@gitlab/ui'; import { GlEmptyState } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils'; import { shallowMount, createLocalVue } from '@vue/test-utils';
import { nextTick } from 'vue';
import Vuex from 'vuex'; import Vuex from 'vuex';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import pipelineSecurityReportSummaryQuery from 'ee/security_dashboard/graphql/queries/pipeline_security_report_summary.query.graphql'; import pipelineSecurityReportSummaryQuery from 'ee/security_dashboard/graphql/queries/pipeline_security_report_summary.query.graphql';
import PipelineSecurityDashboard from 'ee/security_dashboard/components/pipeline/pipeline_security_dashboard.vue'; import PipelineSecurityDashboard from 'ee/security_dashboard/components/pipeline/pipeline_security_dashboard.vue';
import ScanErrorsAlert from 'ee/security_dashboard/components/pipeline/scan_errors_alert.vue'; import ScanErrorsAlert from 'ee/security_dashboard/components/pipeline/scan_errors_alert.vue';
...@@ -161,7 +161,7 @@ describe('Pipeline Security Dashboard component', () => { ...@@ -161,7 +161,7 @@ describe('Pipeline Security Dashboard component', () => {
describe('scans error alert', () => { describe('scans error alert', () => {
describe('with errors', () => { describe('with errors', () => {
beforeEach(() => { beforeEach(async () => {
factoryWithApollo({ factoryWithApollo({
requestHandlers: [ requestHandlers: [
[ [
...@@ -170,6 +170,7 @@ describe('Pipeline Security Dashboard component', () => { ...@@ -170,6 +170,7 @@ describe('Pipeline Security Dashboard component', () => {
], ],
], ],
}); });
await waitForPromises();
}); });
it('shows an alert with information about each scan with errors', () => { it('shows an alert with information about each scan with errors', () => {
...@@ -209,7 +210,7 @@ describe('Pipeline Security Dashboard component', () => { ...@@ -209,7 +210,7 @@ describe('Pipeline Security Dashboard component', () => {
], ],
}); });
await nextTick(); await waitForPromises();
expect(wrapper.findComponent(SecurityReportsSummary).exists()).toBe( expect(wrapper.findComponent(SecurityReportsSummary).exists()).toBe(
shouldShowReportSummary, shouldShowReportSummary,
......
...@@ -14,6 +14,7 @@ import { DASHBOARD_TYPES } from 'ee/security_dashboard/store/constants'; ...@@ -14,6 +14,7 @@ import { DASHBOARD_TYPES } from 'ee/security_dashboard/store/constants';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import { useLocalStorageSpy } from 'helpers/local_storage_helper'; import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { SCANNER_NAMES_MAP } from '~/security_configuration/components/constants'; import { SCANNER_NAMES_MAP } from '~/security_configuration/components/constants';
const localVue = createLocalVue(); const localVue = createLocalVue();
...@@ -100,7 +101,7 @@ describe('Project vulnerability report app component', () => { ...@@ -100,7 +101,7 @@ describe('Project vulnerability report app component', () => {
`('visibility', ({ available, enabled, pipelineRun, expectAlertShown }) => { `('visibility', ({ available, enabled, pipelineRun, expectAlertShown }) => {
it(`should${expectAlertShown ? '' : ' not'} show the alert`, async () => { it(`should${expectAlertShown ? '' : ' not'} show the alert`, async () => {
createWrapper({ securityScanners: { available, enabled, pipelineRun } }); createWrapper({ securityScanners: { available, enabled, pipelineRun } });
await nextTick(); await waitForPromises();
expect(findSecurityScannerAlert().exists()).toBe(expectAlertShown); expect(findSecurityScannerAlert().exists()).toBe(expectAlertShown);
}); });
...@@ -118,10 +119,11 @@ describe('Project vulnerability report app component', () => { ...@@ -118,10 +119,11 @@ describe('Project vulnerability report app component', () => {
}); });
describe('dismissal', () => { describe('dismissal', () => {
beforeEach(() => { beforeEach(async () => {
createWrapper({ createWrapper({
securityScanners: { available: ['DAST'], enabled: [], pipelineRun: [] }, securityScanners: { available: ['DAST'], enabled: [], pipelineRun: [] },
}); });
await waitForPromises();
}); });
it('should hide the alert when it is dismissed', async () => { it('should hide the alert when it is dismissed', async () => {
...@@ -150,10 +152,11 @@ describe('Project vulnerability report app component', () => { ...@@ -150,10 +152,11 @@ describe('Project vulnerability report app component', () => {
(scanner) => { (scanner) => {
const translatedScannerName = SCANNER_NAMES_MAP[scanner]; const translatedScannerName = SCANNER_NAMES_MAP[scanner];
beforeEach(() => { beforeEach(async () => {
createWrapper({ createWrapper({
securityScanners: { available: [scanner], enabled: [], pipelineRun: [] }, securityScanners: { available: [scanner], enabled: [], pipelineRun: [] },
}); });
await waitForPromises();
}); });
it(`passes the translated scanner's name to the alert (${translatedScannerName})`, () => { it(`passes the translated scanner's name to the alert (${translatedScannerName})`, () => {
......
...@@ -7,6 +7,7 @@ import instanceVulnerabilityGradesQuery from 'ee/security_dashboard/graphql/quer ...@@ -7,6 +7,7 @@ import instanceVulnerabilityGradesQuery from 'ee/security_dashboard/graphql/quer
import { severityGroupTypes } from 'ee/security_dashboard/store/modules/vulnerable_projects/constants'; import { severityGroupTypes } from 'ee/security_dashboard/store/modules/vulnerable_projects/constants';
import { Accordion, AccordionItem } from 'ee/vue_shared/components/accordion'; import { Accordion, AccordionItem } from 'ee/vue_shared/components/accordion';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { trimText } from 'helpers/text_helper'; import { trimText } from 'helpers/text_helper';
import { import {
mockProjectsWithSeverityCounts, mockProjectsWithSeverityCounts,
...@@ -55,14 +56,13 @@ describe('Vulnerability Severity component', () => { ...@@ -55,14 +56,13 @@ describe('Vulnerability Severity component', () => {
}); });
describe('when loading the project severity component for group level dashboard', () => { describe('when loading the project severity component for group level dashboard', () => {
beforeEach(() => { beforeEach(async () => {
wrapper = createComponent({ wrapper = createComponent({
provide: { groupFullPath: 'gitlab-org' }, provide: { groupFullPath: 'gitlab-org' },
query: groupVulnerabilityGradesQuery, query: groupVulnerabilityGradesQuery,
mockData: mockGroupVulnerabilityGrades(), mockData: mockGroupVulnerabilityGrades(),
}); });
await waitForPromises();
return wrapper.vm.$nextTick();
}); });
it('should process the data returned from GraphQL properly', () => { it('should process the data returned from GraphQL properly', () => {
...@@ -77,13 +77,13 @@ describe('Vulnerability Severity component', () => { ...@@ -77,13 +77,13 @@ describe('Vulnerability Severity component', () => {
}); });
describe('when loading the project severity component for instance level dashboard', () => { describe('when loading the project severity component for instance level dashboard', () => {
beforeEach(() => { beforeEach(async () => {
wrapper = createComponent({ wrapper = createComponent({
query: instanceVulnerabilityGradesQuery, query: instanceVulnerabilityGradesQuery,
mockData: mockInstanceVulnerabilityGrades(), mockData: mockInstanceVulnerabilityGrades(),
}); });
return wrapper.vm.$nextTick(); await waitForPromises();
}); });
it('should process the data returned from GraphQL properly', () => { it('should process the data returned from GraphQL properly', () => {
...@@ -98,11 +98,13 @@ describe('Vulnerability Severity component', () => { ...@@ -98,11 +98,13 @@ describe('Vulnerability Severity component', () => {
}); });
describe('for all cases', () => { describe('for all cases', () => {
beforeEach(() => { beforeEach(async () => {
wrapper = createComponent({ wrapper = createComponent({
query: instanceVulnerabilityGradesQuery, query: instanceVulnerabilityGradesQuery,
mockData: mockInstanceVulnerabilityGrades(), mockData: mockInstanceVulnerabilityGrades(),
}); });
await waitForPromises();
}); });
it('has the link to the help page', () => { it('has the link to the help page', () => {
...@@ -141,7 +143,7 @@ describe('Vulnerability Severity component', () => { ...@@ -141,7 +143,7 @@ describe('Vulnerability Severity component', () => {
mockData: mockInstanceVulnerabilityGrades(), mockData: mockInstanceVulnerabilityGrades(),
}); });
await wrapper.vm.$nextTick(); await waitForPromises();
accordion = findAccordionItemByGrade(grade); accordion = findAccordionItemByGrade(grade);
text = trimText(accordion.text()); text = trimText(accordion.text());
......
...@@ -92,12 +92,9 @@ describe('Vulnerability counts component', () => { ...@@ -92,12 +92,9 @@ describe('Vulnerability counts component', () => {
it('shows an error message if the query fails', async () => { it('shows an error message if the query fails', async () => {
const countsHandler = jest.fn().mockRejectedValue(new Error()); const countsHandler = jest.fn().mockRejectedValue(new Error());
createWrapper({ countsHandler }); createWrapper({ countsHandler });
// Have to wait 2 ticks here, one for the query to finish loading, and one more for the await waitForPromises();
// GraphQL error handler to be called.
await nextTick();
await nextTick();
expect(createFlash).toHaveBeenCalledTimes(1); expect(createFlash).toHaveBeenCalledTimes(2);
}); });
it.each([DASHBOARD_TYPES.PROJECT, DASHBOARD_TYPES.GROUP, DASHBOARD_TYPES.INSTANCE])( it.each([DASHBOARD_TYPES.PROJECT, DASHBOARD_TYPES.GROUP, DASHBOARD_TYPES.INSTANCE])(
...@@ -135,7 +132,7 @@ describe('Vulnerability counts component', () => { ...@@ -135,7 +132,7 @@ describe('Vulnerability counts component', () => {
it('should show a card for each severity with the correct count', async () => { it('should show a card for each severity with the correct count', async () => {
createWrapper(); createWrapper();
await nextTick(); await waitForPromises();
// Check that there are exactly the same number of cards as there are severities. // Check that there are exactly the same number of cards as there are severities.
expect(findCards()).toHaveLength(Object.keys(counts).length); expect(findCards()).toHaveLength(Object.keys(counts).length);
...@@ -151,10 +148,7 @@ describe('Vulnerability counts component', () => { ...@@ -151,10 +148,7 @@ describe('Vulnerability counts component', () => {
it('should show zero for the count when there is no value for that severity', async () => { it('should show zero for the count when there is no value for that severity', async () => {
const handler = getCountsRequestHandler({ data: {} }); const handler = getCountsRequestHandler({ data: {} });
createWrapper({ countsHandler: handler }); createWrapper({ countsHandler: handler });
// Have to wait 2 ticks here, one for the query to finish loading, and one more for the await waitForPromises();
// computed property to update.
await nextTick();
await nextTick();
SEVERITIES.forEach((severity) => { SEVERITIES.forEach((severity) => {
expect(findCardWithSeverity(severity).text()).toContain('0'); expect(findCardWithSeverity(severity).text()).toContain('0');
......
...@@ -7,6 +7,7 @@ import VulnerabilityList from 'ee/security_dashboard/components/shared/vulnerabi ...@@ -7,6 +7,7 @@ import VulnerabilityList from 'ee/security_dashboard/components/shared/vulnerabi
import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import vulnerabilitiesQuery from 'ee/security_dashboard/graphql/queries/group_vulnerabilities.query.graphql'; import vulnerabilitiesQuery from 'ee/security_dashboard/graphql/queries/group_vulnerabilities.query.graphql';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash'; import createFlash from '~/flash';
jest.mock('~/flash'); jest.mock('~/flash');
...@@ -112,12 +113,9 @@ describe('Vulnerability list GraphQL component', () => { ...@@ -112,12 +113,9 @@ describe('Vulnerability list GraphQL component', () => {
it('shows an error message if the query fails', async () => { it('shows an error message if the query fails', async () => {
const vulnerabilitiesHandler = jest.fn().mockRejectedValue(new Error()); const vulnerabilitiesHandler = jest.fn().mockRejectedValue(new Error());
createWrapper({ vulnerabilitiesHandler }); createWrapper({ vulnerabilitiesHandler });
// Have to wait 2 ticks here, one for the query to finish loading, and one more for the await waitForPromises();
// GraphQL error handler to be called.
await nextTick();
await nextTick();
expect(createFlash).toHaveBeenCalledTimes(1); expect(createFlash).toHaveBeenCalledTimes(2);
}); });
}); });
...@@ -158,10 +156,11 @@ describe('Vulnerability list GraphQL component', () => { ...@@ -158,10 +156,11 @@ describe('Vulnerability list GraphQL component', () => {
it('will fetch more data when the appear event is fired', async () => { it('will fetch more data when the appear event is fired', async () => {
createWrapper(); createWrapper();
await nextTick(); await waitForPromises();
const spy = jest.spyOn(wrapper.vm.$apollo.queries.vulnerabilities, 'fetchMore'); const spy = jest.spyOn(wrapper.vm.$apollo.queries.vulnerabilities, 'fetchMore');
findIntersectionObserver().vm.$emit('appear'); findIntersectionObserver().vm.$emit('appear');
await waitForPromises();
expect(spy).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenCalledTimes(1);
}); });
......
...@@ -13,7 +13,7 @@ import { ...@@ -13,7 +13,7 @@ import {
stateData as mockStateData, stateData as mockStateData,
} from 'ee_jest/subscriptions/mock_data'; } from 'ee_jest/subscriptions/mock_data';
import createMockApollo, { createMockClient } from 'helpers/mock_apollo_helper'; import createMockApollo, { createMockClient } from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import orderPreviewQuery from 'ee/subscriptions/graphql/queries/order_preview.customer.query.graphql'; import orderPreviewQuery from 'ee/subscriptions/graphql/queries/order_preview.customer.query.graphql';
import { CUSTOMERSDOT_CLIENT, I18N_API_ERROR } from 'ee/subscriptions/buy_addons_shared/constants'; import { CUSTOMERSDOT_CLIENT, I18N_API_ERROR } from 'ee/subscriptions/buy_addons_shared/constants';
...@@ -97,7 +97,7 @@ describe('Order Summary', () => { ...@@ -97,7 +97,7 @@ describe('Order Summary', () => {
describe('when subscription has expiration date', () => { describe('when subscription has expiration date', () => {
describe('calls api that returns prorated amount', () => { describe('calls api that returns prorated amount', () => {
beforeEach(() => { beforeEach(async () => {
const orderPreviewQueryMock = jest const orderPreviewQueryMock = jest
.fn() .fn()
.mockResolvedValue({ data: { orderPreview: mockOrderPreview } }); .mockResolvedValue({ data: { orderPreview: mockOrderPreview } });
...@@ -106,6 +106,7 @@ describe('Order Summary', () => { ...@@ -106,6 +106,7 @@ describe('Order Summary', () => {
orderPreviewQueryMock, orderPreviewQueryMock,
); );
createComponent(apolloProvider, { purchaseHasExpiration: true }); createComponent(apolloProvider, { purchaseHasExpiration: true });
await waitForPromises();
}); });
it('renders prorated amount', () => { it('renders prorated amount', () => {
...@@ -114,13 +115,14 @@ describe('Order Summary', () => { ...@@ -114,13 +115,14 @@ describe('Order Summary', () => {
}); });
describe('calls api that returns empty value', () => { describe('calls api that returns empty value', () => {
beforeEach(() => { beforeEach(async () => {
const orderPreviewQueryMock = jest.fn().mockResolvedValue({ data: { orderPreview: null } }); const orderPreviewQueryMock = jest.fn().mockResolvedValue({ data: { orderPreview: null } });
const apolloProvider = createMockApolloProvider( const apolloProvider = createMockApolloProvider(
{ subscription: { quantity: 1 } }, { subscription: { quantity: 1 } },
orderPreviewQueryMock, orderPreviewQueryMock,
); );
createComponent(apolloProvider, { purchaseHasExpiration: true }); createComponent(apolloProvider, { purchaseHasExpiration: true });
await waitForPromises();
}); });
it('renders default amount without proration from the state', () => { it('renders default amount without proration from the state', () => {
......
...@@ -125,10 +125,10 @@ describe('Alert Drawer', () => { ...@@ -125,10 +125,10 @@ describe('Alert Drawer', () => {
const errorMessage = `GraphQL error: ${getAlertDetailsQueryErrorMessage}`; const errorMessage = `GraphQL error: ${getAlertDetailsQueryErrorMessage}`;
const captureExceptionSpy = jest.spyOn(Sentry, 'captureException'); const captureExceptionSpy = jest.spyOn(Sentry, 'captureException');
createWrapper({ apolloSpy: erroredGetAlertDetailsQuerySpy }); createWrapper({ apolloSpy: erroredGetAlertDetailsQuerySpy });
await wrapper.vm.$nextTick(); await waitForPromises();
expect(findAlert().exists()).toBe(true); expect(findAlert().exists()).toBe(true);
expect(findAlert().text()).toBe(DRAWER_ERRORS.DETAILS); expect(findAlert().text()).toBe(DRAWER_ERRORS.DETAILS);
expect(captureExceptionSpy).toHaveBeenCalledTimes(1); expect(captureExceptionSpy).toHaveBeenCalledTimes(2);
expect(captureExceptionSpy.mock.calls[0][0].message).toBe(errorMessage); expect(captureExceptionSpy.mock.calls[0][0].message).toBe(errorMessage);
}); });
......
...@@ -7,6 +7,7 @@ import AlertsList from 'ee/threat_monitoring/components/alerts/alerts_list.vue'; ...@@ -7,6 +7,7 @@ import AlertsList from 'ee/threat_monitoring/components/alerts/alerts_list.vue';
import { DEFAULT_FILTERS } from 'ee/threat_monitoring/components/alerts/constants'; import { DEFAULT_FILTERS } from 'ee/threat_monitoring/components/alerts/constants';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import { mountExtended } from 'helpers/vue_test_utils_helper'; import { mountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import getAlertsQuery from '~/graphql_shared/queries/get_alerts.query.graphql'; import getAlertsQuery from '~/graphql_shared/queries/get_alerts.query.graphql';
import { import {
getAlertsQuerySpy, getAlertsQuerySpy,
...@@ -102,8 +103,9 @@ describe('AlertsList component', () => { ...@@ -102,8 +103,9 @@ describe('AlertsList component', () => {
}); });
describe('default state', () => { describe('default state', () => {
beforeEach(() => { beforeEach(async () => {
createWrapper(); createWrapper();
await waitForPromises();
}); });
it('shows threat monitoring alert filters', () => { it('shows threat monitoring alert filters', () => {
...@@ -223,8 +225,9 @@ describe('AlertsList component', () => { ...@@ -223,8 +225,9 @@ describe('AlertsList component', () => {
}); });
describe('empty state', () => { describe('empty state', () => {
beforeEach(() => { beforeEach(async () => {
createWrapper({ apolloSpy: emptyGetAlertsQuerySpy }); createWrapper({ apolloSpy: emptyGetAlertsQuerySpy });
await waitForPromises();
}); });
it('does show the empty state', () => { it('does show the empty state', () => {
......
...@@ -255,8 +255,9 @@ describe('PoliciesList component', () => { ...@@ -255,8 +255,9 @@ describe('PoliciesList component', () => {
}); });
describe('status column', () => { describe('status column', () => {
beforeEach(() => { beforeEach(async () => {
mountWrapper(); mountWrapper();
await waitForPromises();
}); });
it('renders a checkmark icon for enabled policies', () => { it('renders a checkmark icon for enabled policies', () => {
...@@ -316,7 +317,7 @@ describe('PoliciesList component', () => { ...@@ -316,7 +317,7 @@ describe('PoliciesList component', () => {
}); });
describe('given an autodevops policy', () => { describe('given an autodevops policy', () => {
beforeEach(() => { beforeEach(async () => {
const autoDevOpsPolicy = { const autoDevOpsPolicy = {
...mockNetworkPoliciesResponse[1], ...mockNetworkPoliciesResponse[1],
name: 'auto-devops', name: 'auto-devops',
...@@ -327,6 +328,7 @@ describe('PoliciesList component', () => { ...@@ -327,6 +328,7 @@ describe('PoliciesList component', () => {
networkPolicies: networkPolicies([autoDevOpsPolicy]), networkPolicies: networkPolicies([autoDevOpsPolicy]),
}, },
}); });
await waitForPromises();
}); });
it('renders autodevops alert', () => { it('renders autodevops alert', () => {
...@@ -335,8 +337,9 @@ describe('PoliciesList component', () => { ...@@ -335,8 +337,9 @@ describe('PoliciesList component', () => {
}); });
describe('given no environments', () => { describe('given no environments', () => {
beforeEach(() => { beforeEach(async () => {
mountWrapper({ state: { threatMonitoring: { hasEnvironment: false } } }); mountWrapper({ state: { threatMonitoring: { hasEnvironment: false } } });
await waitForPromises();
}); });
it('does not make a request for network policies', () => { it('does not make a request for network policies', () => {
......
...@@ -35,11 +35,11 @@ describe('ScanNewPolicyModal Component', () => { ...@@ -35,11 +35,11 @@ describe('ScanNewPolicyModal Component', () => {
shouldSubmit = true, shouldSubmit = true,
} = {}) => { } = {}) => {
findInstanceProjectSelector().vm.$emit('projectClicked', project); findInstanceProjectSelector().vm.$emit('projectClicked', project);
await wrapper.vm.$nextTick(); await waitForPromises();
if (shouldSubmit) { if (shouldSubmit) {
findModal().vm.$emit('ok'); findModal().vm.$emit('ok');
await wrapper.vm.$nextTick(); await waitForPromises();
} }
}; };
...@@ -163,6 +163,7 @@ describe('ScanNewPolicyModal Component', () => { ...@@ -163,6 +163,7 @@ describe('ScanNewPolicyModal Component', () => {
createWrapper({ createWrapper({
provide: { assignedPolicyProject: { id: 'gid://gitlab/Project/0', name: 'Test 0' } }, provide: { assignedPolicyProject: { id: 'gid://gitlab/Project/0', name: 'Test 0' } },
}); });
await waitForPromises();
expect(findModal().attributes('ok-disabled')).toBe('true'); expect(findModal().attributes('ok-disabled')).toBe('true');
...@@ -171,7 +172,7 @@ describe('ScanNewPolicyModal Component', () => { ...@@ -171,7 +172,7 @@ describe('ScanNewPolicyModal Component', () => {
name: 'Test 1', name: 'Test 1',
}); });
await wrapper.vm.$nextTick(); await waitForPromises();
expect(findModal().attributes('ok-disabled')).toBeUndefined(); expect(findModal().attributes('ok-disabled')).toBeUndefined();
}); });
......
...@@ -114,9 +114,10 @@ describe('ProjectStorageApp', () => { ...@@ -114,9 +114,10 @@ describe('ProjectStorageApp', () => {
describe('with apollo fetching error', () => { describe('with apollo fetching error', () => {
let mockApollo; let mockApollo;
beforeEach(() => { beforeEach(async () => {
mockApollo = createMockApolloProvider(); mockApollo = createMockApolloProvider();
createComponent({ mockApollo, reject: true }); createComponent({ mockApollo, reject: true });
await waitForPromises();
}); });
it('renders gl-alert', () => { it('renders gl-alert', () => {
......
...@@ -4,6 +4,7 @@ import VueApollo from 'vue-apollo'; ...@@ -4,6 +4,7 @@ import VueApollo from 'vue-apollo';
import getIncidentStateQuery from 'ee/graphql_shared/queries/get_incident_state.query.graphql'; import getIncidentStateQuery from 'ee/graphql_shared/queries/get_incident_state.query.graphql';
import ServiceLevelAgreementCell from 'ee/vue_shared/components/incidents/service_level_agreement.vue'; import ServiceLevelAgreementCell from 'ee/vue_shared/components/incidents/service_level_agreement.vue';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { calculateRemainingMilliseconds, formatTime } from '~/lib/utils/datetime_utility'; import { calculateRemainingMilliseconds, formatTime } from '~/lib/utils/datetime_utility';
const formatTimeActual = jest.requireActual('~/lib/utils/datetime_utility').formatTime; const formatTimeActual = jest.requireActual('~/lib/utils/datetime_utility').formatTime;
...@@ -164,7 +165,7 @@ describe('Service Level Agreement', () => { ...@@ -164,7 +165,7 @@ describe('Service Level Agreement', () => {
const mockApollo = createMockApolloProvider(issueState); const mockApollo = createMockApolloProvider(issueState);
mountComponent({ props: { slaDueAt: mockDateString }, mockApollo }); mountComponent({ props: { slaDueAt: mockDateString }, mockApollo });
await nextTick(); await waitForPromises();
expect(wrapper.text()).toBe('Missed SLA'); expect(wrapper.text()).toBe('Missed SLA');
}); });
...@@ -174,7 +175,7 @@ describe('Service Level Agreement', () => { ...@@ -174,7 +175,7 @@ describe('Service Level Agreement', () => {
const mockApollo = createMockApolloProvider(issueState); const mockApollo = createMockApolloProvider(issueState);
mountComponent({ props: { slaDueAt: mockDateString }, mockApollo }); mountComponent({ props: { slaDueAt: mockDateString }, mockApollo });
await nextTick(); await waitForPromises();
expect(wrapper.text()).toBe('Achieved SLA'); expect(wrapper.text()).toBe('Achieved SLA');
}); });
......
...@@ -16,7 +16,11 @@ export function createMockClient(handlers = [], resolvers = {}, cacheOptions = { ...@@ -16,7 +16,11 @@ export function createMockClient(handlers = [], resolvers = {}, cacheOptions = {
const mockClient = createMockApolloClient({ cache, resolvers }); const mockClient = createMockApolloClient({ cache, resolvers });
if (Array.isArray(handlers)) { 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 { } else {
throw new Error('You should pass an array of handlers to mock Apollo client'); throw new Error('You should pass an array of handlers to mock Apollo client');
} }
......
...@@ -3,6 +3,7 @@ import Vue from 'vue'; ...@@ -3,6 +3,7 @@ import Vue from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { mountExtended } from 'helpers/vue_test_utils_helper'; import { mountExtended } from 'helpers/vue_test_utils_helper';
import AdminUserActions from '~/admin/users/components/user_actions.vue'; import AdminUserActions from '~/admin/users/components/user_actions.vue';
...@@ -106,8 +107,9 @@ describe('AdminUsersTable component', () => { ...@@ -106,8 +107,9 @@ describe('AdminUsersTable component', () => {
}); });
describe('when the data has been fetched', () => { describe('when the data has been fetched', () => {
beforeEach(() => { beforeEach(async () => {
initComponent(); initComponent();
await waitForPromises();
}); });
it("renders the user's group count", () => { it("renders the user's group count", () => {
...@@ -115,8 +117,9 @@ describe('AdminUsersTable component', () => { ...@@ -115,8 +117,9 @@ describe('AdminUsersTable component', () => {
}); });
describe("and a user's group count is null", () => { describe("and a user's group count is null", () => {
beforeEach(() => { beforeEach(async () => {
initComponent({}, createFetchGroupCount([{ id: user.id, groupCount: null }])); initComponent({}, createFetchGroupCount([{ id: user.id, groupCount: null }]));
await waitForPromises();
}); });
it("renders the user's group count as 0", () => { it("renders the user's group count as 0", () => {
...@@ -126,12 +129,13 @@ describe('AdminUsersTable component', () => { ...@@ -126,12 +129,13 @@ describe('AdminUsersTable component', () => {
}); });
describe('when there is an error while fetching the data', () => { describe('when there is an error while fetching the data', () => {
beforeEach(() => { beforeEach(async () => {
initComponent({}, fetchGroupCountsError); initComponent({}, fetchGroupCountsError);
await waitForPromises();
}); });
it('creates a flash message and captures the error', () => { it('creates a flash message and captures the error', () => {
expect(createFlash).toHaveBeenCalledTimes(1); expect(createFlash).toHaveBeenCalledTimes(2);
expect(createFlash).toHaveBeenCalledWith({ expect(createFlash).toHaveBeenCalledWith({
message: 'Could not load user group counts. Please refresh the page to try again.', message: 'Could not load user group counts. Please refresh the page to try again.',
captureError: true, captureError: true,
......
import { GlLoadingIcon, GlAlert } from '@gitlab/ui'; import { GlLoadingIcon, GlAlert } from '@gitlab/ui';
import { mount, createLocalVue } from '@vue/test-utils'; import { mount, createLocalVue } from '@vue/test-utils';
import AxiosMockAdapter from 'axios-mock-adapter'; import AxiosMockAdapter from 'axios-mock-adapter';
import { nextTick } from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import createHttpIntegrationMutation from 'ee_else_ce/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql'; 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'; import updateHttpIntegrationMutation from 'ee_else_ce/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql';
...@@ -79,12 +78,6 @@ describe('AlertsSettingsWrapper', () => { ...@@ -79,12 +78,6 @@ describe('AlertsSettingsWrapper', () => {
.vm.$emit('delete-integration', { id: integrationToDestroy.id }); .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 } = {}) => { const createComponent = ({ data = {}, provide = {}, loading = false } = {}) => {
wrapper = extendedWrapper( wrapper = extendedWrapper(
mount(AlertsSettingsWrapper, { mount(AlertsSettingsWrapper, {
...@@ -476,9 +469,7 @@ describe('AlertsSettingsWrapper', () => { ...@@ -476,9 +469,7 @@ describe('AlertsSettingsWrapper', () => {
describe('with mocked Apollo client', () => { describe('with mocked Apollo client', () => {
it('has a selection of integrations loaded via the getIntegrationsQuery', async () => { it('has a selection of integrations loaded via the getIntegrationsQuery', async () => {
createComponentWithApollo(); createComponentWithApollo();
await waitForPromises();
await jest.runOnlyPendingTimers();
await nextTick();
expect(findIntegrations()).toHaveLength(4); expect(findIntegrations()).toHaveLength(4);
}); });
...@@ -490,7 +481,7 @@ describe('AlertsSettingsWrapper', () => { ...@@ -490,7 +481,7 @@ describe('AlertsSettingsWrapper', () => {
expect(destroyIntegrationHandler).toHaveBeenCalled(); expect(destroyIntegrationHandler).toHaveBeenCalled();
await nextTick(); await waitForPromises();
expect(findIntegrations()).toHaveLength(3); expect(findIntegrations()).toHaveLength(3);
}); });
...@@ -501,7 +492,7 @@ describe('AlertsSettingsWrapper', () => { ...@@ -501,7 +492,7 @@ describe('AlertsSettingsWrapper', () => {
}); });
await destroyHttpIntegration(wrapper); await destroyHttpIntegration(wrapper);
await awaitApolloDomMock(); await waitForPromises();
expect(createFlash).toHaveBeenCalledWith({ message: 'Houston, we have a problem' }); expect(createFlash).toHaveBeenCalledWith({ message: 'Houston, we have a problem' });
}); });
...@@ -512,7 +503,7 @@ describe('AlertsSettingsWrapper', () => { ...@@ -512,7 +503,7 @@ describe('AlertsSettingsWrapper', () => {
}); });
await destroyHttpIntegration(wrapper); await destroyHttpIntegration(wrapper);
await awaitApolloDomMock(); await waitForPromises();
expect(createFlash).toHaveBeenCalledWith({ expect(createFlash).toHaveBeenCalledWith({
message: DELETE_INTEGRATION_ERROR, message: DELETE_INTEGRATION_ERROR,
......
...@@ -4,6 +4,7 @@ import { shallowMount } from '@vue/test-utils'; ...@@ -4,6 +4,7 @@ import { shallowMount } from '@vue/test-utils';
import Vue from 'vue'; import Vue from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper'; 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 UsageTrendsCountChart from '~/analytics/usage_trends/components/usage_trends_count_chart.vue';
import statsQuery from '~/analytics/usage_trends/graphql/queries/usage_count.query.graphql'; import statsQuery from '~/analytics/usage_trends/graphql/queries/usage_count.query.graphql';
import ChartSkeletonLoader from '~/vue_shared/components/resizable_chart/skeleton_loader.vue'; import ChartSkeletonLoader from '~/vue_shared/components/resizable_chart/skeleton_loader.vue';
...@@ -77,9 +78,10 @@ describe('UsageTrendsCountChart', () => { ...@@ -77,9 +78,10 @@ describe('UsageTrendsCountChart', () => {
}); });
describe('without data', () => { describe('without data', () => {
beforeEach(() => { beforeEach(async () => {
queryHandler = mockQueryResponse({ key: queryResponseDataKey, data: [] }); queryHandler = mockQueryResponse({ key: queryResponseDataKey, data: [] });
wrapper = createComponent({ responseHandler: queryHandler }); wrapper = createComponent({ responseHandler: queryHandler });
await waitForPromises();
}); });
it('renders an no data message', () => { it('renders an no data message', () => {
...@@ -96,9 +98,10 @@ describe('UsageTrendsCountChart', () => { ...@@ -96,9 +98,10 @@ describe('UsageTrendsCountChart', () => {
}); });
describe('with data', () => { describe('with data', () => {
beforeEach(() => { beforeEach(async () => {
queryHandler = mockQueryResponse({ key: queryResponseDataKey, data: mockCountsData1 }); queryHandler = mockQueryResponse({ key: queryResponseDataKey, data: mockCountsData1 });
wrapper = createComponent({ responseHandler: queryHandler }); wrapper = createComponent({ responseHandler: queryHandler });
await waitForPromises();
}); });
it('requests data', () => { it('requests data', () => {
...@@ -134,7 +137,7 @@ describe('UsageTrendsCountChart', () => { ...@@ -134,7 +137,7 @@ describe('UsageTrendsCountChart', () => {
}); });
wrapper = createComponent({ responseHandler: queryHandler }); wrapper = createComponent({ responseHandler: queryHandler });
await wrapper.vm.$nextTick(); await waitForPromises();
}); });
it('requests data twice', () => { it('requests data twice', () => {
......
import Draggable from 'vuedraggable'; import Draggable from 'vuedraggable';
import { DraggableItemTypes } from 'ee_else_ce/boards/constants'; import { DraggableItemTypes } from 'ee_else_ce/boards/constants';
import { useFakeRequestAnimationFrame } from 'helpers/fake_request_animation_frame'; import { useFakeRequestAnimationFrame } from 'helpers/fake_request_animation_frame';
import waitForPromises from 'helpers/wait_for_promises';
import createComponent from 'jest/boards/board_list_helper'; import createComponent from 'jest/boards/board_list_helper';
import BoardCard from '~/boards/components/board_card.vue'; import BoardCard from '~/boards/components/board_card.vue';
import eventHub from '~/boards/eventhub'; import eventHub from '~/boards/eventhub';
...@@ -132,7 +133,6 @@ describe('Board list component', () => { ...@@ -132,7 +133,6 @@ describe('Board list component', () => {
}); });
it('shows how many more issues to load', async () => { it('shows how many more issues to load', async () => {
// wrapper.vm.showCount = true;
wrapper = createComponent({ wrapper = createComponent({
data: { data: {
showCount: true, showCount: true,
...@@ -140,6 +140,9 @@ describe('Board list component', () => { ...@@ -140,6 +140,9 @@ describe('Board list component', () => {
}); });
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
await waitForPromises();
await wrapper.vm.$nextTick();
expect(wrapper.find('.board-list-count').text()).toBe('Showing 1 of 20 issues'); expect(wrapper.find('.board-list-count').text()).toBe('Showing 1 of 20 issues');
}); });
}); });
......
...@@ -70,8 +70,9 @@ describe('ActivityEvents', () => { ...@@ -70,8 +70,9 @@ describe('ActivityEvents', () => {
}); });
describe('when there are no agentEvents', () => { describe('when there are no agentEvents', () => {
beforeEach(() => { beforeEach(async () => {
createWrapper({ queryResponse: jest.fn().mockResolvedValue(mockEmptyResponse) }); createWrapper({ queryResponse: jest.fn().mockResolvedValue(mockEmptyResponse) });
await waitForPromises();
}); });
it('displays an empty state with the correct illustration', () => { it('displays an empty state with the correct illustration', () => {
...@@ -83,9 +84,11 @@ describe('ActivityEvents', () => { ...@@ -83,9 +84,11 @@ describe('ActivityEvents', () => {
describe('when the agentEvents are present', () => { describe('when the agentEvents are present', () => {
const length = mockResponse.data?.project?.clusterAgent?.activityEvents?.nodes?.length; const length = mockResponse.data?.project?.clusterAgent?.activityEvents?.nodes?.length;
beforeEach(() => { beforeEach(async () => {
createWrapper(); createWrapper();
await waitForPromises();
}); });
it('renders an activity-history-item components for every event', () => { it('renders an activity-history-item components for every event', () => {
expect(findAllActivityHistoryItems()).toHaveLength(length); expect(findAllActivityHistoryItems()).toHaveLength(length);
}); });
......
...@@ -82,8 +82,9 @@ describe('ClusterAgentShow', () => { ...@@ -82,8 +82,9 @@ describe('ClusterAgentShow', () => {
}); });
describe('default behaviour', () => { describe('default behaviour', () => {
beforeEach(() => { beforeEach(async () => {
return createWrapper({ clusterAgent: defaultClusterAgent }); createWrapper({ clusterAgent: defaultClusterAgent });
await waitForPromises();
}); });
it('sends expected params', () => { it('sends expected params', () => {
...@@ -131,8 +132,9 @@ describe('ClusterAgentShow', () => { ...@@ -131,8 +132,9 @@ describe('ClusterAgentShow', () => {
createdByUser: null, createdByUser: null,
}; };
beforeEach(() => { beforeEach(async () => {
return createWrapper({ clusterAgent: missingUser }); createWrapper({ clusterAgent: missingUser });
await waitForPromises();
}); });
it('displays agent create information with unknown user', () => { it('displays agent create information with unknown user', () => {
...@@ -146,8 +148,9 @@ describe('ClusterAgentShow', () => { ...@@ -146,8 +148,9 @@ describe('ClusterAgentShow', () => {
tokens: null, tokens: null,
}; };
beforeEach(() => { beforeEach(async () => {
return createWrapper({ clusterAgent: missingTokens }); createWrapper({ clusterAgent: missingTokens });
await waitForPromises();
}); });
it('displays token header with no count', () => { it('displays token header with no count', () => {
...@@ -171,8 +174,9 @@ describe('ClusterAgentShow', () => { ...@@ -171,8 +174,9 @@ describe('ClusterAgentShow', () => {
}, },
}; };
beforeEach(() => { beforeEach(async () => {
return createWrapper({ clusterAgent: tokenPagination }); createWrapper({ clusterAgent: tokenPagination });
await waitForPromises();
}); });
it('should render pagination buttons', () => { it('should render pagination buttons', () => {
......
...@@ -6,6 +6,7 @@ import { ENTER_KEY } from '~/lib/utils/keys'; ...@@ -6,6 +6,7 @@ import { ENTER_KEY } from '~/lib/utils/keys';
import getAgentsQuery from '~/clusters_list/graphql/queries/get_agents.query.graphql'; import getAgentsQuery from '~/clusters_list/graphql/queries/get_agents.query.graphql';
import deleteAgentMutation from '~/clusters_list/graphql/mutations/delete_agent.mutation.graphql'; import deleteAgentMutation from '~/clusters_list/graphql/mutations/delete_agent.mutation.graphql';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import AgentOptions from '~/clusters_list/components/agent_options.vue'; import AgentOptions from '~/clusters_list/components/agent_options.vue';
import { MAX_LIST_COUNT } from '~/clusters_list/constants'; import { MAX_LIST_COUNT } from '~/clusters_list/constants';
import { getAgentResponse, mockDeleteResponse, mockErrorDeleteResponse } from '../mocks/apollo'; import { getAgentResponse, mockDeleteResponse, mockErrorDeleteResponse } from '../mocks/apollo';
...@@ -83,6 +84,7 @@ describe('AgentOptions', () => { ...@@ -83,6 +84,7 @@ describe('AgentOptions', () => {
findDeleteBtn().vm.$emit('click'); findDeleteBtn().vm.$emit('click');
findInput().vm.$emit('input', agent.name); findInput().vm.$emit('input', agent.name);
await findModal().vm.$emit('primary'); await findModal().vm.$emit('primary');
await waitForPromises();
}; };
beforeEach(() => { beforeEach(() => {
...@@ -173,8 +175,7 @@ describe('AgentOptions', () => { ...@@ -173,8 +175,7 @@ describe('AgentOptions', () => {
describe('when getting an error deleting agent', () => { describe('when getting an error deleting agent', () => {
beforeEach(async () => { beforeEach(async () => {
await createWrapper({ mutationResponse: mockErrorDeleteResponse }); await createWrapper({ mutationResponse: mockErrorDeleteResponse });
await submitAgentToDelete();
submitAgentToDelete();
}); });
it('displays the error message', () => { it('displays the error message', () => {
...@@ -187,7 +188,7 @@ describe('AgentOptions', () => { ...@@ -187,7 +188,7 @@ describe('AgentOptions', () => {
const loadingResponse = new Promise(() => {}); const loadingResponse = new Promise(() => {});
await createWrapper({ mutationResponse: loadingResponse }); await createWrapper({ mutationResponse: loadingResponse });
submitAgentToDelete(); await submitAgentToDelete();
}); });
it('reenables the options dropdown', async () => { it('reenables the options dropdown', async () => {
......
...@@ -118,7 +118,7 @@ describe('InstallAgentModal', () => { ...@@ -118,7 +118,7 @@ describe('InstallAgentModal', () => {
createWrapper(); createWrapper();
writeQuery(); writeQuery();
await wrapper.vm.$nextTick(); await waitForPromises();
wrapper.vm.setAgentName('agent-name'); wrapper.vm.setAgentName('agent-name');
findActionButton().vm.$emit('click'); findActionButton().vm.$emit('click');
...@@ -126,11 +126,12 @@ describe('InstallAgentModal', () => { ...@@ -126,11 +126,12 @@ describe('InstallAgentModal', () => {
return waitForPromises(); return waitForPromises();
}; };
beforeEach(() => { beforeEach(async () => {
apolloProvider = createMockApollo([ apolloProvider = createMockApollo([
[getAgentConfigurations, jest.fn().mockResolvedValue(apolloQueryResponse)], [getAgentConfigurations, jest.fn().mockResolvedValue(apolloQueryResponse)],
]); ]);
createWrapper(); createWrapper();
await waitForPromises();
trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
}); });
......
...@@ -5,6 +5,7 @@ import VueApollo, { ApolloMutation } from 'vue-apollo'; ...@@ -5,6 +5,7 @@ import VueApollo, { ApolloMutation } from 'vue-apollo';
import VueRouter from 'vue-router'; import VueRouter from 'vue-router';
import VueDraggable from 'vuedraggable'; import VueDraggable from 'vuedraggable';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { mockTracking, unmockTracking } from 'helpers/tracking_helper'; import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import permissionsQuery from 'shared_queries/design_management/design_permissions.query.graphql'; import permissionsQuery from 'shared_queries/design_management/design_permissions.query.graphql';
import getDesignListQuery from 'shared_queries/design_management/get_design_list.query.graphql'; import getDesignListQuery from 'shared_queries/design_management/get_design_list.query.graphql';
...@@ -115,8 +116,7 @@ describe('Design management index page', () => { ...@@ -115,8 +116,7 @@ describe('Design management index page', () => {
const findDesignToolbarWrapper = () => wrapper.find('[data-testid="design-toolbar-wrapper"]'); const findDesignToolbarWrapper = () => wrapper.find('[data-testid="design-toolbar-wrapper"]');
async function moveDesigns(localWrapper) { async function moveDesigns(localWrapper) {
await jest.runOnlyPendingTimers(); await waitForPromises();
await nextTick();
localWrapper.find(VueDraggable).vm.$emit('input', reorderedDesigns); localWrapper.find(VueDraggable).vm.$emit('input', reorderedDesigns);
localWrapper.find(VueDraggable).vm.$emit('change', { localWrapper.find(VueDraggable).vm.$emit('change', {
...@@ -746,9 +746,7 @@ describe('Design management index page', () => { ...@@ -746,9 +746,7 @@ describe('Design management index page', () => {
describe('with mocked Apollo client', () => { describe('with mocked Apollo client', () => {
it('has a design with id 1 as a first one', async () => { it('has a design with id 1 as a first one', async () => {
createComponentWithApollo({}); createComponentWithApollo({});
await waitForPromises();
await jest.runOnlyPendingTimers();
await nextTick();
expect(findDesigns()).toHaveLength(3); expect(findDesigns()).toHaveLength(3);
expect(findDesigns().at(0).props('id')).toBe('1'); expect(findDesigns().at(0).props('id')).toBe('1');
...@@ -773,9 +771,7 @@ describe('Design management index page', () => { ...@@ -773,9 +771,7 @@ describe('Design management index page', () => {
expect(draggableAttributes().disabled).toBe(true); expect(draggableAttributes().disabled).toBe(true);
await jest.runOnlyPendingTimers(); // kick off the mocked GQL stuff (promises) await waitForPromises();
await nextTick(); // kick off the DOM update
await nextTick(); // kick off the DOM update for finally block
expect(draggableAttributes().disabled).toBe(false); expect(draggableAttributes().disabled).toBe(false);
}); });
...@@ -787,7 +783,7 @@ describe('Design management index page', () => { ...@@ -787,7 +783,7 @@ describe('Design management index page', () => {
await moveDesigns(wrapper); await moveDesigns(wrapper);
await nextTick(); await waitForPromises();
expect(createFlash).toHaveBeenCalledWith({ message: 'Houston, we have a problem' }); expect(createFlash).toHaveBeenCalledWith({ message: 'Houston, we have a problem' });
}); });
...@@ -799,9 +795,7 @@ describe('Design management index page', () => { ...@@ -799,9 +795,7 @@ describe('Design management index page', () => {
await moveDesigns(wrapper); await moveDesigns(wrapper);
await nextTick(); // kick off the DOM update await waitForPromises();
await jest.runOnlyPendingTimers(); // kick off the mocked GQL stuff (promises)
await nextTick(); // kick off the DOM update for flash
expect(createFlash).toHaveBeenCalledWith({ expect(createFlash).toHaveBeenCalledWith({
message: 'Something went wrong when reordering designs. Please try again', message: 'Something went wrong when reordering designs. Please try again',
......
...@@ -131,9 +131,10 @@ describe('CE IssuesListApp component', () => { ...@@ -131,9 +131,10 @@ describe('CE IssuesListApp component', () => {
}); });
describe('IssuableList', () => { describe('IssuableList', () => {
beforeEach(() => { beforeEach(async () => {
wrapper = mountComponent(); wrapper = mountComponent();
jest.runOnlyPendingTimers(); jest.runOnlyPendingTimers();
await waitForPromises();
}); });
it('renders', () => { it('renders', () => {
...@@ -167,8 +168,9 @@ describe('CE IssuesListApp component', () => { ...@@ -167,8 +168,9 @@ describe('CE IssuesListApp component', () => {
}); });
describe('header action buttons', () => { describe('header action buttons', () => {
it('renders rss button', () => { it('renders rss button', async () => {
wrapper = mountComponent({ mountFn: mount }); wrapper = mountComponent({ mountFn: mount });
await waitForPromises();
expect(findGlButtonAt(0).props('icon')).toBe('rss'); expect(findGlButtonAt(0).props('icon')).toBe('rss');
expect(findGlButtonAt(0).attributes()).toMatchObject({ expect(findGlButtonAt(0).attributes()).toMatchObject({
...@@ -177,8 +179,9 @@ describe('CE IssuesListApp component', () => { ...@@ -177,8 +179,9 @@ describe('CE IssuesListApp component', () => {
}); });
}); });
it('renders calendar button', () => { it('renders calendar button', async () => {
wrapper = mountComponent({ mountFn: mount }); wrapper = mountComponent({ mountFn: mount });
await waitForPromises();
expect(findGlButtonAt(1).props('icon')).toBe('calendar'); expect(findGlButtonAt(1).props('icon')).toBe('calendar');
expect(findGlButtonAt(1).attributes()).toMatchObject({ expect(findGlButtonAt(1).attributes()).toMatchObject({
...@@ -191,12 +194,13 @@ describe('CE IssuesListApp component', () => { ...@@ -191,12 +194,13 @@ describe('CE IssuesListApp component', () => {
describe('when user is signed in', () => { describe('when user is signed in', () => {
const search = '?search=refactor&sort=created_date&state=opened'; const search = '?search=refactor&sort=created_date&state=opened';
beforeEach(() => { beforeEach(async () => {
setWindowLocation(search); setWindowLocation(search);
wrapper = mountComponent({ provide: { isSignedIn: true }, mountFn: mount }); wrapper = mountComponent({ provide: { isSignedIn: true }, mountFn: mount });
jest.runOnlyPendingTimers(); jest.runOnlyPendingTimers();
await waitForPromises();
}); });
it('renders', () => { it('renders', () => {
...@@ -585,11 +589,12 @@ describe('CE IssuesListApp component', () => { ...@@ -585,11 +589,12 @@ describe('CE IssuesListApp component', () => {
${'fetching issues'} | ${'issuesQueryResponse'} | ${IssuesListApp.i18n.errorFetchingIssues} ${'fetching issues'} | ${'issuesQueryResponse'} | ${IssuesListApp.i18n.errorFetchingIssues}
${'fetching issue counts'} | ${'issuesCountsQueryResponse'} | ${IssuesListApp.i18n.errorFetchingCounts} ${'fetching issue counts'} | ${'issuesCountsQueryResponse'} | ${IssuesListApp.i18n.errorFetchingCounts}
`('when there is an error $error', ({ mountOption, message }) => { `('when there is an error $error', ({ mountOption, message }) => {
beforeEach(() => { beforeEach(async () => {
wrapper = mountComponent({ wrapper = mountComponent({
[mountOption]: jest.fn().mockRejectedValue(new Error('ERROR')), [mountOption]: jest.fn().mockRejectedValue(new Error('ERROR')),
}); });
jest.runOnlyPendingTimers(); jest.runOnlyPendingTimers();
await waitForPromises();
}); });
it('shows an error message', () => { it('shows an error message', () => {
......
...@@ -2,9 +2,11 @@ import { GlDropdown, GlDropdownItem, GlSearchBoxByType } from '@gitlab/ui'; ...@@ -2,9 +2,11 @@ import { GlDropdown, GlDropdownItem, GlSearchBoxByType } from '@gitlab/ui';
import { createLocalVue, mount, shallowMount } from '@vue/test-utils'; import { createLocalVue, mount, shallowMount } from '@vue/test-utils';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper'; 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 NewIssueDropdown from '~/issues/list/components/new_issue_dropdown.vue';
import searchProjectsQuery from '~/issues/list/queries/search_projects.query.graphql'; import searchProjectsQuery from '~/issues/list/queries/search_projects.query.graphql';
import { DASH_SCOPE, joinPaths } from '~/lib/utils/url_utility'; import { DASH_SCOPE, joinPaths } from '~/lib/utils/url_utility';
import { DEBOUNCE_DELAY } from '~/vue_shared/components/filtered_search_bar/constants';
import { import {
emptySearchProjectsQueryResponse, emptySearchProjectsQueryResponse,
project1, project1,
...@@ -42,8 +44,9 @@ describe('NewIssueDropdown component', () => { ...@@ -42,8 +44,9 @@ describe('NewIssueDropdown component', () => {
const findInput = () => wrapper.findComponent(GlSearchBoxByType); const findInput = () => wrapper.findComponent(GlSearchBoxByType);
const showDropdown = async () => { const showDropdown = async () => {
findDropdown().vm.$emit('shown'); findDropdown().vm.$emit('shown');
await wrapper.vm.$apollo.queries.projects.refetch(); await waitForPromises();
jest.runOnlyPendingTimers(); jest.advanceTimersByTime(DEBOUNCE_DELAY);
await waitForPromises();
}; };
afterEach(() => { afterEach(() => {
...@@ -74,7 +77,6 @@ describe('NewIssueDropdown component', () => { ...@@ -74,7 +77,6 @@ describe('NewIssueDropdown component', () => {
it('renders projects with issues enabled', async () => { it('renders projects with issues enabled', async () => {
wrapper = mountComponent({ mountFn: mount }); wrapper = mountComponent({ mountFn: mount });
await showDropdown(); await showDropdown();
const listItems = wrapper.findAll('li'); const listItems = wrapper.findAll('li');
...@@ -112,10 +114,11 @@ describe('NewIssueDropdown component', () => { ...@@ -112,10 +114,11 @@ describe('NewIssueDropdown component', () => {
describe('when a project is selected', () => { describe('when a project is selected', () => {
beforeEach(async () => { beforeEach(async () => {
wrapper = mountComponent({ mountFn: mount }); wrapper = mountComponent({ mountFn: mount });
await waitForPromises();
await showDropdown(); await showDropdown();
wrapper.findComponent(GlDropdownItem).vm.$emit('click', project1); wrapper.findComponent(GlDropdownItem).vm.$emit('click', project1);
await waitForPromises();
}); });
it('dropdown button is a link', () => { it('dropdown button is a link', () => {
......
...@@ -81,10 +81,10 @@ describe('Bridge Show Page', () => { ...@@ -81,10 +81,10 @@ describe('Bridge Show Page', () => {
}); });
describe('after pipeline query is loaded', () => { describe('after pipeline query is loaded', () => {
beforeEach(() => { beforeEach(async () => {
mockPipelineQuery.mockResolvedValue(mockPipelineQueryResponse); mockPipelineQuery.mockResolvedValue(mockPipelineQueryResponse);
createComponentWithApollo(); createComponentWithApollo();
waitForPromises(); await waitForPromises();
}); });
it('query is called with correct variables', async () => { it('query is called with correct variables', async () => {
...@@ -109,10 +109,10 @@ describe('Bridge Show Page', () => { ...@@ -109,10 +109,10 @@ describe('Bridge Show Page', () => {
}); });
describe('sidebar expansion', () => { describe('sidebar expansion', () => {
beforeEach(() => { beforeEach(async () => {
mockPipelineQuery.mockResolvedValue(mockPipelineQueryResponse); mockPipelineQuery.mockResolvedValue(mockPipelineQueryResponse);
createComponentWithApollo(); createComponentWithApollo();
waitForPromises(); await waitForPromises();
}); });
describe('on resize', () => { describe('on resize', () => {
......
...@@ -112,7 +112,7 @@ describe('Job table app', () => { ...@@ -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 // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
...@@ -150,7 +150,7 @@ describe('Job table app', () => { ...@@ -150,7 +150,7 @@ describe('Job table app', () => {
}, },
}); });
await wrapper.vm.$nextTick(); await waitForPromises();
expect(findPrevious().exists()).toBe(true); expect(findPrevious().exists()).toBe(true);
expect(findPrevious().classes('disabled')).toBe(true); expect(findPrevious().classes('disabled')).toBe(true);
......
...@@ -108,6 +108,7 @@ describe('Tags List', () => { ...@@ -108,6 +108,7 @@ describe('Tags List', () => {
describe('events', () => { describe('events', () => {
it('prev-page fetch the previous page', async () => { it('prev-page fetch the previous page', async () => {
findRegistryList().vm.$emit('prev-page'); findRegistryList().vm.$emit('prev-page');
await waitForPromises();
expect(resolver).toHaveBeenCalledWith({ expect(resolver).toHaveBeenCalledWith({
first: null, first: null,
...@@ -119,8 +120,9 @@ describe('Tags List', () => { ...@@ -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'); findRegistryList().vm.$emit('next-page');
await waitForPromises();
expect(resolver).toHaveBeenCalledWith({ expect(resolver).toHaveBeenCalledWith({
after: tagsPageInfo.endCursor, after: tagsPageInfo.endCursor,
......
...@@ -522,7 +522,7 @@ describe('Details Page', () => { ...@@ -522,7 +522,7 @@ describe('Details Page', () => {
findDeleteImage().vm.$emit('start'); findDeleteImage().vm.$emit('start');
await nextTick(); await waitForPromises();
expect(findTagsLoader().exists()).toBe(true); expect(findTagsLoader().exists()).toBe(true);
......
...@@ -361,7 +361,7 @@ describe('List Page', () => { ...@@ -361,7 +361,7 @@ describe('List Page', () => {
findRegistrySearch().vm.$emit('filter:submit'); findRegistrySearch().vm.$emit('filter:submit');
await nextTick(); await waitForPromises();
}; };
it('has a search box element', async () => { it('has a search box element', async () => {
...@@ -429,7 +429,7 @@ describe('List Page', () => { ...@@ -429,7 +429,7 @@ describe('List Page', () => {
await waitForApolloRequestRender(); await waitForApolloRequestRender();
findImageList().vm.$emit('prev-page'); findImageList().vm.$emit('prev-page');
await nextTick(); await waitForPromises();
expect(resolver).toHaveBeenCalledWith( expect(resolver).toHaveBeenCalledWith(
expect.objectContaining({ before: pageInfo.startCursor }), expect.objectContaining({ before: pageInfo.startCursor }),
...@@ -449,7 +449,7 @@ describe('List Page', () => { ...@@ -449,7 +449,7 @@ describe('List Page', () => {
await waitForApolloRequestRender(); await waitForApolloRequestRender();
findImageList().vm.$emit('next-page'); findImageList().vm.$emit('next-page');
await nextTick(); await waitForPromises();
expect(resolver).toHaveBeenCalledWith( expect(resolver).toHaveBeenCalledWith(
expect.objectContaining({ after: pageInfo.endCursor }), expect.objectContaining({ after: pageInfo.endCursor }),
...@@ -471,8 +471,9 @@ describe('List Page', () => { ...@@ -471,8 +471,9 @@ describe('List Page', () => {
}); });
it('contains a description with the path of the item to delete', async () => { it('contains a description with the path of the item to delete', async () => {
await waitForPromises();
findImageList().vm.$emit('delete', { path: 'foo' }); findImageList().vm.$emit('delete', { path: 'foo' });
await nextTick(); await waitForPromises();
expect(findDeleteModal().html()).toContain('foo'); expect(findDeleteModal().html()).toContain('foo');
}); });
}); });
......
...@@ -195,8 +195,9 @@ describe('DependencyProxyApp', () => { ...@@ -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'); findManifestList().vm.$emit('prev-page');
await waitForPromises();
expect(resolver).toHaveBeenCalledWith({ expect(resolver).toHaveBeenCalledWith({
before: pagination().startCursor, before: pagination().startCursor,
...@@ -206,8 +207,9 @@ describe('DependencyProxyApp', () => { ...@@ -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'); findManifestList().vm.$emit('next-page');
await waitForPromises();
expect(resolver).toHaveBeenCalledWith({ expect(resolver).toHaveBeenCalledWith({
after: pagination().endCursor, after: pagination().endCursor,
......
...@@ -85,7 +85,7 @@ describe('PackagesListApp', () => { ...@@ -85,7 +85,7 @@ describe('PackagesListApp', () => {
wrapper.destroy(); wrapper.destroy();
}); });
const waitForFirstRequest = () => { const waitForFirstRequest = async () => {
// emit a search update so the query is executed // emit a search update so the query is executed
findSearch().vm.$emit('update', { sort: 'NAME_DESC', filters: [] }); findSearch().vm.$emit('update', { sort: 'NAME_DESC', filters: [] });
return waitForPromises(); return waitForPromises();
...@@ -149,11 +149,10 @@ describe('PackagesListApp', () => { ...@@ -149,11 +149,10 @@ describe('PackagesListApp', () => {
beforeEach(() => { beforeEach(() => {
resolver = jest.fn().mockResolvedValue(packagesListQuery()); resolver = jest.fn().mockResolvedValue(packagesListQuery());
mountComponent({ resolver }); 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({ expect(findListComponent().props()).toMatchObject({
list: expect.arrayContaining([expect.objectContaining({ id: packageData().id })]), list: expect.arrayContaining([expect.objectContaining({ id: packageData().id })]),
isLoading: false, isLoading: false,
...@@ -161,16 +160,20 @@ describe('PackagesListApp', () => { ...@@ -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'); findListComponent().vm.$emit('next-page');
await waitForPromises();
expect(resolver).toHaveBeenCalledWith( expect(resolver).toHaveBeenCalledWith(
expect.objectContaining({ after: pagination().endCursor, first: GRAPHQL_PAGE_SIZE }), 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'); findListComponent().vm.$emit('prev-page');
await waitForPromises();
expect(resolver).toHaveBeenCalledWith( expect(resolver).toHaveBeenCalledWith(
expect.objectContaining({ before: pagination().startCursor, last: GRAPHQL_PAGE_SIZE }), expect.objectContaining({ before: pagination().startCursor, last: GRAPHQL_PAGE_SIZE }),
......
...@@ -2,6 +2,7 @@ import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui'; ...@@ -2,6 +2,7 @@ import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils'; import { shallowMount, createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper'; 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 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 SettingsForm from '~/packages_and_registries/settings/project/components/settings_form.vue';
import { import {
...@@ -64,8 +65,6 @@ describe('Registry Settings App', () => { ...@@ -64,8 +65,6 @@ describe('Registry Settings App', () => {
localVue, localVue,
apolloProvider: fakeApollo, apolloProvider: fakeApollo,
}); });
return requestHandlers.map((request) => request[1]);
}; };
afterEach(() => { afterEach(() => {
...@@ -101,25 +100,25 @@ describe('Registry Settings App', () => { ...@@ -101,25 +100,25 @@ describe('Registry Settings App', () => {
${'response and changes'} | ${expirationPolicyPayload()} | ${{ ...containerExpirationPolicyData(), nameRegex: '12345' }} | ${true} ${'response and changes'} | ${expirationPolicyPayload()} | ${{ ...containerExpirationPolicyData(), nameRegex: '12345' }} | ${true}
${'response and empty'} | ${expirationPolicyPayload()} | ${{}} | ${true} ${'response and empty'} | ${expirationPolicyPayload()} | ${{}} | ${true}
`('$description', async ({ apiResponse, workingCopy, result }) => { `('$description', async ({ apiResponse, workingCopy, result }) => {
const requests = mountComponentWithApollo({ mountComponentWithApollo({
provide: { ...defaultProvidedValues, enableHistoricEntries: true }, provide: { ...defaultProvidedValues, enableHistoricEntries: true },
resolver: jest.fn().mockResolvedValue(apiResponse), resolver: jest.fn().mockResolvedValue(apiResponse),
}); });
await Promise.all(requests); await waitForPromises();
findSettingsComponent().vm.$emit('input', workingCopy); findSettingsComponent().vm.$emit('input', workingCopy);
await wrapper.vm.$nextTick(); await waitForPromises();
expect(findSettingsComponent().props('isEdited')).toBe(result); expect(findSettingsComponent().props('isEdited')).toBe(result);
}); });
}); });
it('renders the setting form', async () => { it('renders the setting form', async () => {
const requests = mountComponentWithApollo({ mountComponentWithApollo({
resolver: jest.fn().mockResolvedValue(expirationPolicyPayload()), resolver: jest.fn().mockResolvedValue(expirationPolicyPayload()),
}); });
await Promise.all(requests); await waitForPromises();
expect(findSettingsComponent().exists()).toBe(true); expect(findSettingsComponent().exists()).toBe(true);
}); });
...@@ -153,11 +152,11 @@ describe('Registry Settings App', () => { ...@@ -153,11 +152,11 @@ describe('Registry Settings App', () => {
}); });
describe('fetchSettingsError', () => { describe('fetchSettingsError', () => {
beforeEach(() => { beforeEach(async () => {
const requests = mountComponentWithApollo({ mountComponentWithApollo({
resolver: jest.fn().mockRejectedValue(new Error('GraphQL error')), resolver: jest.fn().mockRejectedValue(new Error('GraphQL error')),
}); });
return Promise.all(requests); await waitForPromises();
}); });
it('the form is hidden', () => { it('the form is hidden', () => {
...@@ -175,14 +174,14 @@ describe('Registry Settings App', () => { ...@@ -175,14 +174,14 @@ describe('Registry Settings App', () => {
${true} | ${true} ${true} | ${true}
${false} | ${false} ${false} | ${false}
`('is $isShown that the form is shown', async ({ enableHistoricEntries, isShown }) => { `('is $isShown that the form is shown', async ({ enableHistoricEntries, isShown }) => {
const requests = mountComponentWithApollo({ mountComponentWithApollo({
provide: { provide: {
...defaultProvidedValues, ...defaultProvidedValues,
enableHistoricEntries, enableHistoricEntries,
}, },
resolver: jest.fn().mockResolvedValue(emptyExpirationPolicyPayload()), resolver: jest.fn().mockResolvedValue(emptyExpirationPolicyPayload()),
}); });
await Promise.all(requests); await waitForPromises();
expect(findSettingsComponent().exists()).toBe(isShown); expect(findSettingsComponent().exists()).toBe(isShown);
}); });
......
...@@ -70,13 +70,13 @@ describe('Pipeline Status', () => { ...@@ -70,13 +70,13 @@ describe('Pipeline Status', () => {
describe('when querying data', () => { describe('when querying data', () => {
describe('when data is set', () => { describe('when data is set', () => {
beforeEach(() => { beforeEach(async () => {
mockPipelineQuery.mockResolvedValue({ mockPipelineQuery.mockResolvedValue({
data: { project: mockProjectPipeline() }, data: { project: mockProjectPipeline() },
}); });
createComponentWithApollo(); createComponentWithApollo();
waitForPromises(); await waitForPromises();
}); });
it('query is called with correct variables', async () => { it('query is called with correct variables', async () => {
......
import { shallowMount, createLocalVue } from '@vue/test-utils'; import { shallowMount, createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper'; 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 PipelineEditorMiniGraph from '~/pipeline_editor/components/header/pipeline_editor_mini_graph.vue';
import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_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'; import getLinkedPipelinesQuery from '~/projects/commit_box/info/graphql/queries/get_linked_pipelines.query.graphql';
...@@ -89,13 +90,14 @@ describe('Pipeline Status', () => { ...@@ -89,13 +90,14 @@ describe('Pipeline Status', () => {
}); });
describe('when query fails', () => { describe('when query fails', () => {
beforeEach(() => { beforeEach(async () => {
mockLinkedPipelinesQuery.mockRejectedValue(new Error()); mockLinkedPipelinesQuery.mockRejectedValue(new Error());
createComponentWithApollo(); createComponentWithApollo();
await waitForPromises();
}); });
it('should emit an error event when query fails', async () => { 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([ expect(wrapper.emitted('showError')[0]).toEqual([
{ {
type: PIPELINE_FAILURE, type: PIPELINE_FAILURE,
......
...@@ -80,10 +80,10 @@ describe('Jobs app', () => { ...@@ -80,10 +80,10 @@ describe('Jobs app', () => {
it('handles infinite scrolling by calling fetchMore', async () => { it('handles infinite scrolling by calling fetchMore', async () => {
createComponent(resolverSpy); createComponent(resolverSpy);
await waitForPromises(); await waitForPromises();
triggerInfiniteScroll(); triggerInfiniteScroll();
await waitForPromises();
expect(resolverSpy).toHaveBeenCalledWith({ expect(resolverSpy).toHaveBeenCalledWith({
after: 'eyJpZCI6Ijg0NyJ9', after: 'eyJpZCI6Ijg0NyJ9',
...@@ -96,10 +96,10 @@ describe('Jobs app', () => { ...@@ -96,10 +96,10 @@ describe('Jobs app', () => {
createComponent(resolverSpy); createComponent(resolverSpy);
expect(findSkeletonLoader().exists()).toBe(true); expect(findSkeletonLoader().exists()).toBe(true);
await waitForPromises(); await waitForPromises();
triggerInfiniteScroll(); triggerInfiniteScroll();
await waitForPromises();
expect(findSkeletonLoader().exists()).toBe(false); expect(findSkeletonLoader().exists()).toBe(false);
}); });
......
import { GlAlert, GlButton, GlButtonGroup, GlLoadingIcon } from '@gitlab/ui'; import { GlAlert, GlButton, GlButtonGroup, GlLoadingIcon } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils'; import { mount, shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import Vue, { nextTick } from 'vue'; import Vue from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import { useLocalStorageSpy } from 'helpers/local_storage_helper'; import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import createMockApollo from 'helpers/mock_apollo_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 getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql';
import getUserCallouts from '~/graphql_shared/queries/get_user_callouts.query.graphql'; import getUserCallouts from '~/graphql_shared/queries/get_user_callouts.query.graphql';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
...@@ -100,15 +101,6 @@ describe('Pipeline graph wrapper', () => { ...@@ -100,15 +101,6 @@ describe('Pipeline graph wrapper', () => {
wrapper.destroy(); wrapper.destroy();
}); });
beforeAll(() => {
jest.useFakeTimers();
});
afterAll(() => {
jest.runOnlyPendingTimers();
jest.useRealTimers();
});
describe('when data is loading', () => { describe('when data is loading', () => {
it('displays the loading icon', () => { it('displays the loading icon', () => {
createComponentWithApollo(); createComponentWithApollo();
...@@ -134,8 +126,7 @@ describe('Pipeline graph wrapper', () => { ...@@ -134,8 +126,7 @@ describe('Pipeline graph wrapper', () => {
describe('when data has loaded', () => { describe('when data has loaded', () => {
beforeEach(async () => { beforeEach(async () => {
createComponentWithApollo(); createComponentWithApollo();
jest.runOnlyPendingTimers(); await waitForPromises();
await nextTick();
}); });
it('does not display the loading icon', () => { it('does not display the loading icon', () => {
...@@ -163,8 +154,7 @@ describe('Pipeline graph wrapper', () => { ...@@ -163,8 +154,7 @@ describe('Pipeline graph wrapper', () => {
createComponentWithApollo({ createComponentWithApollo({
getPipelineDetailsHandler: jest.fn().mockRejectedValue(new Error('GraphQL error')), getPipelineDetailsHandler: jest.fn().mockRejectedValue(new Error('GraphQL error')),
}); });
jest.runOnlyPendingTimers(); await waitForPromises();
await nextTick();
}); });
it('does not display the loading icon', () => { it('does not display the loading icon', () => {
...@@ -187,8 +177,7 @@ describe('Pipeline graph wrapper', () => { ...@@ -187,8 +177,7 @@ describe('Pipeline graph wrapper', () => {
pipelineIid: '', pipelineIid: '',
}, },
}); });
jest.runOnlyPendingTimers(); await waitForPromises();
await nextTick();
}); });
it('does not display the loading icon', () => { it('does not display the loading icon', () => {
...@@ -210,7 +199,7 @@ describe('Pipeline graph wrapper', () => { ...@@ -210,7 +199,7 @@ describe('Pipeline graph wrapper', () => {
createComponentWithApollo(); createComponentWithApollo();
jest.spyOn(wrapper.vm.$apollo.queries.headerPipeline, 'refetch'); jest.spyOn(wrapper.vm.$apollo.queries.headerPipeline, 'refetch');
jest.spyOn(wrapper.vm.$apollo.queries.pipeline, 'refetch'); jest.spyOn(wrapper.vm.$apollo.queries.pipeline, 'refetch');
await nextTick(); await waitForPromises();
getGraph().vm.$emit('refreshPipelineGraph'); getGraph().vm.$emit('refreshPipelineGraph');
}); });
...@@ -224,8 +213,7 @@ describe('Pipeline graph wrapper', () => { ...@@ -224,8 +213,7 @@ describe('Pipeline graph wrapper', () => {
describe('when query times out', () => { describe('when query times out', () => {
const advanceApolloTimers = async () => { const advanceApolloTimers = async () => {
jest.runOnlyPendingTimers(); jest.runOnlyPendingTimers();
await nextTick(); await waitForPromises();
await nextTick();
}; };
beforeEach(async () => { beforeEach(async () => {
...@@ -245,7 +233,7 @@ describe('Pipeline graph wrapper', () => { ...@@ -245,7 +233,7 @@ describe('Pipeline graph wrapper', () => {
.mockResolvedValueOnce(errorData); .mockResolvedValueOnce(errorData);
createComponentWithApollo({ getPipelineDetailsHandler: failSucceedFail }); createComponentWithApollo({ getPipelineDetailsHandler: failSucceedFail });
await nextTick(); await waitForPromises();
}); });
it('shows correct errors and does not overwrite populated data when data is empty', async () => { it('shows correct errors and does not overwrite populated data when data is empty', async () => {
...@@ -274,8 +262,7 @@ describe('Pipeline graph wrapper', () => { ...@@ -274,8 +262,7 @@ describe('Pipeline graph wrapper', () => {
mountFn: mount, mountFn: mount,
}); });
jest.runOnlyPendingTimers(); await waitForPromises();
await nextTick();
}); });
it('appears when pipeline uses needs', () => { it('appears when pipeline uses needs', () => {
...@@ -318,7 +305,7 @@ describe('Pipeline graph wrapper', () => { ...@@ -318,7 +305,7 @@ describe('Pipeline graph wrapper', () => {
}); });
jest.runOnlyPendingTimers(); jest.runOnlyPendingTimers();
await nextTick(); await waitForPromises();
}); });
it('sets showLinks to true', async () => { it('sets showLinks to true', async () => {
...@@ -327,8 +314,9 @@ describe('Pipeline graph wrapper', () => { ...@@ -327,8 +314,9 @@ describe('Pipeline graph wrapper', () => {
expect(getLinksLayer().props('showLinks')).toBe(false); expect(getLinksLayer().props('showLinks')).toBe(false);
expect(getViewSelector().props('type')).toBe(LAYER_VIEW); expect(getViewSelector().props('type')).toBe(LAYER_VIEW);
await getDependenciesToggle().vm.$emit('change', true); await getDependenciesToggle().vm.$emit('change', true);
jest.runOnlyPendingTimers(); jest.runOnlyPendingTimers();
await nextTick(); await waitForPromises();
expect(wrapper.findComponent(LinksLayer).props('showLinks')).toBe(true); expect(wrapper.findComponent(LinksLayer).props('showLinks')).toBe(true);
}); });
}); });
...@@ -343,8 +331,7 @@ describe('Pipeline graph wrapper', () => { ...@@ -343,8 +331,7 @@ describe('Pipeline graph wrapper', () => {
mountFn: mount, mountFn: mount,
}); });
jest.runOnlyPendingTimers(); await waitForPromises();
await nextTick();
}); });
it('shows the hover tip in the view selector', async () => { it('shows the hover tip in the view selector', async () => {
...@@ -365,7 +352,7 @@ describe('Pipeline graph wrapper', () => { ...@@ -365,7 +352,7 @@ describe('Pipeline graph wrapper', () => {
}); });
jest.runOnlyPendingTimers(); jest.runOnlyPendingTimers();
await nextTick(); await waitForPromises();
}); });
it('does not show the hover tip', async () => { it('does not show the hover tip', async () => {
...@@ -382,8 +369,7 @@ describe('Pipeline graph wrapper', () => { ...@@ -382,8 +369,7 @@ describe('Pipeline graph wrapper', () => {
mountFn: mount, mountFn: mount,
}); });
jest.runOnlyPendingTimers(); await waitForPromises();
await nextTick();
}); });
afterEach(() => { afterEach(() => {
...@@ -411,8 +397,7 @@ describe('Pipeline graph wrapper', () => { ...@@ -411,8 +397,7 @@ describe('Pipeline graph wrapper', () => {
getPipelineDetailsHandler: jest.fn().mockResolvedValue(nonNeedsResponse), getPipelineDetailsHandler: jest.fn().mockResolvedValue(nonNeedsResponse),
}); });
jest.runOnlyPendingTimers(); await waitForPromises();
await nextTick();
}); });
afterEach(() => { afterEach(() => {
...@@ -435,7 +420,7 @@ describe('Pipeline graph wrapper', () => { ...@@ -435,7 +420,7 @@ describe('Pipeline graph wrapper', () => {
}); });
jest.runOnlyPendingTimers(); jest.runOnlyPendingTimers();
await nextTick(); await waitForPromises();
}); });
it('does not appear when pipeline does not use needs', () => { it('does not appear when pipeline does not use needs', () => {
...@@ -461,8 +446,7 @@ describe('Pipeline graph wrapper', () => { ...@@ -461,8 +446,7 @@ describe('Pipeline graph wrapper', () => {
describe('with no metrics path', () => { describe('with no metrics path', () => {
beforeEach(async () => { beforeEach(async () => {
createComponentWithApollo(); createComponentWithApollo();
jest.runOnlyPendingTimers(); await waitForPromises();
await nextTick();
}); });
it('is not called', () => { it('is not called', () => {
...@@ -505,8 +489,7 @@ describe('Pipeline graph wrapper', () => { ...@@ -505,8 +489,7 @@ describe('Pipeline graph wrapper', () => {
}, },
}); });
jest.runOnlyPendingTimers(); await waitForPromises();
await nextTick();
}); });
it('attempts to collect metrics', () => { it('attempts to collect metrics', () => {
...@@ -517,7 +500,7 @@ describe('Pipeline graph wrapper', () => { ...@@ -517,7 +500,7 @@ describe('Pipeline graph wrapper', () => {
}); });
describe('with duration and no error', () => { describe('with duration and no error', () => {
beforeEach(() => { beforeEach(async () => {
mock = new MockAdapter(axios); mock = new MockAdapter(axios);
mock.onPost(metricsPath).reply(200, {}); mock.onPost(metricsPath).reply(200, {});
...@@ -536,6 +519,7 @@ describe('Pipeline graph wrapper', () => { ...@@ -536,6 +519,7 @@ describe('Pipeline graph wrapper', () => {
currentViewType: LAYER_VIEW, currentViewType: LAYER_VIEW,
}, },
}); });
await waitForPromises();
}); });
afterEach(() => { afterEach(() => {
......
import { mount, shallowMount, createLocalVue } from '@vue/test-utils'; import { mount, shallowMount, createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_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 getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql';
import { import {
DOWNSTREAM, DOWNSTREAM,
...@@ -87,13 +88,12 @@ describe('Linked Pipelines Column', () => { ...@@ -87,13 +88,12 @@ describe('Linked Pipelines Column', () => {
describe('click action', () => { describe('click action', () => {
const clickExpandButton = async () => { const clickExpandButton = async () => {
await findExpandButton().trigger('click'); await findExpandButton().trigger('click');
await wrapper.vm.$nextTick(); await waitForPromises();
}; };
const clickExpandButtonAndAwaitTimers = async () => { const clickExpandButtonAndAwaitTimers = async () => {
await clickExpandButton(); await clickExpandButton();
jest.runOnlyPendingTimers(); jest.runOnlyPendingTimers();
await wrapper.vm.$nextTick();
}; };
describe('layer type rendering', () => { describe('layer type rendering', () => {
...@@ -162,7 +162,10 @@ describe('Linked Pipelines Column', () => { ...@@ -162,7 +162,10 @@ describe('Linked Pipelines Column', () => {
it('emits the error', async () => { it('emits the error', async () => {
await clickExpandButton(); 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 () => { it('does not show the pipeline', async () => {
...@@ -213,7 +216,10 @@ describe('Linked Pipelines Column', () => { ...@@ -213,7 +216,10 @@ describe('Linked Pipelines Column', () => {
it('emits the error', async () => { it('emits the error', async () => {
await clickExpandButton(); 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 () => { it('does not show the pipeline', async () => {
......
...@@ -2,6 +2,7 @@ import VueApollo from 'vue-apollo'; ...@@ -2,6 +2,7 @@ import VueApollo from 'vue-apollo';
import { createLocalVue, shallowMount } from '@vue/test-utils'; import { createLocalVue, shallowMount } from '@vue/test-utils';
import { GlAlert, GlSprintf } from '@gitlab/ui'; import { GlAlert, GlSprintf } from '@gitlab/ui';
import createMockApollo from 'helpers/mock_apollo_helper'; 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 DeprecatedTypeKeywordNotification from '~/pipelines/components/notification/deprecated_type_keyword_notification.vue';
import getPipelineWarnings from '~/pipelines/graphql/queries/get_pipeline_warnings.query.graphql'; import getPipelineWarnings from '~/pipelines/graphql/queries/get_pipeline_warnings.query.graphql';
import { import {
...@@ -77,9 +78,10 @@ describe('Deprecated keyword notification', () => { ...@@ -77,9 +78,10 @@ describe('Deprecated keyword notification', () => {
}); });
describe('if there is an error in the query', () => { describe('if there is an error in the query', () => {
beforeEach(() => { beforeEach(async () => {
mockWarnings.mockResolvedValue({ errors: ['It didnt work'] }); mockWarnings.mockResolvedValue({ errors: ['It didnt work'] });
wrapper = createComponentWithApollo(); wrapper = createComponentWithApollo();
await waitForPromises();
}); });
it('does not display the notification', () => { it('does not display the notification', () => {
...@@ -89,9 +91,10 @@ describe('Deprecated keyword notification', () => { ...@@ -89,9 +91,10 @@ describe('Deprecated keyword notification', () => {
describe('with a valid query result', () => { describe('with a valid query result', () => {
describe('if there are no deprecation warnings', () => { describe('if there are no deprecation warnings', () => {
beforeEach(() => { beforeEach(async () => {
mockWarnings.mockResolvedValue(mockWarningsWithoutDeprecation); mockWarnings.mockResolvedValue(mockWarningsWithoutDeprecation);
wrapper = createComponentWithApollo(); wrapper = createComponentWithApollo();
await waitForPromises();
}); });
it('does not show the notification', () => { it('does not show the notification', () => {
expect(findAlert().exists()).toBe(false); expect(findAlert().exists()).toBe(false);
...@@ -99,9 +102,10 @@ describe('Deprecated keyword notification', () => { ...@@ -99,9 +102,10 @@ describe('Deprecated keyword notification', () => {
}); });
describe('with a root type deprecation message', () => { describe('with a root type deprecation message', () => {
beforeEach(() => { beforeEach(async () => {
mockWarnings.mockResolvedValue(mockWarningsRootType); mockWarnings.mockResolvedValue(mockWarningsRootType);
wrapper = createComponentWithApollo(); wrapper = createComponentWithApollo();
await waitForPromises();
}); });
it('shows the notification with one item', () => { it('shows the notification with one item', () => {
expect(findAlert().exists()).toBe(true); expect(findAlert().exists()).toBe(true);
...@@ -111,9 +115,10 @@ describe('Deprecated keyword notification', () => { ...@@ -111,9 +115,10 @@ describe('Deprecated keyword notification', () => {
}); });
describe('with a job type deprecation message', () => { describe('with a job type deprecation message', () => {
beforeEach(() => { beforeEach(async () => {
mockWarnings.mockResolvedValue(mockWarningsType); mockWarnings.mockResolvedValue(mockWarningsType);
wrapper = createComponentWithApollo(); wrapper = createComponentWithApollo();
await waitForPromises();
}); });
it('shows the notification with one item', () => { it('shows the notification with one item', () => {
expect(findAlert().exists()).toBe(true); expect(findAlert().exists()).toBe(true);
...@@ -124,9 +129,10 @@ describe('Deprecated keyword notification', () => { ...@@ -124,9 +129,10 @@ describe('Deprecated keyword notification', () => {
}); });
describe('with both the root types and job type deprecation message', () => { describe('with both the root types and job type deprecation message', () => {
beforeEach(() => { beforeEach(async () => {
mockWarnings.mockResolvedValue(mockWarningsTypesAll); mockWarnings.mockResolvedValue(mockWarningsTypesAll);
wrapper = createComponentWithApollo(); wrapper = createComponentWithApollo();
await waitForPromises();
}); });
it('shows the notification with two items', () => { it('shows the notification with two items', () => {
expect(findAlert().exists()).toBe(true); expect(findAlert().exists()).toBe(true);
......
...@@ -9,6 +9,7 @@ import { mount, shallowMount } from '@vue/test-utils'; ...@@ -9,6 +9,7 @@ import { mount, shallowMount } from '@vue/test-utils';
import Vue from 'vue'; import Vue from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { mockTracking, unmockTracking } from 'helpers/tracking_helper'; import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import eventHub from '~/projects/new/event_hub'; import eventHub from '~/projects/new/event_hub';
...@@ -101,6 +102,7 @@ describe('NewProjectUrlSelect component', () => { ...@@ -101,6 +102,7 @@ describe('NewProjectUrlSelect component', () => {
findDropdown().vm.$emit('shown'); findDropdown().vm.$emit('shown');
await wrapper.vm.$apollo.queries.currentUser.refetch(); await wrapper.vm.$apollo.queries.currentUser.refetch();
jest.runOnlyPendingTimers(); jest.runOnlyPendingTimers();
await waitForPromises();
}; };
afterEach(() => { afterEach(() => {
...@@ -235,8 +237,7 @@ describe('NewProjectUrlSelect component', () => { ...@@ -235,8 +237,7 @@ describe('NewProjectUrlSelect component', () => {
}; };
wrapper = mountComponent({ search: 'no matches', queryResponse, mountFn: mount }); wrapper = mountComponent({ search: 'no matches', queryResponse, mountFn: mount });
jest.runOnlyPendingTimers(); await waitForPromises();
await wrapper.vm.$nextTick();
expect(wrapper.find('li').text()).toBe('No matches found'); expect(wrapper.find('li').text()).toBe('No matches found');
}); });
......
...@@ -2,6 +2,7 @@ import { GlColumnChart } from '@gitlab/ui/dist/charts'; ...@@ -2,6 +2,7 @@ import { GlColumnChart } from '@gitlab/ui/dist/charts';
import { createLocalVue, shallowMount } from '@vue/test-utils'; import { createLocalVue, shallowMount } from '@vue/test-utils';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper'; 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 PipelineCharts from '~/projects/pipelines/charts/components/pipeline_charts.vue';
import StatisticsList from '~/projects/pipelines/charts/components/statistics_list.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'; 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', () => { ...@@ -25,7 +26,7 @@ describe('~/projects/pipelines/charts/components/pipeline_charts.vue', () => {
return createMockApollo(requestHandlers); return createMockApollo(requestHandlers);
} }
beforeEach(() => { beforeEach(async () => {
wrapper = shallowMount(PipelineCharts, { wrapper = shallowMount(PipelineCharts, {
provide: { provide: {
projectPath, projectPath,
...@@ -33,6 +34,8 @@ describe('~/projects/pipelines/charts/components/pipeline_charts.vue', () => { ...@@ -33,6 +34,8 @@ describe('~/projects/pipelines/charts/components/pipeline_charts.vue', () => {
localVue, localVue,
apolloProvider: createMockApolloProvider(), apolloProvider: createMockApolloProvider(),
}); });
await waitForPromises();
}); });
afterEach(() => { afterEach(() => {
......
...@@ -4,6 +4,7 @@ import VueApollo from 'vue-apollo'; ...@@ -4,6 +4,7 @@ import VueApollo from 'vue-apollo';
import originalAllReleasesQueryResponse from 'test_fixtures/graphql/releases/graphql/queries/all_releases.query.graphql.json'; import originalAllReleasesQueryResponse from 'test_fixtures/graphql/releases/graphql/queries/all_releases.query.graphql.json';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_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 allReleasesQuery from 'shared_queries/releases/all_releases.query.graphql';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { historyPushState } from '~/lib/utils/common_utils'; import { historyPushState } from '~/lib/utils/common_utils';
...@@ -141,7 +142,8 @@ describe('app_index_apollo_client.vue', () => { ...@@ -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); expect(findLoadingIndicator().exists()).toBe(loadingIndicator);
}); });
......
...@@ -3,6 +3,7 @@ import Vue from 'vue'; ...@@ -3,6 +3,7 @@ import Vue from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import oneReleaseQueryResponse from 'test_fixtures/graphql/releases/graphql/queries/one_release.query.graphql.json'; import oneReleaseQueryResponse from 'test_fixtures/graphql/releases/graphql/queries/one_release.query.graphql.json';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash'; import createFlash from '~/flash';
import ReleaseShowApp from '~/releases/components/app_show.vue'; import ReleaseShowApp from '~/releases/components/app_show.vue';
import ReleaseBlock from '~/releases/components/release_block.vue'; import ReleaseBlock from '~/releases/components/release_block.vue';
...@@ -111,12 +112,13 @@ describe('Release show component', () => { ...@@ -111,12 +112,13 @@ describe('Release show component', () => {
}); });
describe('when the component has successfully loaded the release', () => { describe('when the component has successfully loaded the release', () => {
beforeEach(() => { beforeEach(async () => {
const apolloProvider = createMockApollo([ const apolloProvider = createMockApollo([
[oneReleaseQuery, jest.fn().mockResolvedValueOnce(oneReleaseQueryResponse)], [oneReleaseQuery, jest.fn().mockResolvedValueOnce(oneReleaseQueryResponse)],
]); ]);
createComponent({ apolloProvider }); createComponent({ apolloProvider });
await waitForPromises();
}); });
expectNoLoadingIndicator(); expectNoLoadingIndicator();
...@@ -125,12 +127,13 @@ describe('Release show component', () => { ...@@ -125,12 +127,13 @@ describe('Release show component', () => {
}); });
describe('when the request succeeded, but the returned "project" key was null', () => { describe('when the request succeeded, but the returned "project" key was null', () => {
beforeEach(() => { beforeEach(async () => {
const apolloProvider = createMockApollo([ const apolloProvider = createMockApollo([
[oneReleaseQuery, jest.fn().mockResolvedValueOnce({ data: { project: null } })], [oneReleaseQuery, jest.fn().mockResolvedValueOnce({ data: { project: null } })],
]); ]);
createComponent({ apolloProvider }); createComponent({ apolloProvider });
await waitForPromises();
}); });
expectNoLoadingIndicator(); expectNoLoadingIndicator();
...@@ -139,7 +142,7 @@ describe('Release show component', () => { ...@@ -139,7 +142,7 @@ describe('Release show component', () => {
}); });
describe('when the request succeeded, but the returned "project.release" key was null', () => { describe('when the request succeeded, but the returned "project.release" key was null', () => {
beforeEach(() => { beforeEach(async () => {
const apolloProvider = createMockApollo([ const apolloProvider = createMockApollo([
[ [
oneReleaseQuery, oneReleaseQuery,
...@@ -148,6 +151,7 @@ describe('Release show component', () => { ...@@ -148,6 +151,7 @@ describe('Release show component', () => {
]); ]);
createComponent({ apolloProvider }); createComponent({ apolloProvider });
await waitForPromises();
}); });
expectNoLoadingIndicator(); expectNoLoadingIndicator();
...@@ -156,12 +160,13 @@ describe('Release show component', () => { ...@@ -156,12 +160,13 @@ describe('Release show component', () => {
}); });
describe('when an error occurs while loading the release', () => { describe('when an error occurs while loading the release', () => {
beforeEach(() => { beforeEach(async () => {
const apolloProvider = createMockApollo([ const apolloProvider = createMockApollo([
[oneReleaseQuery, jest.fn().mockRejectedValueOnce('An error occurred!')], [oneReleaseQuery, jest.fn().mockRejectedValueOnce('An error occurred!')],
]); ]);
createComponent({ apolloProvider }); createComponent({ apolloProvider });
await waitForPromises();
}); });
expectNoLoadingIndicator(); expectNoLoadingIndicator();
......
...@@ -209,7 +209,7 @@ describe('Blob content viewer component', () => { ...@@ -209,7 +209,7 @@ describe('Blob content viewer component', () => {
it('does not render a BlobContent component if a Blob viewer is available', async () => { it('does not render a BlobContent component if a Blob viewer is available', async () => {
loadViewer.mockReturnValue(() => true); loadViewer.mockReturnValue(() => true);
await createComponent({ blob: richViewerMock }); await createComponent({ blob: richViewerMock });
await waitForPromises();
expect(findBlobContent().exists()).toBe(false); expect(findBlobContent().exists()).toBe(false);
}); });
...@@ -235,7 +235,7 @@ describe('Blob content viewer component', () => { ...@@ -235,7 +235,7 @@ describe('Blob content viewer component', () => {
}, },
}); });
await nextTick(); await waitForPromises();
expect(loadViewer).toHaveBeenCalledWith(viewer); expect(loadViewer).toHaveBeenCalledWith(viewer);
expect(wrapper.findComponent(loadViewerReturnValue).exists()).toBe(true); expect(wrapper.findComponent(loadViewerReturnValue).exists()).toBe(true);
......
...@@ -281,6 +281,7 @@ describe('AdminRunnersApp', () => { ...@@ -281,6 +281,7 @@ describe('AdminRunnersApp', () => {
}, },
}); });
createComponent(); createComponent();
await waitForPromises();
}); });
it('shows a message for no results', async () => { it('shows a message for no results', async () => {
...@@ -289,9 +290,10 @@ describe('AdminRunnersApp', () => { ...@@ -289,9 +290,10 @@ describe('AdminRunnersApp', () => {
}); });
describe('when runners query fails', () => { describe('when runners query fails', () => {
beforeEach(() => { beforeEach(async () => {
mockRunnersQuery = jest.fn().mockRejectedValue(new Error('Error!')); mockRunnersQuery = jest.fn().mockRejectedValue(new Error('Error!'));
createComponent(); createComponent();
await waitForPromises();
}); });
it('error is shown to the user', async () => { it('error is shown to the user', async () => {
......
...@@ -186,7 +186,8 @@ describe('RunnerTypeCell', () => { ...@@ -186,7 +186,8 @@ describe('RunnerTypeCell', () => {
beforeEach(async () => { beforeEach(async () => {
runnerActionsUpdateMutationHandler.mockRejectedValueOnce(new Error(mockErrorMsg)); runnerActionsUpdateMutationHandler.mockRejectedValueOnce(new Error(mockErrorMsg));
await findToggleActiveBtn().vm.$emit('click'); findToggleActiveBtn().vm.$emit('click');
await waitForPromises();
}); });
it('error is reported to sentry', () => { it('error is reported to sentry', () => {
...@@ -215,7 +216,8 @@ describe('RunnerTypeCell', () => { ...@@ -215,7 +216,8 @@ describe('RunnerTypeCell', () => {
}, },
}); });
await findToggleActiveBtn().vm.$emit('click'); findToggleActiveBtn().vm.$emit('click');
await waitForPromises();
}); });
it('error is reported to sentry', () => { it('error is reported to sentry', () => {
...@@ -305,8 +307,9 @@ describe('RunnerTypeCell', () => { ...@@ -305,8 +307,9 @@ describe('RunnerTypeCell', () => {
}); });
describe('When delete is clicked', () => { describe('When delete is clicked', () => {
beforeEach(() => { beforeEach(async () => {
findRunnerDeleteModal().vm.$emit('primary'); findRunnerDeleteModal().vm.$emit('primary');
await waitForPromises();
}); });
it('The delete mutation is called correctly', () => { it('The delete mutation is called correctly', () => {
...@@ -333,10 +336,11 @@ describe('RunnerTypeCell', () => { ...@@ -333,10 +336,11 @@ describe('RunnerTypeCell', () => {
describe('On a network error', () => { describe('On a network error', () => {
const mockErrorMsg = 'Delete error!'; const mockErrorMsg = 'Delete error!';
beforeEach(() => { beforeEach(async () => {
runnerDeleteMutationHandler.mockRejectedValueOnce(new Error(mockErrorMsg)); runnerDeleteMutationHandler.mockRejectedValueOnce(new Error(mockErrorMsg));
findRunnerDeleteModal().vm.$emit('primary'); findRunnerDeleteModal().vm.$emit('primary');
await waitForPromises();
}); });
it('error is reported to sentry', () => { it('error is reported to sentry', () => {
...@@ -359,7 +363,7 @@ describe('RunnerTypeCell', () => { ...@@ -359,7 +363,7 @@ describe('RunnerTypeCell', () => {
const mockErrorMsg = 'Runner not found!'; const mockErrorMsg = 'Runner not found!';
const mockErrorMsg2 = 'User not allowed!'; const mockErrorMsg2 = 'User not allowed!';
beforeEach(() => { beforeEach(async () => {
runnerDeleteMutationHandler.mockResolvedValue({ runnerDeleteMutationHandler.mockResolvedValue({
data: { data: {
runnerDelete: { runnerDelete: {
...@@ -369,6 +373,7 @@ describe('RunnerTypeCell', () => { ...@@ -369,6 +373,7 @@ describe('RunnerTypeCell', () => {
}); });
findRunnerDeleteModal().vm.$emit('primary'); findRunnerDeleteModal().vm.$emit('primary');
await waitForPromises();
}); });
it('error is reported to sentry', () => { it('error is reported to sentry', () => {
......
...@@ -82,7 +82,7 @@ describe('RegistrationDropdown', () => { ...@@ -82,7 +82,7 @@ describe('RegistrationDropdown', () => {
const findModalInBody = () => const findModalInBody = () =>
createWrapper(document.body).find('[data-testid="runner-instructions-modal"]'); createWrapper(document.body).find('[data-testid="runner-instructions-modal"]');
beforeEach(() => { beforeEach(async () => {
createComponent( createComponent(
{ {
localVue, localVue,
...@@ -94,7 +94,7 @@ describe('RegistrationDropdown', () => { ...@@ -94,7 +94,7 @@ describe('RegistrationDropdown', () => {
mountExtended, mountExtended,
); );
findRegistrationInstructionsDropdownItem().trigger('click'); await findRegistrationInstructionsDropdownItem().trigger('click');
}); });
afterEach(() => { afterEach(() => {
......
...@@ -218,6 +218,7 @@ describe('GroupRunnersApp', () => { ...@@ -218,6 +218,7 @@ describe('GroupRunnersApp', () => {
}, },
}); });
createComponent(); createComponent();
await waitForPromises();
}); });
it('shows a message for no results', async () => { it('shows a message for no results', async () => {
...@@ -226,9 +227,10 @@ describe('GroupRunnersApp', () => { ...@@ -226,9 +227,10 @@ describe('GroupRunnersApp', () => {
}); });
describe('when runners query fails', () => { describe('when runners query fails', () => {
beforeEach(() => { beforeEach(async () => {
mockGroupRunnersQuery = jest.fn().mockRejectedValue(new Error('Error!')); mockGroupRunnersQuery = jest.fn().mockRejectedValue(new Error('Error!'));
createComponent(); createComponent();
await waitForPromises();
}); });
it('error is shown to the user', async () => { it('error is shown to the user', async () => {
......
...@@ -322,9 +322,10 @@ describe('Sidebar assignees widget', () => { ...@@ -322,9 +322,10 @@ describe('Sidebar assignees widget', () => {
}); });
describe('when user is not signed in', () => { describe('when user is not signed in', () => {
beforeEach(() => { beforeEach(async () => {
gon.current_username = undefined; gon.current_username = undefined;
createComponent(); createComponent();
await waitForPromises();
}); });
it('passes signedIn prop as false to IssuableAssignees', () => { it('passes signedIn prop as false to IssuableAssignees', () => {
......
...@@ -77,8 +77,6 @@ describe('RunnerInstructionsModal component', () => { ...@@ -77,8 +77,6 @@ describe('RunnerInstructionsModal component', () => {
runnerSetupInstructionsHandler = jest.fn().mockResolvedValue(mockGraphqlInstructions); runnerSetupInstructionsHandler = jest.fn().mockResolvedValue(mockGraphqlInstructions);
createComponent(); createComponent();
await nextTick();
}); });
afterEach(() => { afterEach(() => {
...@@ -113,13 +111,15 @@ describe('RunnerInstructionsModal component', () => { ...@@ -113,13 +111,15 @@ describe('RunnerInstructionsModal component', () => {
}); });
}); });
it('binary instructions are shown', () => { it('binary instructions are shown', async () => {
await waitForPromises();
const instructions = findBinaryInstructions().text(); const instructions = findBinaryInstructions().text();
expect(instructions).toBe(installInstructions); 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(); const instructions = findRegisterCommand().text();
expect(instructions).toBe( expect(instructions).toBe(
...@@ -130,7 +130,7 @@ describe('RunnerInstructionsModal component', () => { ...@@ -130,7 +130,7 @@ describe('RunnerInstructionsModal component', () => {
describe('when a register token is not shown', () => { describe('when a register token is not shown', () => {
beforeEach(async () => { beforeEach(async () => {
createComponent({ props: { registrationToken: undefined } }); createComponent({ props: { registrationToken: undefined } });
await nextTick(); await waitForPromises();
}); });
it('register command is shown without a defined registration token', () => { it('register command is shown without a defined registration token', () => {
...@@ -198,16 +198,16 @@ describe('RunnerInstructionsModal component', () => { ...@@ -198,16 +198,16 @@ describe('RunnerInstructionsModal component', () => {
expect(findSkeletonLoader().exists()).toBe(true); expect(findSkeletonLoader().exists()).toBe(true);
expect(findGlLoadingIcon().exists()).toBe(false); expect(findGlLoadingIcon().exists()).toBe(false);
await nextTick(); // wait for platforms await nextTick();
await jest.runOnlyPendingTimers();
await nextTick();
expect(findGlLoadingIcon().exists()).toBe(true); expect(findGlLoadingIcon().exists()).toBe(true);
}); });
it('once loaded, should not show a loading state', async () => { it('once loaded, should not show a loading state', async () => {
createComponent(); createComponent();
await waitForPromises();
await nextTick(); // wait for platforms
await nextTick(); // wait for architectures
expect(findSkeletonLoader().exists()).toBe(false); expect(findSkeletonLoader().exists()).toBe(false);
expect(findGlLoadingIcon().exists()).toBe(false); expect(findGlLoadingIcon().exists()).toBe(false);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment