summaryrefslogtreecommitdiff
path: root/spec/frontend/environments
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-12-20 13:37:47 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-12-20 13:37:47 +0000
commitaee0a117a889461ce8ced6fcf73207fe017f1d99 (patch)
tree891d9ef189227a8445d83f35c1b0fc99573f4380 /spec/frontend/environments
parent8d46af3258650d305f53b819eabf7ab18d22f59e (diff)
downloadgitlab-ce-aee0a117a889461ce8ced6fcf73207fe017f1d99.tar.gz
Add latest changes from gitlab-org/gitlab@14-6-stable-eev14.6.0-rc42
Diffstat (limited to 'spec/frontend/environments')
-rw-r--r--spec/frontend/environments/confirm_rollback_modal_spec.js134
-rw-r--r--spec/frontend/environments/delete_environment_modal_spec.js64
-rw-r--r--spec/frontend/environments/enable_review_app_modal_spec.js15
-rw-r--r--spec/frontend/environments/environment_delete_spec.js64
-rw-r--r--spec/frontend/environments/environment_rollback_spec.js29
-rw-r--r--spec/frontend/environments/graphql/mock_data.js27
-rw-r--r--spec/frontend/environments/graphql/resolvers_spec.js136
-rw-r--r--spec/frontend/environments/new_environment_folder_spec.js7
-rw-r--r--spec/frontend/environments/new_environments_app_spec.js243
9 files changed, 683 insertions, 36 deletions
diff --git a/spec/frontend/environments/confirm_rollback_modal_spec.js b/spec/frontend/environments/confirm_rollback_modal_spec.js
index d62aaec4f69..b699f953945 100644
--- a/spec/frontend/environments/confirm_rollback_modal_spec.js
+++ b/spec/frontend/environments/confirm_rollback_modal_spec.js
@@ -1,6 +1,9 @@
import { GlModal, GlSprintf } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
+import Vue, { nextTick } from 'vue';
+import VueApollo from 'vue-apollo';
import ConfirmRollbackModal from '~/environments/components/confirm_rollback_modal.vue';
+import createMockApollo from 'helpers/mock_apollo_helper';
import eventHub from '~/environments/event_hub';
describe('Confirm Rollback Modal Component', () => {
@@ -17,6 +20,17 @@ describe('Confirm Rollback Modal Component', () => {
modalId: 'test',
};
+ const envWithLastDeploymentGraphql = {
+ name: 'test',
+ lastDeployment: {
+ commit: {
+ shortId: 'abc0123',
+ },
+ 'last?': true,
+ },
+ modalId: 'test',
+ };
+
const envWithoutLastDeployment = {
name: 'test',
modalId: 'test',
@@ -26,7 +40,7 @@ describe('Confirm Rollback Modal Component', () => {
const retryPath = 'test/-/jobs/123/retry';
- const createComponent = (props = {}) => {
+ const createComponent = (props = {}, options = {}) => {
component = shallowMount(ConfirmRollbackModal, {
propsData: {
...props,
@@ -34,6 +48,7 @@ describe('Confirm Rollback Modal Component', () => {
stubs: {
GlSprintf,
},
+ ...options,
});
};
@@ -101,4 +116,121 @@ describe('Confirm Rollback Modal Component', () => {
});
},
);
+
+ describe('graphql', () => {
+ describe.each`
+ hasMultipleCommits | environmentData | retryUrl | primaryPropsAttrs
+ ${true} | ${envWithLastDeploymentGraphql} | ${null} | ${[{ variant: 'danger' }]}
+ ${false} | ${envWithoutLastDeployment} | ${retryPath} | ${[{ variant: 'danger' }, { 'data-method': 'post' }, { href: retryPath }]}
+ `(
+ 'when hasMultipleCommits=$hasMultipleCommits',
+ ({ hasMultipleCommits, environmentData, retryUrl, primaryPropsAttrs }) => {
+ Vue.use(VueApollo);
+
+ let apolloProvider;
+ let rollbackResolver;
+
+ beforeEach(() => {
+ rollbackResolver = jest.fn();
+ apolloProvider = createMockApollo([], {
+ Mutation: { rollbackEnvironment: rollbackResolver },
+ });
+ environment = environmentData;
+ });
+
+ it('should set contain the commit hash and ask for confirmation', () => {
+ createComponent(
+ {
+ environment: {
+ ...environment,
+ lastDeployment: {
+ ...environment.lastDeployment,
+ 'last?': false,
+ },
+ },
+ hasMultipleCommits,
+ retryUrl,
+ graphql: true,
+ },
+ { apolloProvider },
+ );
+ const modal = component.find(GlModal);
+
+ expect(modal.text()).toContain('commit abc0123');
+ expect(modal.text()).toContain('Are you sure you want to continue?');
+ });
+
+ it('should show "Rollback" when isLastDeployment is false', () => {
+ createComponent(
+ {
+ environment: {
+ ...environment,
+ lastDeployment: {
+ ...environment.lastDeployment,
+ 'last?': false,
+ },
+ },
+ hasMultipleCommits,
+ retryUrl,
+ graphql: true,
+ },
+ { apolloProvider },
+ );
+ const modal = component.find(GlModal);
+
+ expect(modal.attributes('title')).toContain('Rollback');
+ expect(modal.attributes('title')).toContain('test');
+ expect(modal.props('actionPrimary').text).toBe('Rollback');
+ expect(modal.props('actionPrimary').attributes).toEqual(primaryPropsAttrs);
+ });
+
+ it('should show "Re-deploy" when isLastDeployment is true', () => {
+ createComponent(
+ {
+ environment: {
+ ...environment,
+ lastDeployment: {
+ ...environment.lastDeployment,
+ 'last?': true,
+ },
+ },
+ hasMultipleCommits,
+ graphql: true,
+ },
+ { apolloProvider },
+ );
+
+ const modal = component.find(GlModal);
+
+ expect(modal.attributes('title')).toContain('Re-deploy');
+ expect(modal.attributes('title')).toContain('test');
+ expect(modal.props('actionPrimary').text).toBe('Re-deploy');
+ });
+
+ it('should commit the "rollback" mutation when "ok" is clicked', async () => {
+ const env = { ...environmentData, isLastDeployment: true };
+
+ createComponent(
+ {
+ environment: env,
+ hasMultipleCommits,
+ graphql: true,
+ },
+ { apolloProvider },
+ );
+
+ const modal = component.find(GlModal);
+ modal.vm.$emit('ok');
+
+ await nextTick();
+ expect(rollbackResolver).toHaveBeenCalledWith(
+ expect.anything(),
+ { environment: env },
+ expect.anything(),
+ expect.anything(),
+ );
+ });
+ },
+ );
+ });
});
diff --git a/spec/frontend/environments/delete_environment_modal_spec.js b/spec/frontend/environments/delete_environment_modal_spec.js
new file mode 100644
index 00000000000..50c4ca00009
--- /dev/null
+++ b/spec/frontend/environments/delete_environment_modal_spec.js
@@ -0,0 +1,64 @@
+import { GlModal } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import Vue, { nextTick } from 'vue';
+import VueApollo from 'vue-apollo';
+import { s__, sprintf } from '~/locale';
+import DeleteEnvironmentModal from '~/environments/components/delete_environment_modal.vue';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import { resolvedEnvironment } from './graphql/mock_data';
+
+Vue.use(VueApollo);
+
+describe('~/environments/components/delete_environment_modal.vue', () => {
+ let mockApollo;
+ let deleteResolver;
+ let wrapper;
+
+ const createComponent = ({ props = {}, apolloProvider } = {}) => {
+ wrapper = shallowMount(DeleteEnvironmentModal, {
+ propsData: {
+ graphql: true,
+ environment: resolvedEnvironment,
+ ...props,
+ },
+ apolloProvider,
+ });
+ };
+
+ beforeEach(() => {
+ deleteResolver = jest.fn();
+ mockApollo = createMockApollo([], {
+ Mutation: { deleteEnvironment: deleteResolver },
+ });
+ });
+
+ it('should confirm the environment to delete', () => {
+ createComponent({ apolloProvider: mockApollo });
+
+ expect(wrapper.text()).toBe(
+ sprintf(
+ s__(
+ `Environments|Deleting the '%{environmentName}' environment cannot be undone. Do you want to delete it anyway?`,
+ ),
+ {
+ environmentName: resolvedEnvironment.name,
+ },
+ ),
+ );
+ });
+
+ it('should send the delete mutation on primary', async () => {
+ createComponent({ apolloProvider: mockApollo });
+
+ wrapper.findComponent(GlModal).vm.$emit('primary');
+
+ await nextTick();
+
+ expect(deleteResolver).toHaveBeenCalledWith(
+ expect.anything(),
+ { environment: resolvedEnvironment },
+ expect.anything(),
+ expect.anything(),
+ );
+ });
+});
diff --git a/spec/frontend/environments/enable_review_app_modal_spec.js b/spec/frontend/environments/enable_review_app_modal_spec.js
index 9a3f13f19d5..17ae10a2884 100644
--- a/spec/frontend/environments/enable_review_app_modal_spec.js
+++ b/spec/frontend/environments/enable_review_app_modal_spec.js
@@ -1,10 +1,12 @@
import { shallowMount } from '@vue/test-utils';
+import { GlModal } from '@gitlab/ui';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import EnableReviewAppButton from '~/environments/components/enable_review_app_modal.vue';
import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue';
describe('Enable Review App Button', () => {
let wrapper;
+ let modal;
afterEach(() => {
wrapper.destroy();
@@ -16,12 +18,15 @@ describe('Enable Review App Button', () => {
shallowMount(EnableReviewAppButton, {
propsData: {
modalId: 'fake-id',
+ visible: true,
},
provide: {
defaultBranchName: 'main',
},
}),
);
+
+ modal = wrapper.findComponent(GlModal);
});
it('renders the defaultBranchName copy', () => {
@@ -32,5 +37,15 @@ describe('Enable Review App Button', () => {
it('renders the copyToClipboard button', () => {
expect(wrapper.findComponent(ModalCopyButton).exists()).toBe(true);
});
+
+ it('emits change events from the modal up', () => {
+ modal.vm.$emit('change', false);
+
+ expect(wrapper.emitted('change')).toEqual([[false]]);
+ });
+
+ it('passes visible to the modal', () => {
+ expect(modal.props('visible')).toBe(true);
+ });
});
});
diff --git a/spec/frontend/environments/environment_delete_spec.js b/spec/frontend/environments/environment_delete_spec.js
index 2d8cff0c74a..057cb9858c4 100644
--- a/spec/frontend/environments/environment_delete_spec.js
+++ b/spec/frontend/environments/environment_delete_spec.js
@@ -1,37 +1,71 @@
import { GlDropdownItem } from '@gitlab/ui';
-
import { shallowMount } from '@vue/test-utils';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import setEnvironmentToDelete from '~/environments/graphql/mutations/set_environment_to_delete.mutation.graphql';
import DeleteComponent from '~/environments/components/environment_delete.vue';
import eventHub from '~/environments/event_hub';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import { resolvedEnvironment } from './graphql/mock_data';
describe('External URL Component', () => {
let wrapper;
- const createWrapper = () => {
+ const createWrapper = (props = {}, options = {}) => {
wrapper = shallowMount(DeleteComponent, {
+ ...options,
propsData: {
- environment: {},
+ environment: resolvedEnvironment,
+ ...props,
},
});
};
const findDropdownItem = () => wrapper.find(GlDropdownItem);
- beforeEach(() => {
- jest.spyOn(window, 'confirm');
+ describe('event hub', () => {
+ beforeEach(() => {
+ createWrapper();
+ });
- createWrapper();
- });
+ it('should render a dropdown item to delete the environment', () => {
+ expect(findDropdownItem().exists()).toBe(true);
+ expect(wrapper.text()).toEqual('Delete environment');
+ expect(findDropdownItem().attributes('variant')).toBe('danger');
+ });
- it('should render a dropdown item to delete the environment', () => {
- expect(findDropdownItem().exists()).toBe(true);
- expect(wrapper.text()).toEqual('Delete environment');
- expect(findDropdownItem().attributes('variant')).toBe('danger');
+ it('emits requestDeleteEnvironment in the event hub when button is clicked', () => {
+ jest.spyOn(eventHub, '$emit');
+ findDropdownItem().vm.$emit('click');
+ expect(eventHub.$emit).toHaveBeenCalledWith('requestDeleteEnvironment', resolvedEnvironment);
+ });
});
- it('emits requestDeleteEnvironment in the event hub when button is clicked', () => {
- jest.spyOn(eventHub, '$emit');
- findDropdownItem().vm.$emit('click');
- expect(eventHub.$emit).toHaveBeenCalledWith('requestDeleteEnvironment', wrapper.vm.environment);
+ describe('graphql', () => {
+ Vue.use(VueApollo);
+ let mockApollo;
+
+ beforeEach(() => {
+ mockApollo = createMockApollo();
+ createWrapper(
+ { graphql: true, environment: resolvedEnvironment },
+ { apolloProvider: mockApollo },
+ );
+ });
+
+ it('should render a dropdown item to delete the environment', () => {
+ expect(findDropdownItem().exists()).toBe(true);
+ expect(wrapper.text()).toEqual('Delete environment');
+ expect(findDropdownItem().attributes('variant')).toBe('danger');
+ });
+
+ it('emits requestDeleteEnvironment in the event hub when button is clicked', () => {
+ jest.spyOn(mockApollo.defaultClient, 'mutate');
+ findDropdownItem().vm.$emit('click');
+ expect(mockApollo.defaultClient.mutate).toHaveBeenCalledWith({
+ mutation: setEnvironmentToDelete,
+ variables: { environment: resolvedEnvironment },
+ });
+ });
});
});
diff --git a/spec/frontend/environments/environment_rollback_spec.js b/spec/frontend/environments/environment_rollback_spec.js
index cde675cd9e7..7eff46baaf7 100644
--- a/spec/frontend/environments/environment_rollback_spec.js
+++ b/spec/frontend/environments/environment_rollback_spec.js
@@ -1,7 +1,11 @@
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
import { GlDropdownItem } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import RollbackComponent from '~/environments/components/environment_rollback.vue';
import eventHub from '~/environments/event_hub';
+import setEnvironmentToRollback from '~/environments/graphql/mutations/set_environment_to_rollback.mutation.graphql';
+import createMockApollo from 'helpers/mock_apollo_helper';
describe('Rollback Component', () => {
const retryUrl = 'https://gitlab.com/retry';
@@ -50,4 +54,29 @@ describe('Rollback Component', () => {
name: 'test',
});
});
+
+ it('should trigger a graphql mutation when graphql is enabled', () => {
+ Vue.use(VueApollo);
+
+ const apolloProvider = createMockApollo();
+ jest.spyOn(apolloProvider.defaultClient, 'mutate');
+ const environment = {
+ name: 'test',
+ };
+ const wrapper = shallowMount(RollbackComponent, {
+ propsData: {
+ retryUrl,
+ graphql: true,
+ environment,
+ },
+ apolloProvider,
+ });
+ const button = wrapper.find(GlDropdownItem);
+ button.vm.$emit('click');
+
+ expect(apolloProvider.defaultClient.mutate).toHaveBeenCalledWith({
+ mutation: setEnvironmentToRollback,
+ variables: { environment },
+ });
+ });
});
diff --git a/spec/frontend/environments/graphql/mock_data.js b/spec/frontend/environments/graphql/mock_data.js
index e56b6448b7d..e75d3ac0321 100644
--- a/spec/frontend/environments/graphql/mock_data.js
+++ b/spec/frontend/environments/graphql/mock_data.js
@@ -469,6 +469,33 @@ export const folder = {
stopped_count: 0,
};
+export const resolvedEnvironment = {
+ id: 41,
+ globalId: 'gid://gitlab/Environment/41',
+ name: 'review/hello',
+ state: 'available',
+ externalUrl: 'https://example.org',
+ environmentType: 'review',
+ nameWithoutType: 'hello',
+ lastDeployment: null,
+ hasStopAction: false,
+ rolloutStatus: null,
+ environmentPath: '/h5bp/html5-boilerplate/-/environments/41',
+ stopPath: '/h5bp/html5-boilerplate/-/environments/41/stop',
+ cancelAutoStopPath: '/h5bp/html5-boilerplate/-/environments/41/cancel_auto_stop',
+ deletePath: '/api/v4/projects/8/environments/41',
+ folderPath: '/h5bp/html5-boilerplate/-/environments/folders/review',
+ createdAt: '2021-10-04T19:27:00.527Z',
+ updatedAt: '2021-10-04T19:27:00.527Z',
+ canStop: true,
+ logsPath: '/h5bp/html5-boilerplate/-/logs?environment_name=review%2Fhello',
+ logsApiPath: '/h5bp/html5-boilerplate/-/logs/k8s.json?environment_name=review%2Fhello',
+ enableAdvancedLogsQuerying: false,
+ canDelete: false,
+ hasOpenedAlert: false,
+ __typename: 'LocalEnvironment',
+};
+
export const resolvedFolder = {
availableCount: 2,
environments: [
diff --git a/spec/frontend/environments/graphql/resolvers_spec.js b/spec/frontend/environments/graphql/resolvers_spec.js
index 4d2a0818996..d8d26b74504 100644
--- a/spec/frontend/environments/graphql/resolvers_spec.js
+++ b/spec/frontend/environments/graphql/resolvers_spec.js
@@ -1,18 +1,33 @@
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import { resolvers } from '~/environments/graphql/resolvers';
+import environmentToRollback from '~/environments/graphql/queries/environment_to_rollback.query.graphql';
+import environmentToDelete from '~/environments/graphql/queries/environment_to_delete.query.graphql';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import pollIntervalQuery from '~/environments/graphql/queries/poll_interval.query.graphql';
+import pageInfoQuery from '~/environments/graphql/queries/page_info.query.graphql';
import { TEST_HOST } from 'helpers/test_constants';
-import { environmentsApp, resolvedEnvironmentsApp, folder, resolvedFolder } from './mock_data';
+import {
+ environmentsApp,
+ resolvedEnvironmentsApp,
+ resolvedEnvironment,
+ folder,
+ resolvedFolder,
+} from './mock_data';
const ENDPOINT = `${TEST_HOST}/environments`;
describe('~/frontend/environments/graphql/resolvers', () => {
let mockResolvers;
let mock;
+ let mockApollo;
+ let localState;
beforeEach(() => {
mockResolvers = resolvers(ENDPOINT);
mock = new MockAdapter(axios);
+ mockApollo = createMockApollo();
+ localState = mockApollo.defaultClient.localState;
});
afterEach(() => {
@@ -21,10 +36,87 @@ describe('~/frontend/environments/graphql/resolvers', () => {
describe('environmentApp', () => {
it('should fetch environments and map them to frontend data', async () => {
- mock.onGet(ENDPOINT, { params: { nested: true } }).reply(200, environmentsApp);
+ const cache = { writeQuery: jest.fn() };
+ const scope = 'available';
+ mock
+ .onGet(ENDPOINT, { params: { nested: true, scope, page: 1 } })
+ .reply(200, environmentsApp, {});
- const app = await mockResolvers.Query.environmentApp();
+ const app = await mockResolvers.Query.environmentApp(null, { scope, page: 1 }, { cache });
expect(app).toEqual(resolvedEnvironmentsApp);
+ expect(cache.writeQuery).toHaveBeenCalledWith({
+ query: pollIntervalQuery,
+ data: { interval: undefined },
+ });
+ });
+ it('should set the poll interval when there is one', async () => {
+ const cache = { writeQuery: jest.fn() };
+ const scope = 'stopped';
+ const interval = 3000;
+ mock
+ .onGet(ENDPOINT, { params: { nested: true, scope, page: 1 } })
+ .reply(200, environmentsApp, {
+ 'poll-interval': interval,
+ });
+
+ await mockResolvers.Query.environmentApp(null, { scope, page: 1 }, { cache });
+ expect(cache.writeQuery).toHaveBeenCalledWith({
+ query: pollIntervalQuery,
+ data: { interval },
+ });
+ });
+ it('should set page info if there is any', async () => {
+ const cache = { writeQuery: jest.fn() };
+ const scope = 'stopped';
+ mock
+ .onGet(ENDPOINT, { params: { nested: true, scope, page: 1 } })
+ .reply(200, environmentsApp, {
+ 'x-next-page': '2',
+ 'x-page': '1',
+ 'X-Per-Page': '2',
+ 'X-Prev-Page': '',
+ 'X-TOTAL': '37',
+ 'X-Total-Pages': '5',
+ });
+
+ await mockResolvers.Query.environmentApp(null, { scope, page: 1 }, { cache });
+ expect(cache.writeQuery).toHaveBeenCalledWith({
+ query: pageInfoQuery,
+ data: {
+ pageInfo: {
+ total: 37,
+ perPage: 2,
+ previousPage: NaN,
+ totalPages: 5,
+ nextPage: 2,
+ page: 1,
+ __typename: 'LocalPageInfo',
+ },
+ },
+ });
+ });
+ it('should not set page info if there is none', async () => {
+ const cache = { writeQuery: jest.fn() };
+ const scope = 'stopped';
+ mock
+ .onGet(ENDPOINT, { params: { nested: true, scope, page: 1 } })
+ .reply(200, environmentsApp, {});
+
+ await mockResolvers.Query.environmentApp(null, { scope, page: 1 }, { cache });
+ expect(cache.writeQuery).toHaveBeenCalledWith({
+ query: pageInfoQuery,
+ data: {
+ pageInfo: {
+ __typename: 'LocalPageInfo',
+ nextPage: NaN,
+ page: NaN,
+ perPage: NaN,
+ previousPage: NaN,
+ total: NaN,
+ totalPages: NaN,
+ },
+ },
+ });
});
});
describe('folder', () => {
@@ -42,7 +134,7 @@ describe('~/frontend/environments/graphql/resolvers', () => {
it('should post to the stop environment path', async () => {
mock.onPost(ENDPOINT).reply(200);
- await mockResolvers.Mutations.stopEnvironment(null, { environment: { stopPath: ENDPOINT } });
+ await mockResolvers.Mutation.stopEnvironment(null, { environment: { stopPath: ENDPOINT } });
expect(mock.history.post).toContainEqual(
expect.objectContaining({ url: ENDPOINT, method: 'post' }),
@@ -53,7 +145,7 @@ describe('~/frontend/environments/graphql/resolvers', () => {
it('should post to the retry environment path', async () => {
mock.onPost(ENDPOINT).reply(200);
- await mockResolvers.Mutations.rollbackEnvironment(null, {
+ await mockResolvers.Mutation.rollbackEnvironment(null, {
environment: { retryUrl: ENDPOINT },
});
@@ -66,7 +158,7 @@ describe('~/frontend/environments/graphql/resolvers', () => {
it('should DELETE to the delete environment path', async () => {
mock.onDelete(ENDPOINT).reply(200);
- await mockResolvers.Mutations.deleteEnvironment(null, {
+ await mockResolvers.Mutation.deleteEnvironment(null, {
environment: { deletePath: ENDPOINT },
});
@@ -79,7 +171,7 @@ describe('~/frontend/environments/graphql/resolvers', () => {
it('should post to the auto stop path', async () => {
mock.onPost(ENDPOINT).reply(200);
- await mockResolvers.Mutations.cancelAutoStop(null, {
+ await mockResolvers.Mutation.cancelAutoStop(null, {
environment: { autoStopPath: ENDPOINT },
});
@@ -88,4 +180,34 @@ describe('~/frontend/environments/graphql/resolvers', () => {
);
});
});
+ describe('setEnvironmentToRollback', () => {
+ it('should write the given environment to the cache', () => {
+ localState.client.writeQuery = jest.fn();
+ mockResolvers.Mutation.setEnvironmentToRollback(
+ null,
+ { environment: resolvedEnvironment },
+ localState,
+ );
+
+ expect(localState.client.writeQuery).toHaveBeenCalledWith({
+ query: environmentToRollback,
+ data: { environmentToRollback: resolvedEnvironment },
+ });
+ });
+ });
+ describe('setEnvironmentToDelete', () => {
+ it('should write the given environment to the cache', () => {
+ localState.client.writeQuery = jest.fn();
+ mockResolvers.Mutation.setEnvironmentToDelete(
+ null,
+ { environment: resolvedEnvironment },
+ localState,
+ );
+
+ expect(localState.client.writeQuery).toHaveBeenCalledWith({
+ query: environmentToDelete,
+ data: { environmentToDelete: resolvedEnvironment },
+ });
+ });
+ });
});
diff --git a/spec/frontend/environments/new_environment_folder_spec.js b/spec/frontend/environments/new_environment_folder_spec.js
index 5696e187a86..27d27d5869a 100644
--- a/spec/frontend/environments/new_environment_folder_spec.js
+++ b/spec/frontend/environments/new_environment_folder_spec.js
@@ -3,8 +3,8 @@ import Vue from 'vue';
import { GlCollapse, GlIcon } from '@gitlab/ui';
import createMockApollo from 'helpers/mock_apollo_helper';
import { mountExtended } from 'helpers/vue_test_utils_helper';
+import { __, s__ } from '~/locale';
import EnvironmentsFolder from '~/environments/components/new_environment_folder.vue';
-import { s__ } from '~/locale';
import { resolvedEnvironmentsApp, resolvedFolder } from './graphql/mock_data';
Vue.use(VueApollo);
@@ -14,6 +14,7 @@ describe('~/environments/components/new_environments_folder.vue', () => {
let environmentFolderMock;
let nestedEnvironment;
let folderName;
+ let button;
const findLink = () => wrapper.findByRole('link', { name: s__('Environments|Show all') });
@@ -32,6 +33,7 @@ describe('~/environments/components/new_environments_folder.vue', () => {
environmentFolderMock.mockReturnValue(resolvedFolder);
wrapper = createWrapper({ nestedEnvironment }, createApolloProvider());
folderName = wrapper.findByText(nestedEnvironment.name);
+ button = wrapper.findByRole('button', { name: __('Expand') });
});
afterEach(() => {
@@ -61,10 +63,11 @@ describe('~/environments/components/new_environments_folder.vue', () => {
});
it('opens on click', async () => {
- await folderName.trigger('click');
+ await button.trigger('click');
const link = findLink();
+ expect(button.attributes('aria-label')).toBe(__('Collapse'));
expect(collapse.attributes('visible')).toBe('true');
expect(icons.wrappers.map((i) => i.props('name'))).toEqual(['angle-down', 'folder-open']);
expect(folderName.classes('gl-font-weight-bold')).toBe(true);
diff --git a/spec/frontend/environments/new_environments_app_spec.js b/spec/frontend/environments/new_environments_app_spec.js
index 0ad8e8f442c..1e9bd4d64c9 100644
--- a/spec/frontend/environments/new_environments_app_spec.js
+++ b/spec/frontend/environments/new_environments_app_spec.js
@@ -1,8 +1,11 @@
-import Vue from 'vue';
+import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
-import { mount } from '@vue/test-utils';
+import { GlPagination } from '@gitlab/ui';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
+import setWindowLocation from 'helpers/set_window_location_helper';
+import { sprintf, __, s__ } from '~/locale';
import EnvironmentsApp from '~/environments/components/new_environments_app.vue';
import EnvironmentsFolder from '~/environments/components/new_environment_folder.vue';
import { resolvedEnvironmentsApp, resolvedFolder } from './graphql/mock_data';
@@ -13,20 +16,59 @@ describe('~/environments/components/new_environments_app.vue', () => {
let wrapper;
let environmentAppMock;
let environmentFolderMock;
+ let paginationMock;
const createApolloProvider = () => {
const mockResolvers = {
- Query: { environmentApp: environmentAppMock, folder: environmentFolderMock },
+ Query: {
+ environmentApp: environmentAppMock,
+ folder: environmentFolderMock,
+ pageInfo: paginationMock,
+ },
};
return createMockApollo([], mockResolvers);
};
- const createWrapper = (apolloProvider) => mount(EnvironmentsApp, { apolloProvider });
+ const createWrapper = ({ provide = {}, apolloProvider } = {}) =>
+ mountExtended(EnvironmentsApp, {
+ provide: {
+ newEnvironmentPath: '/environments/new',
+ canCreateEnvironment: true,
+ defaultBranchName: 'main',
+ ...provide,
+ },
+ apolloProvider,
+ });
+
+ const createWrapperWithMocked = async ({
+ provide = {},
+ environmentsApp,
+ folder,
+ pageInfo = {
+ total: 20,
+ perPage: 5,
+ nextPage: 3,
+ page: 2,
+ previousPage: 1,
+ __typename: 'LocalPageInfo',
+ },
+ }) => {
+ setWindowLocation('?scope=available&page=2');
+ environmentAppMock.mockReturnValue(environmentsApp);
+ environmentFolderMock.mockReturnValue(folder);
+ paginationMock.mockReturnValue(pageInfo);
+ const apolloProvider = createApolloProvider();
+ wrapper = createWrapper({ apolloProvider, provide });
+
+ await waitForPromises();
+ await nextTick();
+ };
beforeEach(() => {
environmentAppMock = jest.fn();
environmentFolderMock = jest.fn();
+ paginationMock = jest.fn();
});
afterEach(() => {
@@ -34,17 +76,196 @@ describe('~/environments/components/new_environments_app.vue', () => {
});
it('should show all the folders that are fetched', async () => {
- environmentAppMock.mockReturnValue(resolvedEnvironmentsApp);
- environmentFolderMock.mockReturnValue(resolvedFolder);
- const apolloProvider = createApolloProvider();
- wrapper = createWrapper(apolloProvider);
-
- await waitForPromises();
- await Vue.nextTick();
+ await createWrapperWithMocked({
+ environmentsApp: resolvedEnvironmentsApp,
+ folder: resolvedFolder,
+ });
const text = wrapper.findAllComponents(EnvironmentsFolder).wrappers.map((w) => w.text());
expect(text).toContainEqual(expect.stringMatching('review'));
expect(text).not.toContainEqual(expect.stringMatching('production'));
});
+
+ it('should show a button to create a new environment', async () => {
+ await createWrapperWithMocked({
+ environmentsApp: resolvedEnvironmentsApp,
+ folder: resolvedFolder,
+ });
+
+ const button = wrapper.findByRole('link', { name: s__('Environments|New environment') });
+ expect(button.attributes('href')).toBe('/environments/new');
+ });
+
+ it('should not show a button to create a new environment if the user has no permissions', async () => {
+ await createWrapperWithMocked({
+ environmentsApp: resolvedEnvironmentsApp,
+ folder: resolvedFolder,
+ provide: { canCreateEnvironment: false, newEnvironmentPath: '' },
+ });
+
+ const button = wrapper.findByRole('link', { name: s__('Environments|New environment') });
+ expect(button.exists()).toBe(false);
+ });
+
+ it('should show a button to open the review app modal', async () => {
+ await createWrapperWithMocked({
+ environmentsApp: resolvedEnvironmentsApp,
+ folder: resolvedFolder,
+ });
+
+ const button = wrapper.findByRole('button', { name: s__('Environments|Enable review app') });
+ button.trigger('click');
+
+ await nextTick();
+
+ expect(wrapper.findByText(s__('ReviewApp|Enable Review App')).exists()).toBe(true);
+ });
+
+ it('should not show a button to open the review app modal if review apps are configured', async () => {
+ await createWrapperWithMocked({
+ environmentsApp: {
+ ...resolvedEnvironmentsApp,
+ reviewApp: { canSetupReviewApp: false },
+ },
+ folder: resolvedFolder,
+ });
+ await waitForPromises();
+ await nextTick();
+
+ const button = wrapper.findByRole('button', { name: s__('Environments|Enable review app') });
+ expect(button.exists()).toBe(false);
+ });
+
+ describe('tabs', () => {
+ it('should show tabs for available and stopped environmets', async () => {
+ await createWrapperWithMocked({
+ environmentsApp: resolvedEnvironmentsApp,
+ folder: resolvedFolder,
+ });
+
+ const [available, stopped] = wrapper.findAllByRole('tab').wrappers;
+
+ expect(available.text()).toContain(__('Available'));
+ expect(available.text()).toContain(resolvedEnvironmentsApp.availableCount);
+ expect(stopped.text()).toContain(__('Stopped'));
+ expect(stopped.text()).toContain(resolvedEnvironmentsApp.stoppedCount);
+ });
+
+ it('should change the requested scope on tab change', async () => {
+ await createWrapperWithMocked({
+ environmentsApp: resolvedEnvironmentsApp,
+ folder: resolvedFolder,
+ });
+ const stopped = wrapper.findByRole('tab', {
+ name: `${__('Stopped')} ${resolvedEnvironmentsApp.stoppedCount}`,
+ });
+
+ stopped.trigger('click');
+
+ await nextTick();
+ await waitForPromises();
+
+ expect(environmentAppMock).toHaveBeenCalledWith(
+ expect.anything(),
+ expect.objectContaining({ scope: 'stopped' }),
+ expect.anything(),
+ expect.anything(),
+ );
+ });
+ });
+
+ describe('pagination', () => {
+ it('should sync page from query params on load', async () => {
+ await createWrapperWithMocked({
+ environmentsApp: resolvedEnvironmentsApp,
+ folder: resolvedFolder,
+ });
+
+ expect(wrapper.findComponent(GlPagination).props('value')).toBe(2);
+ });
+
+ it('should change the requested page on next page click', async () => {
+ await createWrapperWithMocked({
+ environmentsApp: resolvedEnvironmentsApp,
+ folder: resolvedFolder,
+ });
+ const next = wrapper.findByRole('link', {
+ name: __('Go to next page'),
+ });
+
+ next.trigger('click');
+
+ await nextTick();
+ await waitForPromises();
+
+ expect(environmentAppMock).toHaveBeenCalledWith(
+ expect.anything(),
+ expect.objectContaining({ page: 3 }),
+ expect.anything(),
+ expect.anything(),
+ );
+ });
+
+ it('should change the requested page on previous page click', async () => {
+ await createWrapperWithMocked({
+ environmentsApp: resolvedEnvironmentsApp,
+ folder: resolvedFolder,
+ });
+ const prev = wrapper.findByRole('link', {
+ name: __('Go to previous page'),
+ });
+
+ prev.trigger('click');
+
+ await nextTick();
+ await waitForPromises();
+
+ expect(environmentAppMock).toHaveBeenCalledWith(
+ expect.anything(),
+ expect.objectContaining({ page: 1 }),
+ expect.anything(),
+ expect.anything(),
+ );
+ });
+
+ it('should change the requested page on specific page click', async () => {
+ await createWrapperWithMocked({
+ environmentsApp: resolvedEnvironmentsApp,
+ folder: resolvedFolder,
+ });
+
+ const page = 1;
+ const pageButton = wrapper.findByRole('link', {
+ name: sprintf(__('Go to page %{page}'), { page }),
+ });
+
+ pageButton.trigger('click');
+
+ await nextTick();
+ await waitForPromises();
+
+ expect(environmentAppMock).toHaveBeenCalledWith(
+ expect.anything(),
+ expect.objectContaining({ page }),
+ expect.anything(),
+ expect.anything(),
+ );
+ });
+
+ it('should sync the query params to the new page', async () => {
+ await createWrapperWithMocked({
+ environmentsApp: resolvedEnvironmentsApp,
+ folder: resolvedFolder,
+ });
+ const next = wrapper.findByRole('link', {
+ name: __('Go to next page'),
+ });
+
+ next.trigger('click');
+
+ await nextTick();
+ expect(window.location.search).toBe('?scope=available&page=3');
+ });
+ });
});