summaryrefslogtreecommitdiff
path: root/spec/frontend/environments
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/environments')
-rw-r--r--spec/frontend/environments/confirm_rollback_modal_spec.js8
-rw-r--r--spec/frontend/environments/deployment_spec.js29
-rw-r--r--spec/frontend/environments/deployment_status_badge_spec.js42
-rw-r--r--spec/frontend/environments/environment_actions_spec.js35
-rw-r--r--spec/frontend/environments/environment_stop_spec.js72
-rw-r--r--spec/frontend/environments/graphql/mock_data.js136
-rw-r--r--spec/frontend/environments/graphql/resolvers_spec.js34
-rw-r--r--spec/frontend/environments/new_environment_folder_spec.js34
-rw-r--r--spec/frontend/environments/new_environment_item_spec.js341
-rw-r--r--spec/frontend/environments/new_environments_app_spec.js37
10 files changed, 739 insertions, 29 deletions
diff --git a/spec/frontend/environments/confirm_rollback_modal_spec.js b/spec/frontend/environments/confirm_rollback_modal_spec.js
index b699f953945..b8dcb7c0d08 100644
--- a/spec/frontend/environments/confirm_rollback_modal_spec.js
+++ b/spec/frontend/environments/confirm_rollback_modal_spec.js
@@ -26,7 +26,7 @@ describe('Confirm Rollback Modal Component', () => {
commit: {
shortId: 'abc0123',
},
- 'last?': true,
+ isLast: true,
},
modalId: 'test',
};
@@ -145,7 +145,7 @@ describe('Confirm Rollback Modal Component', () => {
...environment,
lastDeployment: {
...environment.lastDeployment,
- 'last?': false,
+ isLast: false,
},
},
hasMultipleCommits,
@@ -167,7 +167,7 @@ describe('Confirm Rollback Modal Component', () => {
...environment,
lastDeployment: {
...environment.lastDeployment,
- 'last?': false,
+ isLast: false,
},
},
hasMultipleCommits,
@@ -191,7 +191,7 @@ describe('Confirm Rollback Modal Component', () => {
...environment,
lastDeployment: {
...environment.lastDeployment,
- 'last?': true,
+ isLast: true,
},
},
hasMultipleCommits,
diff --git a/spec/frontend/environments/deployment_spec.js b/spec/frontend/environments/deployment_spec.js
new file mode 100644
index 00000000000..37209bdc86c
--- /dev/null
+++ b/spec/frontend/environments/deployment_spec.js
@@ -0,0 +1,29 @@
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import Deployment from '~/environments/components/deployment.vue';
+import DeploymentStatusBadge from '~/environments/components/deployment_status_badge.vue';
+import { resolvedEnvironment } from './graphql/mock_data';
+
+describe('~/environments/components/deployment.vue', () => {
+ let wrapper;
+
+ const createWrapper = ({ propsData = {} } = {}) =>
+ mountExtended(Deployment, {
+ propsData: {
+ deployment: resolvedEnvironment.lastDeployment,
+ ...propsData,
+ },
+ });
+
+ afterEach(() => {
+ wrapper?.destroy();
+ });
+
+ describe('status', () => {
+ it('should pass the deployable status to the badge', () => {
+ wrapper = createWrapper();
+ expect(wrapper.findComponent(DeploymentStatusBadge).props('status')).toBe(
+ resolvedEnvironment.lastDeployment.status,
+ );
+ });
+ });
+});
diff --git a/spec/frontend/environments/deployment_status_badge_spec.js b/spec/frontend/environments/deployment_status_badge_spec.js
new file mode 100644
index 00000000000..02aae57396a
--- /dev/null
+++ b/spec/frontend/environments/deployment_status_badge_spec.js
@@ -0,0 +1,42 @@
+import { GlBadge } from '@gitlab/ui';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import { s__ } from '~/locale';
+import DeploymentStatusBadge from '~/environments/components/deployment_status_badge.vue';
+
+describe('~/environments/components/deployment_status_badge.vue', () => {
+ let wrapper;
+
+ const createWrapper = ({ propsData = {} } = {}) =>
+ mountExtended(DeploymentStatusBadge, {
+ propsData,
+ });
+
+ describe.each`
+ status | text | variant | icon
+ ${'created'} | ${s__('Deployment|Created')} | ${'neutral'} | ${'status_created'}
+ ${'running'} | ${s__('Deployment|Running')} | ${'info'} | ${'status_running'}
+ ${'success'} | ${s__('Deployment|Success')} | ${'success'} | ${'status_success'}
+ ${'failed'} | ${s__('Deployment|Failed')} | ${'danger'} | ${'status_failed'}
+ ${'canceled'} | ${s__('Deployment|Cancelled')} | ${'neutral'} | ${'status_canceled'}
+ ${'skipped'} | ${s__('Deployment|Skipped')} | ${'neutral'} | ${'status_skipped'}
+ ${'blocked'} | ${s__('Deployment|Waiting')} | ${'neutral'} | ${'status_manual'}
+ `('$status', ({ status, text, variant, icon }) => {
+ let badge;
+
+ beforeEach(() => {
+ wrapper = createWrapper({ propsData: { status } });
+ badge = wrapper.findComponent(GlBadge);
+ });
+
+ it(`sets the text to ${text}`, () => {
+ expect(wrapper.text()).toBe(text);
+ });
+
+ it(`sets the variant to ${variant}`, () => {
+ expect(badge.props('variant')).toBe(variant);
+ });
+ it(`sets the icon to ${icon}`, () => {
+ expect(badge.props('icon')).toBe(icon);
+ });
+ });
+});
diff --git a/spec/frontend/environments/environment_actions_spec.js b/spec/frontend/environments/environment_actions_spec.js
index db78a6b0cdd..1b68a692db8 100644
--- a/spec/frontend/environments/environment_actions_spec.js
+++ b/spec/frontend/environments/environment_actions_spec.js
@@ -1,9 +1,13 @@
import { GlDropdown, GlDropdownItem, GlLoadingIcon, GlIcon } from '@gitlab/ui';
import { shallowMount, mount } from '@vue/test-utils';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
import { TEST_HOST } from 'helpers/test_constants';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import EnvironmentActions from '~/environments/components/environment_actions.vue';
import eventHub from '~/environments/event_hub';
+import actionMutation from '~/environments/graphql/mutations/action.mutation.graphql';
+import createMockApollo from 'helpers/mock_apollo_helper';
const scheduledJobAction = {
name: 'scheduled action',
@@ -25,12 +29,13 @@ describe('EnvironmentActions Component', () => {
const findEnvironmentActionsButton = () =>
wrapper.find('[data-testid="environment-actions-button"]');
- function createComponent(props, { mountFn = shallowMount } = {}) {
+ function createComponent(props, { mountFn = shallowMount, options = {} } = {}) {
wrapper = mountFn(EnvironmentActions, {
propsData: { actions: [], ...props },
directives: {
GlTooltip: createMockDirective(),
},
+ ...options,
});
}
@@ -150,4 +155,32 @@ describe('EnvironmentActions Component', () => {
expect(findDropdownItem(expiredJobAction).text()).toContain('00:00:00');
});
});
+
+ describe('graphql', () => {
+ Vue.use(VueApollo);
+
+ const action = {
+ name: 'bar',
+ play_path: 'https://gitlab.com/play',
+ };
+
+ let mockApollo;
+
+ beforeEach(() => {
+ mockApollo = createMockApollo();
+ createComponent(
+ { actions: [action], graphql: true },
+ { options: { apolloProvider: mockApollo } },
+ );
+ });
+
+ it('should trigger a graphql mutation on click', () => {
+ jest.spyOn(mockApollo.defaultClient, 'mutate');
+ findDropdownItem(action).vm.$emit('click');
+ expect(mockApollo.defaultClient.mutate).toHaveBeenCalledWith({
+ mutation: actionMutation,
+ variables: { action },
+ });
+ });
+ });
});
diff --git a/spec/frontend/environments/environment_stop_spec.js b/spec/frontend/environments/environment_stop_spec.js
index dff444b79f3..358abca2f77 100644
--- a/spec/frontend/environments/environment_stop_spec.js
+++ b/spec/frontend/environments/environment_stop_spec.js
@@ -1,38 +1,80 @@
import { GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
-import $ from 'jquery';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import setEnvironmentToStopMutation from '~/environments/graphql/mutations/set_environment_to_stop.mutation.graphql';
+import isEnvironmentStoppingQuery from '~/environments/graphql/queries/is_environment_stopping.query.graphql';
import StopComponent from '~/environments/components/environment_stop.vue';
import eventHub from '~/environments/event_hub';
-
-$.fn.tooltip = () => {};
+import createMockApollo from 'helpers/mock_apollo_helper';
+import { resolvedEnvironment } from './graphql/mock_data';
describe('Stop Component', () => {
let wrapper;
- const createWrapper = () => {
+ const createWrapper = (props = {}, options = {}) => {
wrapper = shallowMount(StopComponent, {
propsData: {
environment: {},
+ ...props,
},
+ ...options,
});
};
const findButton = () => wrapper.find(GlButton);
- beforeEach(() => {
- jest.spyOn(window, 'confirm');
+ describe('eventHub', () => {
+ beforeEach(() => {
+ createWrapper();
+ });
- createWrapper();
- });
+ it('should render a button to stop the environment', () => {
+ expect(findButton().exists()).toBe(true);
+ expect(wrapper.attributes('title')).toEqual('Stop environment');
+ });
- it('should render a button to stop the environment', () => {
- expect(findButton().exists()).toBe(true);
- expect(wrapper.attributes('title')).toEqual('Stop environment');
+ it('emits requestStopEnvironment in the event hub when button is clicked', () => {
+ jest.spyOn(eventHub, '$emit');
+ findButton().vm.$emit('click');
+ expect(eventHub.$emit).toHaveBeenCalledWith('requestStopEnvironment', wrapper.vm.environment);
+ });
});
- it('emits requestStopEnvironment in the event hub when button is clicked', () => {
- jest.spyOn(eventHub, '$emit');
- findButton().vm.$emit('click');
- expect(eventHub.$emit).toHaveBeenCalledWith('requestStopEnvironment', wrapper.vm.environment);
+ describe('graphql', () => {
+ Vue.use(VueApollo);
+ let mockApollo;
+
+ beforeEach(() => {
+ mockApollo = createMockApollo();
+ mockApollo.clients.defaultClient.writeQuery({
+ query: isEnvironmentStoppingQuery,
+ variables: { environment: resolvedEnvironment },
+ data: { isEnvironmentStopping: true },
+ });
+
+ createWrapper(
+ { graphql: true, environment: resolvedEnvironment },
+ { apolloProvider: mockApollo },
+ );
+ });
+
+ it('should render a button to stop the environment', () => {
+ expect(findButton().exists()).toBe(true);
+ expect(wrapper.attributes('title')).toEqual('Stop environment');
+ });
+
+ it('sets the environment to stop on click', () => {
+ jest.spyOn(mockApollo.defaultClient, 'mutate');
+ findButton().vm.$emit('click');
+ expect(mockApollo.defaultClient.mutate).toHaveBeenCalledWith({
+ mutation: setEnvironmentToStopMutation,
+ variables: { environment: resolvedEnvironment },
+ });
+ });
+
+ it('should show a loading icon if the environment is currently stopping', async () => {
+ expect(findButton().props('loading')).toBe(true);
+ });
});
});
diff --git a/spec/frontend/environments/graphql/mock_data.js b/spec/frontend/environments/graphql/mock_data.js
index e75d3ac0321..fce30973547 100644
--- a/spec/frontend/environments/graphql/mock_data.js
+++ b/spec/frontend/environments/graphql/mock_data.js
@@ -477,7 +477,141 @@ export const resolvedEnvironment = {
externalUrl: 'https://example.org',
environmentType: 'review',
nameWithoutType: 'hello',
- lastDeployment: null,
+ lastDeployment: {
+ id: 78,
+ iid: 24,
+ sha: 'f3ba6dd84f8f891373e9b869135622b954852db1',
+ ref: { name: 'main', refPath: '/h5bp/html5-boilerplate/-/tree/main' },
+ status: 'success',
+ createdAt: '2022-01-07T15:47:27.415Z',
+ deployedAt: '2022-01-07T15:47:32.450Z',
+ tag: false,
+ isLast: true,
+ user: {
+ id: 1,
+ username: 'root',
+ name: 'Administrator',
+ state: 'active',
+ avatarUrl:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ webUrl: 'http://gck.test:3000/root',
+ showStatus: false,
+ path: '/root',
+ },
+ deployable: {
+ id: 1014,
+ name: 'deploy-prod',
+ started: '2022-01-07T15:47:31.037Z',
+ complete: true,
+ archived: false,
+ buildPath: '/h5bp/html5-boilerplate/-/jobs/1014',
+ retryPath: '/h5bp/html5-boilerplate/-/jobs/1014/retry',
+ playable: false,
+ scheduled: false,
+ createdAt: '2022-01-07T15:47:27.404Z',
+ updatedAt: '2022-01-07T15:47:32.341Z',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ tooltip: 'passed',
+ hasDetails: true,
+ detailsPath: '/h5bp/html5-boilerplate/-/jobs/1014',
+ illustration: {
+ image:
+ '/assets/illustrations/skipped-job_empty-29a8a37d8a61d1b6f68cf3484f9024e53cd6eb95e28eae3554f8011a1146bf27.svg',
+ size: 'svg-430',
+ title: 'This job does not have a trace.',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/h5bp/html5-boilerplate/-/jobs/1014/retry',
+ method: 'post',
+ buttonTitle: 'Retry this job',
+ },
+ },
+ },
+ commit: {
+ id: 'f3ba6dd84f8f891373e9b869135622b954852db1',
+ shortId: 'f3ba6dd8',
+ createdAt: '2022-01-07T15:47:26.000+00:00',
+ parentIds: ['3213b6ac17afab99be37d5d38f38c6c8407387cc'],
+ title: 'Update .gitlab-ci.yml file',
+ message: 'Update .gitlab-ci.yml file',
+ authorName: 'Administrator',
+ authorEmail: 'admin@example.com',
+ authoredDate: '2022-01-07T15:47:26.000+00:00',
+ committerName: 'Administrator',
+ committerEmail: 'admin@example.com',
+ committedDate: '2022-01-07T15:47:26.000+00:00',
+ trailers: {},
+ webUrl:
+ 'http://gck.test:3000/h5bp/html5-boilerplate/-/commit/f3ba6dd84f8f891373e9b869135622b954852db1',
+ author: {
+ id: 1,
+ username: 'root',
+ name: 'Administrator',
+ state: 'active',
+ avatarUrl:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ webUrl: 'http://gck.test:3000/root',
+ showStatus: false,
+ path: '/root',
+ },
+ authorGravatarUrl:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ commitUrl:
+ 'http://gck.test:3000/h5bp/html5-boilerplate/-/commit/f3ba6dd84f8f891373e9b869135622b954852db1',
+ commitPath: '/h5bp/html5-boilerplate/-/commit/f3ba6dd84f8f891373e9b869135622b954852db1',
+ },
+ manualActions: [
+ {
+ id: 1015,
+ name: 'deploy-staging',
+ started: null,
+ complete: false,
+ archived: false,
+ buildPath: '/h5bp/html5-boilerplate/-/jobs/1015',
+ playPath: '/h5bp/html5-boilerplate/-/jobs/1015/play',
+ playable: true,
+ scheduled: false,
+ createdAt: '2022-01-07T15:47:27.422Z',
+ updatedAt: '2022-01-07T15:47:28.557Z',
+ status: {
+ icon: 'status_manual',
+ text: 'manual',
+ label: 'manual play action',
+ group: 'manual',
+ tooltip: 'manual action',
+ hasDetails: true,
+ detailsPath: '/h5bp/html5-boilerplate/-/jobs/1015',
+ illustration: {
+ image:
+ '/assets/illustrations/manual_action-c55aee2c5f9ebe9f72751480af8bb307be1a6f35552f344cc6d1bf979d3422f6.svg',
+ size: 'svg-394',
+ title: 'This job requires a manual action',
+ content:
+ 'This job requires manual intervention to start. Before starting this job, you can add variables below for last-minute configuration changes.',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png',
+ action: {
+ icon: 'play',
+ title: 'Play',
+ path: '/h5bp/html5-boilerplate/-/jobs/1015/play',
+ method: 'post',
+ buttonTitle: 'Trigger this manual action',
+ },
+ },
+ },
+ ],
+ scheduledActions: [],
+ cluster: null,
+ },
hasStopAction: false,
rolloutStatus: null,
environmentPath: '/h5bp/html5-boilerplate/-/environments/41',
diff --git a/spec/frontend/environments/graphql/resolvers_spec.js b/spec/frontend/environments/graphql/resolvers_spec.js
index d8d26b74504..6b53dc24f0f 100644
--- a/spec/frontend/environments/graphql/resolvers_spec.js
+++ b/spec/frontend/environments/graphql/resolvers_spec.js
@@ -1,8 +1,10 @@
import MockAdapter from 'axios-mock-adapter';
+import { s__ } from '~/locale';
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 environmentToStopQuery from '~/environments/graphql/queries/environment_to_stop.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';
@@ -210,4 +212,36 @@ describe('~/frontend/environments/graphql/resolvers', () => {
});
});
});
+ describe('setEnvironmentToStop', () => {
+ it('should write the given environment to the cache', () => {
+ localState.client.writeQuery = jest.fn();
+ mockResolvers.Mutation.setEnvironmentToStop(
+ null,
+ { environment: resolvedEnvironment },
+ localState,
+ );
+
+ expect(localState.client.writeQuery).toHaveBeenCalledWith({
+ query: environmentToStopQuery,
+ data: { environmentToStop: resolvedEnvironment },
+ });
+ });
+ });
+ describe('action', () => {
+ it('should POST to the given path', async () => {
+ mock.onPost(ENDPOINT).reply(200);
+ const errors = await mockResolvers.Mutation.action(null, { action: { playPath: ENDPOINT } });
+
+ expect(errors).toEqual({ __typename: 'LocalEnvironmentErrors', errors: [] });
+ });
+ it('should return a nice error message on fail', async () => {
+ mock.onPost(ENDPOINT).reply(500);
+ const errors = await mockResolvers.Mutation.action(null, { action: { playPath: ENDPOINT } });
+
+ expect(errors).toEqual({
+ __typename: 'LocalEnvironmentErrors',
+ errors: [s__('Environments|An error occurred while making the request.')],
+ });
+ });
+ });
});
diff --git a/spec/frontend/environments/new_environment_folder_spec.js b/spec/frontend/environments/new_environment_folder_spec.js
index 27d27d5869a..6823c88a5a1 100644
--- a/spec/frontend/environments/new_environment_folder_spec.js
+++ b/spec/frontend/environments/new_environment_folder_spec.js
@@ -1,10 +1,13 @@
import VueApollo from 'vue-apollo';
-import Vue from 'vue';
+import Vue, { nextTick } from 'vue';
import { GlCollapse, GlIcon } from '@gitlab/ui';
+import waitForPromises from 'helpers/wait_for_promises';
import createMockApollo from 'helpers/mock_apollo_helper';
import { mountExtended } from 'helpers/vue_test_utils_helper';
+import { stubTransition } from 'helpers/stub_transition';
import { __, s__ } from '~/locale';
import EnvironmentsFolder from '~/environments/components/new_environment_folder.vue';
+import EnvironmentItem from '~/environments/components/new_environment_item.vue';
import { resolvedEnvironmentsApp, resolvedFolder } from './graphql/mock_data';
Vue.use(VueApollo);
@@ -25,13 +28,20 @@ describe('~/environments/components/new_environments_folder.vue', () => {
};
const createWrapper = (propsData, apolloProvider) =>
- mountExtended(EnvironmentsFolder, { apolloProvider, propsData });
+ mountExtended(EnvironmentsFolder, {
+ apolloProvider,
+ propsData,
+ stubs: { transition: stubTransition() },
+ });
- beforeEach(() => {
+ beforeEach(async () => {
environmentFolderMock = jest.fn();
[nestedEnvironment] = resolvedEnvironmentsApp.environments;
environmentFolderMock.mockReturnValue(resolvedFolder);
wrapper = createWrapper({ nestedEnvironment }, createApolloProvider());
+
+ await nextTick();
+ await waitForPromises();
folderName = wrapper.findByText(nestedEnvironment.name);
button = wrapper.findByRole('button', { name: __('Expand') });
});
@@ -57,7 +67,8 @@ describe('~/environments/components/new_environments_folder.vue', () => {
const link = findLink();
expect(collapse.attributes('visible')).toBeUndefined();
- expect(icons.wrappers.map((i) => i.props('name'))).toEqual(['angle-right', 'folder-o']);
+ const iconNames = icons.wrappers.map((i) => i.props('name')).slice(0, 2);
+ expect(iconNames).toEqual(['angle-right', 'folder-o']);
expect(folderName.classes('gl-font-weight-bold')).toBe(false);
expect(link.exists()).toBe(false);
});
@@ -68,10 +79,21 @@ describe('~/environments/components/new_environments_folder.vue', () => {
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(collapse.attributes('visible')).toBe('visible');
+ const iconNames = icons.wrappers.map((i) => i.props('name')).slice(0, 2);
+ expect(iconNames).toEqual(['angle-down', 'folder-open']);
expect(folderName.classes('gl-font-weight-bold')).toBe(true);
expect(link.attributes('href')).toBe(nestedEnvironment.latest.folderPath);
});
+
+ it('displays all environments when opened', async () => {
+ await button.trigger('click');
+
+ const names = resolvedFolder.environments.map((e) =>
+ expect.stringMatching(e.nameWithoutType),
+ );
+ const environments = wrapper.findAllComponents(EnvironmentItem).wrappers.map((w) => w.text());
+ expect(environments).toEqual(expect.arrayContaining(names));
+ });
});
});
diff --git a/spec/frontend/environments/new_environment_item_spec.js b/spec/frontend/environments/new_environment_item_spec.js
new file mode 100644
index 00000000000..244aef5c43b
--- /dev/null
+++ b/spec/frontend/environments/new_environment_item_spec.js
@@ -0,0 +1,341 @@
+import VueApollo from 'vue-apollo';
+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 { stubTransition } from 'helpers/stub_transition';
+import { __, s__ } from '~/locale';
+import EnvironmentItem from '~/environments/components/new_environment_item.vue';
+import Deployment from '~/environments/components/deployment.vue';
+import { resolvedEnvironment } from './graphql/mock_data';
+
+Vue.use(VueApollo);
+
+describe('~/environments/components/new_environment_item.vue', () => {
+ let wrapper;
+
+ const createApolloProvider = () => {
+ return createMockApollo();
+ };
+
+ const createWrapper = ({ propsData = {}, apolloProvider } = {}) =>
+ mountExtended(EnvironmentItem, {
+ apolloProvider,
+ propsData: { environment: resolvedEnvironment, ...propsData },
+ stubs: { transition: stubTransition() },
+ });
+
+ const findDeployment = () => wrapper.findComponent(Deployment);
+
+ afterEach(() => {
+ wrapper?.destroy();
+ });
+
+ it('displays the name when not in a folder', () => {
+ wrapper = createWrapper({ apolloProvider: createApolloProvider() });
+
+ const name = wrapper.findByRole('link', { name: resolvedEnvironment.name });
+ expect(name.exists()).toBe(true);
+ });
+
+ it('displays the name minus the folder prefix when in a folder', () => {
+ wrapper = createWrapper({
+ propsData: { inFolder: true },
+ apolloProvider: createApolloProvider(),
+ });
+
+ const name = wrapper.findByRole('link', { name: resolvedEnvironment.nameWithoutType });
+ expect(name.exists()).toBe(true);
+ });
+
+ it('truncates the name if it is very long', () => {
+ const environment = {
+ ...resolvedEnvironment,
+ name:
+ 'this is a really long name that should be truncated because otherwise it would look strange in the UI',
+ };
+ wrapper = createWrapper({ propsData: { environment }, apolloProvider: createApolloProvider() });
+
+ const name = wrapper.findByRole('link', {
+ name: (text) => environment.name.startsWith(text.slice(0, -1)),
+ });
+ expect(name.exists()).toBe(true);
+ expect(name.text()).toHaveLength(80);
+ });
+
+ describe('url', () => {
+ it('shows a link for the url if one is present', () => {
+ wrapper = createWrapper({ apolloProvider: createApolloProvider() });
+
+ const url = wrapper.findByRole('link', { name: s__('Environments|Open live environment') });
+
+ expect(url.attributes('href')).toEqual(resolvedEnvironment.externalUrl);
+ });
+
+ it('does not show a link for the url if one is missing', () => {
+ wrapper = createWrapper({
+ propsData: { environment: { ...resolvedEnvironment, externalUrl: '' } },
+ apolloProvider: createApolloProvider(),
+ });
+
+ const url = wrapper.findByRole('link', { name: s__('Environments|Open live environment') });
+
+ expect(url.exists()).toBe(false);
+ });
+ });
+
+ describe('actions', () => {
+ it('shows a dropdown if there are actions to perform', () => {
+ wrapper = createWrapper({ apolloProvider: createApolloProvider() });
+
+ const actions = wrapper.findByRole('button', { name: __('Deploy to...') });
+
+ expect(actions.exists()).toBe(true);
+ });
+
+ it('does not show a dropdown if there are no actions to perform', () => {
+ wrapper = createWrapper({
+ propsData: {
+ environment: {
+ ...resolvedEnvironment,
+ lastDeployment: null,
+ },
+ apolloProvider: createApolloProvider(),
+ },
+ });
+
+ const actions = wrapper.findByRole('button', { name: __('Deploy to...') });
+
+ expect(actions.exists()).toBe(false);
+ });
+
+ it('passes all the actions down to the action component', () => {
+ wrapper = createWrapper({ apolloProvider: createApolloProvider() });
+
+ const action = wrapper.findByRole('menuitem', { name: 'deploy-staging' });
+
+ expect(action.exists()).toBe(true);
+ });
+ });
+
+ describe('stop', () => {
+ it('shows a buton to stop the environment if the environment is available', () => {
+ wrapper = createWrapper({ apolloProvider: createApolloProvider() });
+
+ const stop = wrapper.findByRole('button', { name: s__('Environments|Stop environment') });
+
+ expect(stop.exists()).toBe(true);
+ });
+
+ it('does not show a buton to stop the environment if the environment is stopped', () => {
+ wrapper = createWrapper({
+ propsData: { environment: { ...resolvedEnvironment, canStop: false } },
+ apolloProvider: createApolloProvider(),
+ });
+
+ const stop = wrapper.findByRole('button', { name: s__('Environments|Stop environment') });
+
+ expect(stop.exists()).toBe(false);
+ });
+ });
+
+ describe('rollback', () => {
+ it('shows the option to rollback/re-deploy if available', () => {
+ wrapper = createWrapper({ apolloProvider: createApolloProvider() });
+
+ const rollback = wrapper.findByRole('menuitem', {
+ name: s__('Environments|Re-deploy to environment'),
+ });
+
+ expect(rollback.exists()).toBe(true);
+ });
+
+ it('does not show the option to rollback/re-deploy if not available', () => {
+ wrapper = createWrapper({
+ propsData: { environment: { ...resolvedEnvironment, lastDeployment: null } },
+ apolloProvider: createApolloProvider(),
+ });
+
+ const rollback = wrapper.findByRole('menuitem', {
+ name: s__('Environments|Re-deploy to environment'),
+ });
+
+ expect(rollback.exists()).toBe(false);
+ });
+ });
+
+ describe('pin', () => {
+ it('shows the option to pin the environment if there is an autostop date', () => {
+ wrapper = createWrapper({
+ propsData: {
+ environment: { ...resolvedEnvironment, autoStopAt: new Date(Date.now() + 100000) },
+ },
+ apolloProvider: createApolloProvider(),
+ });
+
+ const rollback = wrapper.findByRole('menuitem', { name: __('Prevent auto-stopping') });
+
+ expect(rollback.exists()).toBe(true);
+ });
+
+ it('does not show the option to pin the environment if there is no autostop date', () => {
+ wrapper = createWrapper({ apolloProvider: createApolloProvider() });
+
+ const rollback = wrapper.findByRole('menuitem', { name: __('Prevent auto-stopping') });
+
+ expect(rollback.exists()).toBe(false);
+ });
+ });
+
+ describe('monitoring', () => {
+ it('shows the link to monitoring if metrics are set up', () => {
+ wrapper = createWrapper({
+ propsData: { environment: { ...resolvedEnvironment, metricsPath: '/metrics' } },
+ apolloProvider: createApolloProvider(),
+ });
+
+ const rollback = wrapper.findByRole('menuitem', { name: __('Monitoring') });
+
+ expect(rollback.exists()).toBe(true);
+ });
+
+ it('does not show the link to monitoring if metrics are not set up', () => {
+ wrapper = createWrapper({ apolloProvider: createApolloProvider() });
+
+ const rollback = wrapper.findByRole('menuitem', { name: __('Monitoring') });
+
+ expect(rollback.exists()).toBe(false);
+ });
+ });
+ describe('terminal', () => {
+ it('shows the link to the terminal if set up', () => {
+ wrapper = createWrapper({
+ propsData: { environment: { ...resolvedEnvironment, terminalPath: '/terminal' } },
+ apolloProvider: createApolloProvider(),
+ });
+
+ const rollback = wrapper.findByRole('menuitem', { name: __('Terminal') });
+
+ expect(rollback.exists()).toBe(true);
+ });
+
+ it('does not show the link to the terminal if not set up', () => {
+ wrapper = createWrapper({ apolloProvider: createApolloProvider() });
+
+ const rollback = wrapper.findByRole('menuitem', { name: __('Terminal') });
+
+ expect(rollback.exists()).toBe(false);
+ });
+ });
+
+ describe('delete', () => {
+ it('shows the button to delete the environment if possible', () => {
+ wrapper = createWrapper({
+ propsData: {
+ environment: { ...resolvedEnvironment, canDelete: true, deletePath: '/terminal' },
+ },
+ apolloProvider: createApolloProvider(),
+ });
+
+ const rollback = wrapper.findByRole('menuitem', {
+ name: s__('Environments|Delete environment'),
+ });
+
+ expect(rollback.exists()).toBe(true);
+ });
+
+ it('does not show the button to delete the environment if not possible', () => {
+ wrapper = createWrapper({ apolloProvider: createApolloProvider() });
+
+ const rollback = wrapper.findByRole('menuitem', {
+ name: s__('Environments|Delete environment'),
+ });
+
+ expect(rollback.exists()).toBe(false);
+ });
+ });
+
+ describe('collapse', () => {
+ let icon;
+ let collapse;
+ let button;
+ let environmentName;
+
+ beforeEach(() => {
+ wrapper = createWrapper({ apolloProvider: createApolloProvider() });
+ collapse = wrapper.findComponent(GlCollapse);
+ icon = wrapper.findComponent(GlIcon);
+ button = wrapper.findByRole('button', { name: __('Expand') });
+ environmentName = wrapper.findByText(resolvedEnvironment.name);
+ });
+
+ it('is collapsed by default', () => {
+ expect(collapse.attributes('visible')).toBeUndefined();
+ expect(icon.props('name')).toEqual('angle-right');
+ expect(environmentName.classes('gl-font-weight-bold')).toBe(false);
+ });
+
+ it('opens on click', async () => {
+ expect(findDeployment().isVisible()).toBe(false);
+
+ await button.trigger('click');
+
+ expect(button.attributes('aria-label')).toBe(__('Collapse'));
+ expect(collapse.attributes('visible')).toBe('visible');
+ expect(icon.props('name')).toEqual('angle-down');
+ expect(environmentName.classes('gl-font-weight-bold')).toBe(true);
+ expect(findDeployment().isVisible()).toBe(true);
+ });
+ });
+ describe('last deployment', () => {
+ it('should pass the last deployment to the deployment component when it exists', () => {
+ wrapper = createWrapper({ apolloProvider: createApolloProvider() });
+
+ const deployment = findDeployment();
+ expect(deployment.props('deployment')).toEqual(resolvedEnvironment.lastDeployment);
+ });
+ it('should not show the last deployment when it is missing', () => {
+ const environment = {
+ ...resolvedEnvironment,
+ lastDeployment: null,
+ };
+
+ wrapper = createWrapper({
+ propsData: { environment },
+ apolloProvider: createApolloProvider(),
+ });
+
+ const deployment = findDeployment();
+ expect(deployment.exists()).toBe(false);
+ });
+ });
+
+ describe('upcoming deployment', () => {
+ it('should pass the upcoming deployment to the deployment component when it exists', () => {
+ const upcomingDeployment = resolvedEnvironment.lastDeployment;
+ const environment = { ...resolvedEnvironment, lastDeployment: null, upcomingDeployment };
+ wrapper = createWrapper({
+ propsData: { environment },
+ apolloProvider: createApolloProvider(),
+ });
+
+ const deployment = findDeployment();
+ expect(deployment.props('deployment')).toEqual(upcomingDeployment);
+ });
+ it('should not show the upcoming deployment when it is missing', () => {
+ const environment = {
+ ...resolvedEnvironment,
+ lastDeployment: null,
+ upcomingDeployment: null,
+ };
+
+ wrapper = createWrapper({
+ propsData: { environment },
+ apolloProvider: createApolloProvider(),
+ });
+
+ const deployment = findDeployment();
+ expect(deployment.exists()).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/environments/new_environments_app_spec.js b/spec/frontend/environments/new_environments_app_spec.js
index 1e9bd4d64c9..c9eccc26694 100644
--- a/spec/frontend/environments/new_environments_app_spec.js
+++ b/spec/frontend/environments/new_environments_app_spec.js
@@ -8,7 +8,9 @@ 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';
+import EnvironmentsItem from '~/environments/components/new_environment_item.vue';
+import StopEnvironmentModal from '~/environments/components/stop_environment_modal.vue';
+import { resolvedEnvironmentsApp, resolvedFolder, resolvedEnvironment } from './graphql/mock_data';
Vue.use(VueApollo);
@@ -17,6 +19,7 @@ describe('~/environments/components/new_environments_app.vue', () => {
let environmentAppMock;
let environmentFolderMock;
let paginationMock;
+ let environmentToStopMock;
const createApolloProvider = () => {
const mockResolvers = {
@@ -24,6 +27,7 @@ describe('~/environments/components/new_environments_app.vue', () => {
environmentApp: environmentAppMock,
folder: environmentFolderMock,
pageInfo: paginationMock,
+ environmentToStop: environmentToStopMock,
},
};
@@ -45,6 +49,7 @@ describe('~/environments/components/new_environments_app.vue', () => {
provide = {},
environmentsApp,
folder,
+ environmentToStop = {},
pageInfo = {
total: 20,
perPage: 5,
@@ -58,6 +63,7 @@ describe('~/environments/components/new_environments_app.vue', () => {
environmentAppMock.mockReturnValue(environmentsApp);
environmentFolderMock.mockReturnValue(folder);
paginationMock.mockReturnValue(pageInfo);
+ environmentToStopMock.mockReturnValue(environmentToStop);
const apolloProvider = createApolloProvider();
wrapper = createWrapper({ apolloProvider, provide });
@@ -68,6 +74,7 @@ describe('~/environments/components/new_environments_app.vue', () => {
beforeEach(() => {
environmentAppMock = jest.fn();
environmentFolderMock = jest.fn();
+ environmentToStopMock = jest.fn();
paginationMock = jest.fn();
});
@@ -87,6 +94,18 @@ describe('~/environments/components/new_environments_app.vue', () => {
expect(text).not.toContainEqual(expect.stringMatching('production'));
});
+ it('should show all the environments that are fetched', async () => {
+ await createWrapperWithMocked({
+ environmentsApp: resolvedEnvironmentsApp,
+ folder: resolvedFolder,
+ });
+
+ const text = wrapper.findAllComponents(EnvironmentsItem).wrappers.map((w) => w.text());
+
+ expect(text).not.toContainEqual(expect.stringMatching('review'));
+ expect(text).toContainEqual(expect.stringMatching('production'));
+ });
+
it('should show a button to create a new environment', async () => {
await createWrapperWithMocked({
environmentsApp: resolvedEnvironmentsApp,
@@ -168,13 +187,27 @@ describe('~/environments/components/new_environments_app.vue', () => {
expect(environmentAppMock).toHaveBeenCalledWith(
expect.anything(),
- expect.objectContaining({ scope: 'stopped' }),
+ expect.objectContaining({ scope: 'stopped', page: 1 }),
expect.anything(),
expect.anything(),
);
});
});
+ describe('modals', () => {
+ it('should pass the environment to stop to the stop environment modal', async () => {
+ await createWrapperWithMocked({
+ environmentsApp: resolvedEnvironmentsApp,
+ folder: resolvedFolder,
+ environmentToStop: resolvedEnvironment,
+ });
+
+ const modal = wrapper.findComponent(StopEnvironmentModal);
+
+ expect(modal.props('environment')).toMatchObject(resolvedEnvironment);
+ });
+ });
+
describe('pagination', () => {
it('should sync page from query params on load', async () => {
await createWrapperWithMocked({