Commit b568065f authored by Vitaly Slobodin's avatar Vitaly Slobodin

Merge branch 'ntepluhina-backport-unit-tests' into 'master'

Backport unit tests changes from Apollo migration MR

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