diff options
Diffstat (limited to 'spec/frontend/runner/runner_list')
-rw-r--r-- | spec/frontend/runner/runner_list/runner_list_app_spec.js | 237 | ||||
-rw-r--r-- | spec/frontend/runner/runner_list/runner_search_utils_spec.js | 272 |
2 files changed, 0 insertions, 509 deletions
diff --git a/spec/frontend/runner/runner_list/runner_list_app_spec.js b/spec/frontend/runner/runner_list/runner_list_app_spec.js deleted file mode 100644 index 54b7d1f1bdb..00000000000 --- a/spec/frontend/runner/runner_list/runner_list_app_spec.js +++ /dev/null @@ -1,237 +0,0 @@ -import { createLocalVue, mount, shallowMount } from '@vue/test-utils'; -import VueApollo from 'vue-apollo'; -import createMockApollo from 'helpers/mock_apollo_helper'; -import { TEST_HOST } from 'helpers/test_constants'; -import waitForPromises from 'helpers/wait_for_promises'; -import createFlash from '~/flash'; -import { updateHistory } from '~/lib/utils/url_utility'; - -import RunnerFilteredSearchBar from '~/runner/components/runner_filtered_search_bar.vue'; -import RunnerList from '~/runner/components/runner_list.vue'; -import RunnerManualSetupHelp from '~/runner/components/runner_manual_setup_help.vue'; -import RunnerPagination from '~/runner/components/runner_pagination.vue'; -import RunnerTypeHelp from '~/runner/components/runner_type_help.vue'; - -import { - CREATED_ASC, - CREATED_DESC, - DEFAULT_SORT, - INSTANCE_TYPE, - PARAM_KEY_STATUS, - STATUS_ACTIVE, - RUNNER_PAGE_SIZE, -} from '~/runner/constants'; -import getRunnersQuery from '~/runner/graphql/get_runners.query.graphql'; -import RunnerListApp from '~/runner/runner_list/runner_list_app.vue'; -import { captureException } from '~/runner/sentry_utils'; - -import { runnersData, runnersDataPaginated } from '../mock_data'; - -const mockRegistrationToken = 'MOCK_REGISTRATION_TOKEN'; -const mockActiveRunnersCount = 2; - -jest.mock('~/flash'); -jest.mock('~/runner/sentry_utils'); -jest.mock('~/lib/utils/url_utility', () => ({ - ...jest.requireActual('~/lib/utils/url_utility'), - updateHistory: jest.fn(), -})); - -const localVue = createLocalVue(); -localVue.use(VueApollo); - -describe('RunnerListApp', () => { - let wrapper; - let mockRunnersQuery; - let originalLocation; - - const findRunnerTypeHelp = () => wrapper.findComponent(RunnerTypeHelp); - const findRunnerManualSetupHelp = () => wrapper.findComponent(RunnerManualSetupHelp); - const findRunnerList = () => wrapper.findComponent(RunnerList); - const findRunnerPagination = () => wrapper.findComponent(RunnerPagination); - const findRunnerFilteredSearchBar = () => wrapper.findComponent(RunnerFilteredSearchBar); - - const createComponentWithApollo = ({ props = {}, mountFn = shallowMount } = {}) => { - const handlers = [[getRunnersQuery, mockRunnersQuery]]; - - wrapper = mountFn(RunnerListApp, { - localVue, - apolloProvider: createMockApollo(handlers), - propsData: { - activeRunnersCount: mockActiveRunnersCount, - registrationToken: mockRegistrationToken, - ...props, - }, - }); - }; - - const setQuery = (query) => { - window.location.href = `${TEST_HOST}/admin/runners?${query}`; - window.location.search = query; - }; - - beforeAll(() => { - originalLocation = window.location; - Object.defineProperty(window, 'location', { writable: true, value: { href: '', search: '' } }); - }); - - afterAll(() => { - window.location = originalLocation; - }); - - beforeEach(async () => { - setQuery(''); - - mockRunnersQuery = jest.fn().mockResolvedValue(runnersData); - createComponentWithApollo(); - await waitForPromises(); - }); - - afterEach(() => { - mockRunnersQuery.mockReset(); - wrapper.destroy(); - }); - - it('shows the runners list', () => { - expect(runnersData.data.runners.nodes).toMatchObject(findRunnerList().props('runners')); - }); - - it('requests the runners with no filters', () => { - expect(mockRunnersQuery).toHaveBeenLastCalledWith({ - status: undefined, - type: undefined, - sort: DEFAULT_SORT, - first: RUNNER_PAGE_SIZE, - }); - }); - - it('shows the runner type help', () => { - expect(findRunnerTypeHelp().exists()).toBe(true); - }); - - it('shows the runner setup instructions', () => { - expect(findRunnerManualSetupHelp().exists()).toBe(true); - expect(findRunnerManualSetupHelp().props('registrationToken')).toBe(mockRegistrationToken); - }); - - describe('when a filter is preselected', () => { - beforeEach(async () => { - setQuery(`?status[]=${STATUS_ACTIVE}&runner_type[]=${INSTANCE_TYPE}&tag[]=tag1`); - - createComponentWithApollo(); - await waitForPromises(); - }); - - it('sets the filters in the search bar', () => { - expect(findRunnerFilteredSearchBar().props('value')).toEqual({ - filters: [ - { type: 'status', value: { data: STATUS_ACTIVE, operator: '=' } }, - { type: 'runner_type', value: { data: INSTANCE_TYPE, operator: '=' } }, - { type: 'tag', value: { data: 'tag1', operator: '=' } }, - ], - sort: 'CREATED_DESC', - pagination: { page: 1 }, - }); - }); - - it('requests the runners with filter parameters', () => { - expect(mockRunnersQuery).toHaveBeenLastCalledWith({ - status: STATUS_ACTIVE, - type: INSTANCE_TYPE, - tagList: ['tag1'], - sort: DEFAULT_SORT, - first: RUNNER_PAGE_SIZE, - }); - }); - }); - - describe('when a filter is selected by the user', () => { - beforeEach(() => { - findRunnerFilteredSearchBar().vm.$emit('input', { - filters: [{ type: PARAM_KEY_STATUS, value: { data: 'ACTIVE', operator: '=' } }], - sort: CREATED_ASC, - }); - }); - - it('updates the browser url', () => { - expect(updateHistory).toHaveBeenLastCalledWith({ - title: expect.any(String), - url: 'http://test.host/admin/runners?status[]=ACTIVE&sort=CREATED_ASC', - }); - }); - - it('requests the runners with filters', () => { - expect(mockRunnersQuery).toHaveBeenLastCalledWith({ - status: STATUS_ACTIVE, - sort: CREATED_ASC, - first: RUNNER_PAGE_SIZE, - }); - }); - }); - - describe('when no runners are found', () => { - beforeEach(async () => { - mockRunnersQuery = jest.fn().mockResolvedValue({ data: { runners: { nodes: [] } } }); - createComponentWithApollo(); - await waitForPromises(); - }); - - it('shows a message for no results', async () => { - expect(wrapper.text()).toContain('No runners found'); - }); - }); - - it('when runners have not loaded, shows a loading state', () => { - createComponentWithApollo(); - expect(findRunnerList().props('loading')).toBe(true); - }); - - describe('when runners query fails', () => { - beforeEach(async () => { - mockRunnersQuery = jest.fn().mockRejectedValue(new Error('Error!')); - createComponentWithApollo(); - - await waitForPromises(); - }); - - it('error is reported to sentry', async () => { - expect(captureException).toHaveBeenCalledWith({ - error: new Error('Network error: Error!'), - component: 'RunnerListApp', - }); - }); - - it('error is shown to the user', async () => { - expect(createFlash).toHaveBeenCalledTimes(1); - }); - }); - - describe('Pagination', () => { - beforeEach(() => { - mockRunnersQuery = jest.fn().mockResolvedValue(runnersDataPaginated); - - createComponentWithApollo({ mountFn: mount }); - }); - - it('more pages can be selected', () => { - expect(findRunnerPagination().text()).toMatchInterpolatedText('Prev Next'); - }); - - it('cannot navigate to the previous page', () => { - expect(findRunnerPagination().find('[aria-disabled]').text()).toBe('Prev'); - }); - - it('navigates to the next page', async () => { - const nextPageBtn = findRunnerPagination().find('a'); - expect(nextPageBtn.text()).toBe('Next'); - - await nextPageBtn.trigger('click'); - - expect(mockRunnersQuery).toHaveBeenLastCalledWith({ - sort: CREATED_DESC, - first: RUNNER_PAGE_SIZE, - after: runnersDataPaginated.data.runners.pageInfo.endCursor, - }); - }); - }); -}); diff --git a/spec/frontend/runner/runner_list/runner_search_utils_spec.js b/spec/frontend/runner/runner_list/runner_search_utils_spec.js deleted file mode 100644 index e7969676549..00000000000 --- a/spec/frontend/runner/runner_list/runner_search_utils_spec.js +++ /dev/null @@ -1,272 +0,0 @@ -import { RUNNER_PAGE_SIZE } from '~/runner/constants'; -import { - fromUrlQueryToSearch, - fromSearchToUrl, - fromSearchToVariables, -} from '~/runner/runner_list/runner_search_utils'; - -describe('search_params.js', () => { - const examples = [ - { - name: 'a default query', - urlQuery: '', - search: { filters: [], pagination: { page: 1 }, sort: 'CREATED_DESC' }, - graphqlVariables: { sort: 'CREATED_DESC', first: RUNNER_PAGE_SIZE }, - }, - { - name: 'a single status', - urlQuery: '?status[]=ACTIVE', - search: { - filters: [{ type: 'status', value: { data: 'ACTIVE', operator: '=' } }], - pagination: { page: 1 }, - sort: 'CREATED_DESC', - }, - graphqlVariables: { status: 'ACTIVE', sort: 'CREATED_DESC', first: RUNNER_PAGE_SIZE }, - }, - { - name: 'a single term text search', - urlQuery: '?search=something', - search: { - filters: [ - { - type: 'filtered-search-term', - value: { data: 'something' }, - }, - ], - pagination: { page: 1 }, - sort: 'CREATED_DESC', - }, - graphqlVariables: { search: 'something', sort: 'CREATED_DESC', first: RUNNER_PAGE_SIZE }, - }, - { - name: 'a two terms text search', - urlQuery: '?search=something+else', - search: { - filters: [ - { - type: 'filtered-search-term', - value: { data: 'something' }, - }, - { - type: 'filtered-search-term', - value: { data: 'else' }, - }, - ], - pagination: { page: 1 }, - sort: 'CREATED_DESC', - }, - graphqlVariables: { search: 'something else', sort: 'CREATED_DESC', first: RUNNER_PAGE_SIZE }, - }, - { - name: 'single instance type', - urlQuery: '?runner_type[]=INSTANCE_TYPE', - search: { - filters: [{ type: 'runner_type', value: { data: 'INSTANCE_TYPE', operator: '=' } }], - pagination: { page: 1 }, - sort: 'CREATED_DESC', - }, - graphqlVariables: { type: 'INSTANCE_TYPE', sort: 'CREATED_DESC', first: RUNNER_PAGE_SIZE }, - }, - { - name: 'multiple runner status', - urlQuery: '?status[]=ACTIVE&status[]=PAUSED', - search: { - filters: [ - { type: 'status', value: { data: 'ACTIVE', operator: '=' } }, - { type: 'status', value: { data: 'PAUSED', operator: '=' } }, - ], - pagination: { page: 1 }, - sort: 'CREATED_DESC', - }, - graphqlVariables: { status: 'ACTIVE', sort: 'CREATED_DESC', first: RUNNER_PAGE_SIZE }, - }, - { - name: 'multiple status, a single instance type and a non default sort', - urlQuery: '?status[]=ACTIVE&runner_type[]=INSTANCE_TYPE&sort=CREATED_ASC', - search: { - filters: [ - { type: 'status', value: { data: 'ACTIVE', operator: '=' } }, - { type: 'runner_type', value: { data: 'INSTANCE_TYPE', operator: '=' } }, - ], - pagination: { page: 1 }, - sort: 'CREATED_ASC', - }, - graphqlVariables: { - status: 'ACTIVE', - type: 'INSTANCE_TYPE', - sort: 'CREATED_ASC', - first: RUNNER_PAGE_SIZE, - }, - }, - { - name: 'a tag', - urlQuery: '?tag[]=tag-1', - search: { - filters: [{ type: 'tag', value: { data: 'tag-1', operator: '=' } }], - pagination: { page: 1 }, - sort: 'CREATED_DESC', - }, - graphqlVariables: { - tagList: ['tag-1'], - first: 20, - sort: 'CREATED_DESC', - }, - }, - { - name: 'two tags', - urlQuery: '?tag[]=tag-1&tag[]=tag-2', - search: { - filters: [ - { type: 'tag', value: { data: 'tag-1', operator: '=' } }, - { type: 'tag', value: { data: 'tag-2', operator: '=' } }, - ], - pagination: { page: 1 }, - sort: 'CREATED_DESC', - }, - graphqlVariables: { - tagList: ['tag-1', 'tag-2'], - first: 20, - sort: 'CREATED_DESC', - }, - }, - { - name: 'the next page', - urlQuery: '?page=2&after=AFTER_CURSOR', - search: { filters: [], pagination: { page: 2, after: 'AFTER_CURSOR' }, sort: 'CREATED_DESC' }, - graphqlVariables: { sort: 'CREATED_DESC', after: 'AFTER_CURSOR', first: RUNNER_PAGE_SIZE }, - }, - { - name: 'the previous page', - urlQuery: '?page=2&before=BEFORE_CURSOR', - search: { - filters: [], - pagination: { page: 2, before: 'BEFORE_CURSOR' }, - sort: 'CREATED_DESC', - }, - graphqlVariables: { sort: 'CREATED_DESC', before: 'BEFORE_CURSOR', last: RUNNER_PAGE_SIZE }, - }, - { - name: 'the next page filtered by a status, an instance type, tags and a non default sort', - urlQuery: - '?status[]=ACTIVE&runner_type[]=INSTANCE_TYPE&tag[]=tag-1&tag[]=tag-2&sort=CREATED_ASC&page=2&after=AFTER_CURSOR', - search: { - filters: [ - { type: 'status', value: { data: 'ACTIVE', operator: '=' } }, - { type: 'runner_type', value: { data: 'INSTANCE_TYPE', operator: '=' } }, - { type: 'tag', value: { data: 'tag-1', operator: '=' } }, - { type: 'tag', value: { data: 'tag-2', operator: '=' } }, - ], - pagination: { page: 2, after: 'AFTER_CURSOR' }, - sort: 'CREATED_ASC', - }, - graphqlVariables: { - status: 'ACTIVE', - type: 'INSTANCE_TYPE', - tagList: ['tag-1', 'tag-2'], - sort: 'CREATED_ASC', - after: 'AFTER_CURSOR', - first: RUNNER_PAGE_SIZE, - }, - }, - ]; - - describe('fromUrlQueryToSearch', () => { - examples.forEach(({ name, urlQuery, search }) => { - it(`Converts ${name} to a search object`, () => { - expect(fromUrlQueryToSearch(urlQuery)).toEqual(search); - }); - }); - - it('When search params appear as array, they are concatenated', () => { - expect(fromUrlQueryToSearch('?search[]=my&search[]=text').filters).toEqual([ - { type: 'filtered-search-term', value: { data: 'my' } }, - { type: 'filtered-search-term', value: { data: 'text' } }, - ]); - }); - - it('When a page cannot be parsed as a number, it defaults to `1`', () => { - expect(fromUrlQueryToSearch('?page=NONSENSE&after=AFTER_CURSOR').pagination).toEqual({ - page: 1, - }); - }); - - it('When a page is less than 1, it defaults to `1`', () => { - expect(fromUrlQueryToSearch('?page=0&after=AFTER_CURSOR').pagination).toEqual({ - page: 1, - }); - }); - - it('When a page with no cursor is given, it defaults to `1`', () => { - expect(fromUrlQueryToSearch('?page=2').pagination).toEqual({ - page: 1, - }); - }); - }); - - describe('fromSearchToUrl', () => { - examples.forEach(({ name, urlQuery, search }) => { - it(`Converts ${name} to a url`, () => { - expect(fromSearchToUrl(search)).toEqual(`http://test.host/${urlQuery}`); - }); - }); - - it.each([ - 'http://test.host/?status[]=ACTIVE', - 'http://test.host/?runner_type[]=INSTANCE_TYPE', - 'http://test.host/?search=my_text', - ])('When a filter is removed, it is removed from the URL', (initalUrl) => { - const search = { filters: [], sort: 'CREATED_DESC' }; - const expectedUrl = `http://test.host/`; - - expect(fromSearchToUrl(search, initalUrl)).toEqual(expectedUrl); - }); - - it('When unrelated search parameter is present, it does not get removed', () => { - const initialUrl = `http://test.host/?unrelated=UNRELATED&status[]=ACTIVE`; - const search = { filters: [], sort: 'CREATED_DESC' }; - const expectedUrl = `http://test.host/?unrelated=UNRELATED`; - - expect(fromSearchToUrl(search, initialUrl)).toEqual(expectedUrl); - }); - }); - - describe('fromSearchToVariables', () => { - examples.forEach(({ name, graphqlVariables, search }) => { - it(`Converts ${name} to a GraphQL query variables object`, () => { - expect(fromSearchToVariables(search)).toEqual(graphqlVariables); - }); - }); - - it('When a search param is empty, it gets removed', () => { - expect( - fromSearchToVariables({ - filters: [ - { - type: 'filtered-search-term', - value: { data: '' }, - }, - ], - }), - ).toMatchObject({ - search: '', - }); - - expect( - fromSearchToVariables({ - filters: [ - { - type: 'filtered-search-term', - value: { data: 'something' }, - }, - { - type: 'filtered-search-term', - value: { data: '' }, - }, - ], - }), - ).toMatchObject({ - search: 'something', - }); - }); - }); -}); |