diff options
Diffstat (limited to 'spec/frontend/environments/environment_details')
6 files changed, 422 insertions, 0 deletions
diff --git a/spec/frontend/environments/environment_details/deployment_job_spec.js b/spec/frontend/environments/environment_details/deployment_job_spec.js new file mode 100644 index 00000000000..9bb61abb293 --- /dev/null +++ b/spec/frontend/environments/environment_details/deployment_job_spec.js @@ -0,0 +1,49 @@ +import { GlTruncate, GlLink, GlBadge } from '@gitlab/ui'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; +import DeploymentJob from '~/environments/environment_details/components/deployment_job.vue'; + +describe('app/assets/javascripts/environments/environment_details/components/deployment_job.vue', () => { + const jobData = { + webPath: 'http://example.com', + label: 'example job', + }; + let wrapper; + + const createWrapper = ({ job }) => { + return mountExtended(DeploymentJob, { + propsData: { + job, + }, + }); + }; + + describe('when the job data exists', () => { + beforeEach(() => { + wrapper = createWrapper({ job: jobData }); + }); + + it('should render a link with a correct href', () => { + const jobLink = wrapper.findComponent(GlLink); + expect(jobLink.exists()).toBe(true); + expect(jobLink.attributes().href).toBe(jobData.webPath); + }); + it('should render a truncated label', () => { + const truncatedLabel = wrapper.findComponent(GlTruncate); + expect(truncatedLabel.exists()).toBe(true); + expect(truncatedLabel.props().text).toBe(jobData.label); + }); + }); + + describe('when the job data does not exist', () => { + beforeEach(() => { + wrapper = createWrapper({ job: null }); + }); + + it('should render a badge with the text "API"', () => { + const badge = wrapper.findComponent(GlBadge); + expect(badge.exists()).toBe(true); + expect(badge.props().variant).toBe('info'); + expect(badge.text()).toBe('API'); + }); + }); +}); diff --git a/spec/frontend/environments/environment_details/deployment_status_link_spec.js b/spec/frontend/environments/environment_details/deployment_status_link_spec.js new file mode 100644 index 00000000000..5db7740423a --- /dev/null +++ b/spec/frontend/environments/environment_details/deployment_status_link_spec.js @@ -0,0 +1,57 @@ +import { mountExtended } from 'helpers/vue_test_utils_helper'; +import DeploymentStatusLink from '~/environments/environment_details/components/deployment_status_link.vue'; +import DeploymentStatusBadge from '~/environments/components/deployment_status_badge.vue'; + +describe('app/assets/javascripts/environments/environment_details/components/deployment_status_link.vue', () => { + const testData = { + webPath: 'http://example.com', + status: 'success', + }; + let wrapper; + + const createWrapper = (props) => { + return mountExtended(DeploymentStatusLink, { + propsData: props, + }); + }; + + describe('when the job link exists', () => { + beforeEach(() => { + wrapper = createWrapper({ + deploymentJob: { webPath: testData.webPath }, + status: testData.status, + }); + }); + + it('should render a link with a correct href', () => { + const jobLink = wrapper.findByTestId('deployment-status-job-link'); + expect(jobLink.exists()).toBe(true); + expect(jobLink.attributes().href).toBe(testData.webPath); + }); + + it('should render a status badge', () => { + const statusBadge = wrapper.findComponent(DeploymentStatusBadge); + expect(statusBadge.exists()).toBe(true); + expect(statusBadge.props().status).toBe(testData.status); + }); + }); + + describe('when no deployment job is provided', () => { + beforeEach(() => { + wrapper = createWrapper({ + status: testData.status, + }); + }); + + it('should render a link with a correct href', () => { + const jobLink = wrapper.findByTestId('deployment-status-job-link'); + expect(jobLink.exists()).toBe(false); + }); + + it('should render only a status badge', () => { + const statusBadge = wrapper.findComponent(DeploymentStatusBadge); + expect(statusBadge.exists()).toBe(true); + expect(statusBadge.props().status).toBe(testData.status); + }); + }); +}); diff --git a/spec/frontend/environments/environment_details/deployment_triggerer_spec.js b/spec/frontend/environments/environment_details/deployment_triggerer_spec.js new file mode 100644 index 00000000000..48af82661bf --- /dev/null +++ b/spec/frontend/environments/environment_details/deployment_triggerer_spec.js @@ -0,0 +1,51 @@ +import { GlAvatar, GlAvatarLink } from '@gitlab/ui'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; +import DeploymentTriggerer from '~/environments/environment_details/components/deployment_triggerer.vue'; + +describe('app/assets/javascripts/environments/environment_details/components/deployment_triggerer.vue', () => { + const triggererData = { + id: 'gid://gitlab/User/1', + webUrl: 'http://gdk.test:3000/root', + name: 'Administrator', + avatarUrl: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + }; + let wrapper; + + const createWrapper = ({ triggerer }) => { + return mountExtended(DeploymentTriggerer, { + propsData: { + triggerer, + }, + }); + }; + + describe('when the triggerer data exists', () => { + beforeEach(() => { + wrapper = createWrapper({ triggerer: triggererData }); + }); + + it('should render an avatar link with a correct href', () => { + const triggererAvatarLink = wrapper.findComponent(GlAvatarLink); + expect(triggererAvatarLink.exists()).toBe(true); + expect(triggererAvatarLink.attributes().href).toBe(triggererData.webUrl); + }); + + it('should render an avatar', () => { + const triggererAvatar = wrapper.findComponent(GlAvatar); + expect(triggererAvatar.exists()).toBe(true); + expect(triggererAvatar.attributes().title).toBe(triggererData.name); + expect(triggererAvatar.props().src).toBe(triggererData.avatarUrl); + }); + }); + + describe('when the triggerer data does not exist', () => { + beforeEach(() => { + wrapper = createWrapper({ triggerer: null }); + }); + + it('should render nothing', () => { + const avatarLink = wrapper.findComponent(GlAvatarLink); + expect(avatarLink.exists()).toBe(false); + }); + }); +}); diff --git a/spec/frontend/environments/environment_details/empty_state_spec.js b/spec/frontend/environments/environment_details/empty_state_spec.js new file mode 100644 index 00000000000..aaf597d68ed --- /dev/null +++ b/spec/frontend/environments/environment_details/empty_state_spec.js @@ -0,0 +1,39 @@ +import { GlEmptyState } from '@gitlab/ui'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; +import EmptyState from '~/environments/environment_details/empty_state.vue'; +import { + translations, + environmentsHelpPagePath, + codeBlockPlaceholders, +} from '~/environments/environment_details/constants'; + +describe('~/environments/environment_details/empty_state.vue', () => { + let wrapper; + + const createWrapper = () => { + return mountExtended(EmptyState); + }; + + describe('when Empty State is rendered for environment details page', () => { + beforeEach(() => { + wrapper = createWrapper(); + }); + + it('should render the proper title', () => { + expect(wrapper.text()).toContain(translations.emptyStateTitle); + }); + + it('should render GlEmptyState component with correct props', () => { + const glEmptyStateComponent = wrapper.findComponent(GlEmptyState); + expect(glEmptyStateComponent.props().primaryButtonText).toBe( + translations.emptyStatePrimaryButton, + ); + expect(glEmptyStateComponent.props().primaryButtonLink).toBe(environmentsHelpPagePath); + }); + + it('should render formatted description', () => { + expect(wrapper.text()).not.toContain(codeBlockPlaceholders.code[0]); + expect(wrapper.text()).not.toContain(codeBlockPlaceholders.code[1]); + }); + }); +}); diff --git a/spec/frontend/environments/environment_details/page_spec.js b/spec/frontend/environments/environment_details/page_spec.js new file mode 100644 index 00000000000..3a1a3238abe --- /dev/null +++ b/spec/frontend/environments/environment_details/page_spec.js @@ -0,0 +1,69 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import { GlLoadingIcon, GlTableLite } from '@gitlab/ui'; +import resolvedEnvironmentDetails from 'test_fixtures/graphql/environments/graphql/queries/environment_details.query.graphql.json'; +import emptyEnvironmentDetails from 'test_fixtures/graphql/environments/graphql/queries/environment_details.query.graphql.empty.json'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; +import EnvironmentsDetailPage from '~/environments/environment_details/index.vue'; +import EmptyState from '~/environments/environment_details/empty_state.vue'; +import getEnvironmentDetails from '~/environments/graphql/queries/environment_details.query.graphql'; +import createMockApollo from '../../__helpers__/mock_apollo_helper'; +import waitForPromises from '../../__helpers__/wait_for_promises'; + +describe('~/environments/environment_details/page.vue', () => { + Vue.use(VueApollo); + + let wrapper; + + const defaultWrapperParameters = { + resolvedData: resolvedEnvironmentDetails, + }; + + const createWrapper = ({ resolvedData } = defaultWrapperParameters) => { + const mockApollo = createMockApollo([ + [getEnvironmentDetails, jest.fn().mockResolvedValue(resolvedData)], + ]); + + return mountExtended(EnvironmentsDetailPage, { + apolloProvider: mockApollo, + propsData: { + projectFullPath: 'gitlab-group/test-project', + environmentName: 'test-environment-name', + }, + }); + }; + + describe('when fetching data', () => { + it('should show a loading indicator', () => { + wrapper = createWrapper(); + + expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true); + expect(wrapper.findComponent(GlTableLite).exists()).not.toBe(true); + }); + }); + + describe('when data is fetched', () => { + describe('and there are deployments', () => { + beforeEach(async () => { + wrapper = createWrapper(); + await waitForPromises(); + }); + it('should render a table when query is loaded', async () => { + expect(wrapper.findComponent(GlLoadingIcon).exists()).not.toBe(true); + expect(wrapper.findComponent(GlTableLite).exists()).toBe(true); + }); + }); + + describe('and there are no deployments', () => { + beforeEach(async () => { + wrapper = createWrapper({ resolvedData: emptyEnvironmentDetails }); + await waitForPromises(); + }); + + it('should render empty state component', async () => { + expect(wrapper.findComponent(GlTableLite).exists()).toBe(false); + expect(wrapper.findComponent(EmptyState).exists()).toBe(true); + }); + }); + }); +}); diff --git a/spec/frontend/environments/environment_details/pagination_spec.js b/spec/frontend/environments/environment_details/pagination_spec.js new file mode 100644 index 00000000000..107f3c3dd5e --- /dev/null +++ b/spec/frontend/environments/environment_details/pagination_spec.js @@ -0,0 +1,157 @@ +import { GlKeysetPagination } from '@gitlab/ui'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; +import Pagination from '~/environments/environment_details/pagination.vue'; + +describe('~/environments/environment_details/pagniation.vue', () => { + const mockRouter = { + push: jest.fn(), + }; + + const pageInfo = { + startCursor: 'eyJpZCI6IjE2In0', + endCursor: 'eyJpZCI6IjIifQ', + hasNextPage: true, + hasPreviousPage: true, + }; + let wrapper; + + const createWrapper = (pageInfoProp) => { + return mountExtended(Pagination, { + propsData: { + pageInfo: pageInfoProp, + }, + mocks: { + $router: mockRouter, + }, + }); + }; + + describe('when neither next nor previous page exists', () => { + beforeEach(() => { + const emptyPageInfo = { ...pageInfo, hasPreviousPage: false, hasNextPage: false }; + wrapper = createWrapper(emptyPageInfo); + }); + + it('should not render pagination component', () => { + expect(wrapper.html()).toBe(''); + }); + }); + + describe('when Pagination is rendered for environment details page', () => { + beforeEach(() => { + wrapper = createWrapper(pageInfo); + }); + + it('should pass correct props to keyset pagination', () => { + const glPagination = wrapper.findComponent(GlKeysetPagination); + expect(glPagination.exists()).toBe(true); + expect(glPagination.props()).toEqual(expect.objectContaining(pageInfo)); + }); + + describe.each([ + { + testPageInfo: pageInfo, + expectedAfter: `after=${pageInfo.endCursor}`, + expectedBefore: `before=${pageInfo.startCursor}`, + }, + { + testPageInfo: { ...pageInfo, hasNextPage: true, hasPreviousPage: false }, + expectedAfter: `after=${pageInfo.endCursor}`, + expectedBefore: '', + }, + { + testPageInfo: { ...pageInfo, hasNextPage: false, hasPreviousPage: true }, + expectedAfter: '', + expectedBefore: `before=${pageInfo.startCursor}`, + }, + ])( + 'button links generation for $testPageInfo', + ({ testPageInfo, expectedAfter, expectedBefore }) => { + beforeEach(() => { + wrapper = createWrapper(testPageInfo); + }); + + it(`should have button links defined as ${expectedAfter || 'empty'} and + ${expectedBefore || 'empty'}`, () => { + const glPagination = wrapper.findComponent(GlKeysetPagination); + expect(glPagination.props().prevButtonLink).toContain(expectedBefore); + expect(glPagination.props().nextButtonLink).toContain(expectedAfter); + }); + }, + ); + + describe.each([ + { + clickEvent: { + shiftKey: false, + ctrlKey: false, + altKey: false, + metaKey: false, + }, + isDefaultPrevented: true, + }, + { + clickEvent: { + shiftKey: true, + ctrlKey: false, + altKey: false, + metaKey: false, + }, + isDefaultPrevented: false, + }, + { + clickEvent: { + shiftKey: false, + ctrlKey: true, + altKey: false, + metaKey: false, + }, + isDefaultPrevented: false, + }, + { + clickEvent: { + shiftKey: false, + ctrlKey: false, + altKey: true, + metaKey: false, + }, + isDefaultPrevented: false, + }, + { + clickEvent: { + shiftKey: false, + ctrlKey: false, + altKey: false, + metaKey: true, + }, + isDefaultPrevented: false, + }, + ])( + 'when a pagination button is clicked with $clickEvent', + ({ clickEvent, isDefaultPrevented }) => { + let clickEventMock; + beforeEach(() => { + clickEventMock = { ...clickEvent, preventDefault: jest.fn() }; + }); + + it(`should ${isDefaultPrevented ? '' : 'not '}prevent default event`, () => { + const pagination = wrapper.findComponent(GlKeysetPagination); + pagination.vm.$emit('click', clickEventMock); + expect(clickEventMock.preventDefault).toHaveBeenCalledTimes(isDefaultPrevented ? 1 : 0); + }); + }, + ); + + it('should navigate to a correct previous page', () => { + const pagination = wrapper.findComponent(GlKeysetPagination); + pagination.vm.$emit('prev', pageInfo.startCursor); + expect(mockRouter.push).toHaveBeenCalledWith({ query: { before: pageInfo.startCursor } }); + }); + + it('should navigate to a correct next page', () => { + const pagination = wrapper.findComponent(GlKeysetPagination); + pagination.vm.$emit('next', pageInfo.endCursor); + expect(mockRouter.push).toHaveBeenCalledWith({ query: { after: pageInfo.endCursor } }); + }); + }); +}); |