Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-08-23 18:10:43 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-08-23 18:10:43 +0300
commita7f478c9b1806a67ec9d991c3f54c242bb596f60 (patch)
treeb3a6ea0db1327002ced7d0bbc51bd37ef1abee4b /spec/frontend
parentd67a86595f0866927815e0945bff032a35c76da9 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend')
-rw-r--r--spec/frontend/fixtures/runner.rb47
-rw-r--r--spec/frontend/projects/storage_counter/app_spec.js22
-rw-r--r--spec/frontend/runner/admin_runners/admin_runners_app_spec.js97
-rw-r--r--spec/frontend/runner/components/runner_filtered_search_bar_spec.js33
-rw-r--r--spec/frontend/runner/components/runner_list_spec.js2
-rw-r--r--spec/frontend/runner/group_runners/group_runners_app_spec.js239
-rw-r--r--spec/frontend/runner/mock_data.js16
7 files changed, 399 insertions, 57 deletions
diff --git a/spec/frontend/fixtures/runner.rb b/spec/frontend/fixtures/runner.rb
index e29a58f43b9..d5d6f534def 100644
--- a/spec/frontend/fixtures/runner.rb
+++ b/spec/frontend/fixtures/runner.rb
@@ -14,6 +14,7 @@ RSpec.describe 'Runner (JavaScript fixtures)' do
let_it_be(:instance_runner) { create(:ci_runner, :instance, version: '1.0.0', revision: '123', description: 'Instance runner', ip_address: '127.0.0.1') }
let_it_be(:group_runner) { create(:ci_runner, :group, groups: [group], active: false, version: '2.0.0', revision: '456', description: 'Group runner', ip_address: '127.0.0.1') }
+ let_it_be(:group_runner_2) { create(:ci_runner, :group, groups: [group], active: false, version: '2.0.0', revision: '456', description: 'Group runner 2', ip_address: '127.0.0.1') }
let_it_be(:project_runner) { create(:ci_runner, :project, projects: [project], active: false, version: '2.0.0', revision: '456', description: 'Project runner', ip_address: '127.0.0.1') }
query_path = 'runner/graphql/'
@@ -27,14 +28,14 @@ RSpec.describe 'Runner (JavaScript fixtures)' do
remove_repository(project)
end
- before do
- sign_in(admin)
- enable_admin_mode!(admin)
- end
-
describe GraphQL::Query, type: :request do
get_runners_query_name = 'get_runners.query.graphql'
+ before do
+ sign_in(admin)
+ enable_admin_mode!(admin)
+ end
+
let_it_be(:query) do
get_graphql_query_as_string("#{query_path}#{get_runners_query_name}")
end
@@ -55,6 +56,11 @@ RSpec.describe 'Runner (JavaScript fixtures)' do
describe GraphQL::Query, type: :request do
get_runner_query_name = 'get_runner.query.graphql'
+ before do
+ sign_in(admin)
+ enable_admin_mode!(admin)
+ end
+
let_it_be(:query) do
get_graphql_query_as_string("#{query_path}#{get_runner_query_name}")
end
@@ -67,4 +73,35 @@ RSpec.describe 'Runner (JavaScript fixtures)' do
expect_graphql_errors_to_be_empty
end
end
+
+ describe GraphQL::Query, type: :request do
+ get_group_runners_query_name = 'get_group_runners.query.graphql'
+
+ let_it_be(:group_owner) { create(:user) }
+
+ before do
+ group.add_owner(group_owner)
+ end
+
+ let_it_be(:query) do
+ get_graphql_query_as_string("#{query_path}#{get_group_runners_query_name}")
+ end
+
+ it "#{fixtures_path}#{get_group_runners_query_name}.json" do
+ post_graphql(query, current_user: group_owner, variables: {
+ groupFullPath: group.full_path
+ })
+
+ expect_graphql_errors_to_be_empty
+ end
+
+ it "#{fixtures_path}#{get_group_runners_query_name}.paginated.json" do
+ post_graphql(query, current_user: group_owner, variables: {
+ groupFullPath: group.full_path,
+ first: 1
+ })
+
+ expect_graphql_errors_to_be_empty
+ end
+ end
end
diff --git a/spec/frontend/projects/storage_counter/app_spec.js b/spec/frontend/projects/storage_counter/app_spec.js
new file mode 100644
index 00000000000..cf71a782f21
--- /dev/null
+++ b/spec/frontend/projects/storage_counter/app_spec.js
@@ -0,0 +1,22 @@
+import { shallowMount } from '@vue/test-utils';
+import StorageCounterApp from '~/projects/storage_counter/components/app.vue';
+
+describe('Storage counter app', () => {
+ let wrapper;
+
+ const createComponent = (propsData = {}) => {
+ wrapper = shallowMount(StorageCounterApp, { propsData });
+ };
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders app successfully', () => {
+ expect(wrapper.text()).toBe('Usage');
+ });
+});
diff --git a/spec/frontend/runner/admin_runners/admin_runners_app_spec.js b/spec/frontend/runner/admin_runners/admin_runners_app_spec.js
index c1596711be7..3292f635f6b 100644
--- a/spec/frontend/runner/admin_runners/admin_runners_app_spec.js
+++ b/spec/frontend/runner/admin_runners/admin_runners_app_spec.js
@@ -2,6 +2,7 @@ import { createLocalVue, mount, shallowMount } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import setWindowLocation from 'helpers/set_window_location_helper';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash';
import { updateHistory } from '~/lib/utils/url_utility';
@@ -14,16 +15,20 @@ import RunnerPagination from '~/runner/components/runner_pagination.vue';
import RunnerTypeHelp from '~/runner/components/runner_type_help.vue';
import {
+ ADMIN_FILTERED_SEARCH_NAMESPACE,
CREATED_ASC,
CREATED_DESC,
DEFAULT_SORT,
INSTANCE_TYPE,
PARAM_KEY_STATUS,
+ PARAM_KEY_RUNNER_TYPE,
+ PARAM_KEY_TAG,
STATUS_ACTIVE,
RUNNER_PAGE_SIZE,
} from '~/runner/constants';
import getRunnersQuery from '~/runner/graphql/get_runners.query.graphql';
import { captureException } from '~/runner/sentry_utils';
+import FilteredSearch from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
import { runnersData, runnersDataPaginated } from '../mock_data';
@@ -47,10 +52,14 @@ describe('AdminRunnersApp', () => {
const findRunnerTypeHelp = () => wrapper.findComponent(RunnerTypeHelp);
const findRunnerManualSetupHelp = () => wrapper.findComponent(RunnerManualSetupHelp);
const findRunnerList = () => wrapper.findComponent(RunnerList);
- const findRunnerPagination = () => wrapper.findComponent(RunnerPagination);
+ const findRunnerPagination = () => extendedWrapper(wrapper.findComponent(RunnerPagination));
+ const findRunnerPaginationPrev = () =>
+ findRunnerPagination().findByLabelText('Go to previous page');
+ const findRunnerPaginationNext = () => findRunnerPagination().findByLabelText('Go to next page');
const findRunnerFilteredSearchBar = () => wrapper.findComponent(RunnerFilteredSearchBar);
+ const findFilteredSearch = () => wrapper.findComponent(FilteredSearch);
- const createComponentWithApollo = ({ props = {}, mountFn = shallowMount } = {}) => {
+ const createComponent = ({ props = {}, mountFn = shallowMount } = {}) => {
const handlers = [[getRunnersQuery, mockRunnersQuery]];
wrapper = mountFn(AdminRunnersApp, {
@@ -68,7 +77,7 @@ describe('AdminRunnersApp', () => {
setWindowLocation('/admin/runners');
mockRunnersQuery = jest.fn().mockResolvedValue(runnersData);
- createComponentWithApollo();
+ createComponent();
await waitForPromises();
});
@@ -77,8 +86,16 @@ describe('AdminRunnersApp', () => {
wrapper.destroy();
});
+ it('shows the runner type help', () => {
+ expect(findRunnerTypeHelp().exists()).toBe(true);
+ });
+
+ it('shows the runner setup instructions', () => {
+ expect(findRunnerManualSetupHelp().props('registrationToken')).toBe(mockRegistrationToken);
+ });
+
it('shows the runners list', () => {
- expect(runnersData.data.runners.nodes).toMatchObject(findRunnerList().props('runners'));
+ expect(findRunnerList().props('runners')).toEqual(runnersData.data.runners.nodes);
});
it('requests the runners with no filters', () => {
@@ -90,20 +107,38 @@ describe('AdminRunnersApp', () => {
});
});
- it('shows the runner type help', () => {
- expect(findRunnerTypeHelp().exists()).toBe(true);
+ it('sets tokens in the filtered search', () => {
+ createComponent({ mountFn: mount });
+
+ expect(findFilteredSearch().props('tokens')).toEqual([
+ expect.objectContaining({
+ type: PARAM_KEY_STATUS,
+ options: expect.any(Array),
+ }),
+ expect.objectContaining({
+ type: PARAM_KEY_RUNNER_TYPE,
+ options: expect.any(Array),
+ }),
+ expect.objectContaining({
+ type: PARAM_KEY_TAG,
+ recentTokenValuesStorageKey: `${ADMIN_FILTERED_SEARCH_NAMESPACE}-recent-tags`,
+ }),
+ ]);
});
- it('shows the runner setup instructions', () => {
- expect(findRunnerManualSetupHelp().exists()).toBe(true);
- expect(findRunnerManualSetupHelp().props('registrationToken')).toBe(mockRegistrationToken);
+ it('shows the active runner count', () => {
+ createComponent({ mountFn: mount });
+
+ expect(findRunnerFilteredSearchBar().text()).toMatch(
+ `Runners currently online: ${mockActiveRunnersCount}`,
+ );
});
describe('when a filter is preselected', () => {
beforeEach(async () => {
setWindowLocation(`?status[]=${STATUS_ACTIVE}&runner_type[]=${INSTANCE_TYPE}&tag[]=tag1`);
- createComponentWithApollo();
+ createComponent();
await waitForPromises();
});
@@ -133,7 +168,7 @@ describe('AdminRunnersApp', () => {
describe('when a filter is selected by the user', () => {
beforeEach(() => {
findRunnerFilteredSearchBar().vm.$emit('input', {
- filters: [{ type: PARAM_KEY_STATUS, value: { data: 'ACTIVE', operator: '=' } }],
+ filters: [{ type: PARAM_KEY_STATUS, value: { data: STATUS_ACTIVE, operator: '=' } }],
sort: CREATED_ASC,
});
});
@@ -154,11 +189,19 @@ describe('AdminRunnersApp', () => {
});
});
+ it('when runners have not loaded, shows a loading state', () => {
+ createComponent();
+ expect(findRunnerList().props('loading')).toBe(true);
+ });
+
describe('when no runners are found', () => {
beforeEach(async () => {
- mockRunnersQuery = jest.fn().mockResolvedValue({ data: { runners: { nodes: [] } } });
- createComponentWithApollo();
- await waitForPromises();
+ mockRunnersQuery = jest.fn().mockResolvedValue({
+ data: {
+ runners: { nodes: [] },
+ },
+ });
+ createComponent();
});
it('shows a message for no results', async () => {
@@ -166,17 +209,14 @@ describe('AdminRunnersApp', () => {
});
});
- it('when runners have not loaded, shows a loading state', () => {
- createComponentWithApollo();
- expect(findRunnerList().props('loading')).toBe(true);
- });
-
describe('when runners query fails', () => {
- beforeEach(async () => {
+ beforeEach(() => {
mockRunnersQuery = jest.fn().mockRejectedValue(new Error('Error!'));
- createComponentWithApollo();
+ createComponent();
+ });
- await waitForPromises();
+ it('error is shown to the user', async () => {
+ expect(createFlash).toHaveBeenCalledTimes(1);
});
it('error is reported to sentry', async () => {
@@ -185,17 +225,13 @@ describe('AdminRunnersApp', () => {
component: 'AdminRunnersApp',
});
});
-
- it('error is shown to the user', async () => {
- expect(createFlash).toHaveBeenCalledTimes(1);
- });
});
describe('Pagination', () => {
beforeEach(() => {
mockRunnersQuery = jest.fn().mockResolvedValue(runnersDataPaginated);
- createComponentWithApollo({ mountFn: mount });
+ createComponent({ mountFn: mount });
});
it('more pages can be selected', () => {
@@ -203,14 +239,11 @@ describe('AdminRunnersApp', () => {
});
it('cannot navigate to the previous page', () => {
- expect(findRunnerPagination().find('[aria-disabled]').text()).toBe('Prev');
+ expect(findRunnerPaginationPrev().attributes('aria-disabled')).toBe('true');
});
it('navigates to the next page', async () => {
- const nextPageBtn = findRunnerPagination().find('a');
- expect(nextPageBtn.text()).toBe('Next');
-
- await nextPageBtn.trigger('click');
+ await findRunnerPaginationNext().trigger('click');
expect(mockRunnersQuery).toHaveBeenLastCalledWith({
sort: CREATED_DESC,
diff --git a/spec/frontend/runner/components/runner_filtered_search_bar_spec.js b/spec/frontend/runner/components/runner_filtered_search_bar_spec.js
index 85cf7ea92df..46948af1f28 100644
--- a/spec/frontend/runner/components/runner_filtered_search_bar_spec.js
+++ b/spec/frontend/runner/components/runner_filtered_search_bar_spec.js
@@ -2,8 +2,16 @@ import { GlFilteredSearch, GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import RunnerFilteredSearchBar from '~/runner/components/runner_filtered_search_bar.vue';
+import { statusTokenConfig } from '~/runner/components/search_tokens/status_token_config';
import TagToken from '~/runner/components/search_tokens/tag_token.vue';
-import { PARAM_KEY_STATUS, PARAM_KEY_RUNNER_TYPE, PARAM_KEY_TAG } from '~/runner/constants';
+import { tagTokenConfig } from '~/runner/components/search_tokens/tag_token_config';
+import { typeTokenConfig } from '~/runner/components/search_tokens/type_token_config';
+import {
+ PARAM_KEY_STATUS,
+ PARAM_KEY_RUNNER_TYPE,
+ PARAM_KEY_TAG,
+ STATUS_ACTIVE,
+} from '~/runner/constants';
import FilteredSearch from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
@@ -13,12 +21,12 @@ describe('RunnerList', () => {
const findFilteredSearch = () => wrapper.findComponent(FilteredSearch);
const findGlFilteredSearch = () => wrapper.findComponent(GlFilteredSearch);
const findSortOptions = () => wrapper.findAllComponents(GlDropdownItem);
- const findActiveRunnersMessage = () => wrapper.findByTestId('active-runners-message');
+ const findActiveRunnersMessage = () => wrapper.findByTestId('runner-count');
const mockDefaultSort = 'CREATED_DESC';
const mockOtherSort = 'CONTACTED_DESC';
const mockFilters = [
- { type: PARAM_KEY_STATUS, value: { data: 'ACTIVE', operator: '=' } },
+ { type: PARAM_KEY_STATUS, value: { data: STATUS_ACTIVE, operator: '=' } },
{ type: 'filtered-search-term', value: { data: '' } },
];
const mockActiveRunnersCount = 2;
@@ -28,13 +36,16 @@ describe('RunnerList', () => {
shallowMount(RunnerFilteredSearchBar, {
propsData: {
namespace: 'runners',
+ tokens: [],
value: {
filters: [],
sort: mockDefaultSort,
},
- activeRunnersCount: mockActiveRunnersCount,
...props,
},
+ slots: {
+ 'runner-count': `Runners currently online: ${mockActiveRunnersCount}`,
+ },
stubs: {
FilteredSearch,
GlFilteredSearch,
@@ -64,12 +75,6 @@ describe('RunnerList', () => {
);
});
- it('Displays a large active runner count', () => {
- createComponent({ props: { activeRunnersCount: 2000 } });
-
- expect(findActiveRunnersMessage().text()).toBe('Runners currently online: 2,000');
- });
-
it('sets sorting options', () => {
const SORT_OPTIONS_COUNT = 2;
@@ -78,7 +83,13 @@ describe('RunnerList', () => {
expect(findSortOptions().at(1).text()).toBe('Last contact');
});
- it('sets tokens', () => {
+ it('sets tokens to the filtered search', () => {
+ createComponent({
+ props: {
+ tokens: [statusTokenConfig, typeTokenConfig, tagTokenConfig],
+ },
+ });
+
expect(findFilteredSearch().props('tokens')).toEqual([
expect.objectContaining({
type: PARAM_KEY_STATUS,
diff --git a/spec/frontend/runner/components/runner_list_spec.js b/spec/frontend/runner/components/runner_list_spec.js
index 5fff3581e39..344d1e5c150 100644
--- a/spec/frontend/runner/components/runner_list_spec.js
+++ b/spec/frontend/runner/components/runner_list_spec.js
@@ -56,7 +56,7 @@ describe('RunnerList', () => {
});
it('Displays a list of runners', () => {
- expect(findRows()).toHaveLength(3);
+ expect(findRows()).toHaveLength(4);
expect(findSkeletonLoader().exists()).toBe(false);
});
diff --git a/spec/frontend/runner/group_runners/group_runners_app_spec.js b/spec/frontend/runner/group_runners/group_runners_app_spec.js
index 6a0863e92b4..e80da40e3bd 100644
--- a/spec/frontend/runner/group_runners/group_runners_app_spec.js
+++ b/spec/frontend/runner/group_runners/group_runners_app_spec.js
@@ -1,26 +1,85 @@
-import { shallowMount } from '@vue/test-utils';
+import { createLocalVue, shallowMount, mount } from '@vue/test-utils';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import setWindowLocation from 'helpers/set_window_location_helper';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+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,
+ PARAM_KEY_RUNNER_TYPE,
+ STATUS_ACTIVE,
+ RUNNER_PAGE_SIZE,
+} from '~/runner/constants';
+import getGroupRunnersQuery from '~/runner/graphql/get_group_runners.query.graphql';
import GroupRunnersApp from '~/runner/group_runners/group_runners_app.vue';
+import { captureException } from '~/runner/sentry_utils';
+import FilteredSearch from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
+import { groupRunnersData, groupRunnersDataPaginated } from '../mock_data';
+
+const localVue = createLocalVue();
+localVue.use(VueApollo);
+const mockGroupFullPath = 'group1';
const mockRegistrationToken = 'AABBCC';
+const mockRunners = groupRunnersData.data.group.runners.nodes;
+const mockGroupRunnersLimitedCount = mockRunners.length;
+
+jest.mock('~/flash');
+jest.mock('~/runner/sentry_utils');
+jest.mock('~/lib/utils/url_utility', () => ({
+ ...jest.requireActual('~/lib/utils/url_utility'),
+ updateHistory: jest.fn(),
+}));
describe('GroupRunnersApp', () => {
let wrapper;
+ let mockGroupRunnersQuery;
const findRunnerTypeHelp = () => wrapper.findComponent(RunnerTypeHelp);
const findRunnerManualSetupHelp = () => wrapper.findComponent(RunnerManualSetupHelp);
+ const findRunnerList = () => wrapper.findComponent(RunnerList);
+ const findRunnerPagination = () => extendedWrapper(wrapper.findComponent(RunnerPagination));
+ const findRunnerPaginationPrev = () =>
+ findRunnerPagination().findByLabelText('Go to previous page');
+ const findRunnerPaginationNext = () => findRunnerPagination().findByLabelText('Go to next page');
+ const findRunnerFilteredSearchBar = () => wrapper.findComponent(RunnerFilteredSearchBar);
+ const findFilteredSearch = () => wrapper.findComponent(FilteredSearch);
+
+ const createComponent = ({ props = {}, mountFn = shallowMount } = {}) => {
+ const handlers = [[getGroupRunnersQuery, mockGroupRunnersQuery]];
- const createComponent = ({ mountFn = shallowMount } = {}) => {
wrapper = mountFn(GroupRunnersApp, {
+ localVue,
+ apolloProvider: createMockApollo(handlers),
propsData: {
registrationToken: mockRegistrationToken,
+ groupFullPath: mockGroupFullPath,
+ groupRunnersLimitedCount: mockGroupRunnersLimitedCount,
+ ...props,
},
});
};
- beforeEach(() => {
+ beforeEach(async () => {
+ setWindowLocation(`/groups/${mockGroupFullPath}/-/runners`);
+
+ mockGroupRunnersQuery = jest.fn().mockResolvedValue(groupRunnersData);
+
createComponent();
+ await waitForPromises();
});
it('shows the runner type help', () => {
@@ -28,7 +87,179 @@ describe('GroupRunnersApp', () => {
});
it('shows the runner setup instructions', () => {
- expect(findRunnerManualSetupHelp().exists()).toBe(true);
expect(findRunnerManualSetupHelp().props('registrationToken')).toBe(mockRegistrationToken);
});
+
+ it('shows the runners list', () => {
+ expect(findRunnerList().props('runners')).toEqual(groupRunnersData.data.group.runners.nodes);
+ });
+
+ it('requests the runners with group path and no other filters', () => {
+ expect(mockGroupRunnersQuery).toHaveBeenLastCalledWith({
+ groupFullPath: mockGroupFullPath,
+ status: undefined,
+ type: undefined,
+ sort: DEFAULT_SORT,
+ first: RUNNER_PAGE_SIZE,
+ });
+ });
+
+ it('sets tokens in the filtered search', () => {
+ createComponent({ mountFn: mount });
+
+ expect(findFilteredSearch().props('tokens')).toEqual([
+ expect.objectContaining({
+ type: PARAM_KEY_STATUS,
+ options: expect.any(Array),
+ }),
+ expect.objectContaining({
+ type: PARAM_KEY_RUNNER_TYPE,
+ options: expect.any(Array),
+ }),
+ ]);
+ });
+
+ describe('shows the active runner count', () => {
+ it('with a regular value', () => {
+ createComponent({ mountFn: mount });
+
+ expect(findRunnerFilteredSearchBar().text()).toMatch(
+ `Runners in this group: ${mockGroupRunnersLimitedCount}`,
+ );
+ });
+
+ it('at the limit', () => {
+ createComponent({ props: { groupRunnersLimitedCount: 1000 }, mountFn: mount });
+
+ expect(findRunnerFilteredSearchBar().text()).toMatch(`Runners in this group: 1,000`);
+ });
+
+ it('over the limit', () => {
+ createComponent({ props: { groupRunnersLimitedCount: 1001 }, mountFn: mount });
+
+ expect(findRunnerFilteredSearchBar().text()).toMatch(`Runners in this group: 1,000+`);
+ });
+ });
+
+ describe('when a filter is preselected', () => {
+ beforeEach(async () => {
+ setWindowLocation(`?status[]=${STATUS_ACTIVE}&runner_type[]=${INSTANCE_TYPE}`);
+
+ createComponent();
+ 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: '=' } },
+ ],
+ sort: 'CREATED_DESC',
+ pagination: { page: 1 },
+ });
+ });
+
+ it('requests the runners with filter parameters', () => {
+ expect(mockGroupRunnersQuery).toHaveBeenLastCalledWith({
+ groupFullPath: mockGroupFullPath,
+ status: STATUS_ACTIVE,
+ type: INSTANCE_TYPE,
+ 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: STATUS_ACTIVE, operator: '=' } }],
+ sort: CREATED_ASC,
+ });
+ });
+
+ it('updates the browser url', () => {
+ expect(updateHistory).toHaveBeenLastCalledWith({
+ title: expect.any(String),
+ url: 'http://test.host/groups/group1/-/runners?status[]=ACTIVE&sort=CREATED_ASC',
+ });
+ });
+
+ it('requests the runners with filters', () => {
+ expect(mockGroupRunnersQuery).toHaveBeenLastCalledWith({
+ groupFullPath: mockGroupFullPath,
+ status: STATUS_ACTIVE,
+ sort: CREATED_ASC,
+ first: RUNNER_PAGE_SIZE,
+ });
+ });
+ });
+
+ it('when runners have not loaded, shows a loading state', () => {
+ createComponent();
+ expect(findRunnerList().props('loading')).toBe(true);
+ });
+
+ describe('when no runners are found', () => {
+ beforeEach(async () => {
+ mockGroupRunnersQuery = jest.fn().mockResolvedValue({
+ data: {
+ group: {
+ runners: { nodes: [] },
+ },
+ },
+ });
+ createComponent();
+ });
+
+ it('shows a message for no results', async () => {
+ expect(wrapper.text()).toContain('No runners found');
+ });
+ });
+
+ describe('when runners query fails', () => {
+ beforeEach(() => {
+ mockGroupRunnersQuery = jest.fn().mockRejectedValue(new Error('Error!'));
+ createComponent();
+ });
+
+ it('error is shown to the user', async () => {
+ expect(createFlash).toHaveBeenCalledTimes(1);
+ });
+
+ it('error is reported to sentry', async () => {
+ expect(captureException).toHaveBeenCalledWith({
+ error: new Error('Network error: Error!'),
+ component: 'GroupRunnersApp',
+ });
+ });
+ });
+
+ describe('Pagination', () => {
+ beforeEach(() => {
+ mockGroupRunnersQuery = jest.fn().mockResolvedValue(groupRunnersDataPaginated);
+
+ createComponent({ mountFn: mount });
+ });
+
+ it('more pages can be selected', () => {
+ expect(findRunnerPagination().text()).toMatchInterpolatedText('Prev Next');
+ });
+
+ it('cannot navigate to the previous page', () => {
+ expect(findRunnerPaginationPrev().attributes('aria-disabled')).toBe('true');
+ });
+
+ it('navigates to the next page', async () => {
+ await findRunnerPaginationNext().trigger('click');
+
+ expect(mockGroupRunnersQuery).toHaveBeenLastCalledWith({
+ groupFullPath: mockGroupFullPath,
+ sort: CREATED_DESC,
+ first: RUNNER_PAGE_SIZE,
+ after: groupRunnersDataPaginated.data.group.runners.pageInfo.endCursor,
+ });
+ });
+ });
});
diff --git a/spec/frontend/runner/mock_data.js b/spec/frontend/runner/mock_data.js
index 8f551feca6e..c90b9a4c426 100644
--- a/spec/frontend/runner/mock_data.js
+++ b/spec/frontend/runner/mock_data.js
@@ -1,6 +1,14 @@
+const runnerFixture = (filename) => getJSONFixture(`graphql/runner/${filename}`);
+
// Fixtures generated by: spec/frontend/fixtures/runner.rb
-export const runnersData = getJSONFixture('graphql/runner/get_runners.query.graphql.json');
-export const runnersDataPaginated = getJSONFixture(
- 'graphql/runner/get_runners.query.graphql.paginated.json',
+
+// Admin queries
+export const runnersData = runnerFixture('get_runners.query.graphql.json');
+export const runnersDataPaginated = runnerFixture('get_runners.query.graphql.paginated.json');
+export const runnerData = runnerFixture('get_runner.query.graphql.json');
+
+// Group queries
+export const groupRunnersData = runnerFixture('get_group_runners.query.graphql.json');
+export const groupRunnersDataPaginated = runnerFixture(
+ 'get_group_runners.query.graphql.paginated.json',
);
-export const runnerData = getJSONFixture('graphql/runner/get_runner.query.graphql.json');