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
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-12-09 15:09:42 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-12-09 15:09:42 +0300
commit1361891b0a87187364d1586395df176a8984e914 (patch)
tree4f47ddf2cd0d06cd0eb98a7bf1b7001504e6416b /spec
parent109562e64e1e1c51fe32a7443df86ee63b856115 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/import/github_controller_spec.rb74
-rw-r--r--spec/factories/experiment_subjects.rb9
-rw-r--r--spec/features/admin/users/user_spec.rb1
-rw-r--r--spec/features/admin/users/users_spec.rb2
-rw-r--r--spec/features/profiles/user_edit_profile_spec.rb41
-rw-r--r--spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb2
-rw-r--r--spec/fixtures/api/schemas/entities/admin_users_data_attributes_paths.json30
-rw-r--r--spec/frontend/admin/users/index_spec.js35
-rw-r--r--spec/frontend/admin/users/mock_data.js29
-rw-r--r--spec/frontend/boards/stores/actions_spec.js84
-rw-r--r--spec/frontend/helpers/vue_test_utils_helper.js15
-rw-r--r--spec/frontend/helpers/vue_test_utils_helper_spec.js46
-rw-r--r--spec/frontend/packages/details/components/package_history_spec.js107
-rw-r--r--spec/helpers/tree_helper_spec.rb18
-rw-r--r--spec/helpers/users_helper_spec.rb17
-rw-r--r--spec/lib/gitlab/danger/commit_linter_spec.rb6
-rw-r--r--spec/lib/gitlab/github_import/client_spec.rb2
-rw-r--r--spec/models/experiment_spec.rb1
-rw-r--r--spec/models/experiment_subject_spec.rb54
-rw-r--r--spec/models/repository_spec.rb2
-rw-r--r--spec/presenters/project_presenter_spec.rb6
21 files changed, 457 insertions, 124 deletions
diff --git a/spec/controllers/import/github_controller_spec.rb b/spec/controllers/import/github_controller_spec.rb
index a408d821833..d82fff1f7ae 100644
--- a/spec/controllers/import/github_controller_spec.rb
+++ b/spec/controllers/import/github_controller_spec.rb
@@ -123,26 +123,33 @@ RSpec.describe Import::GithubController do
end
it 'fetches repos using latest github client' do
- expect_next_instance_of(Gitlab::GithubImport::Client) do |client|
- expect(client).to receive(:each_page).with(:repos).and_return([].to_enum)
+ expect_next_instance_of(Octokit::Client) do |client|
+ expect(client).to receive(:repos).and_return([].to_enum)
end
get :status
end
- it 'concatenates list of repos from multiple pages' do
- repo_1 = OpenStruct.new(login: 'emacs', full_name: 'asd/emacs', name: 'emacs', owner: { login: 'owner' })
- repo_2 = OpenStruct.new(login: 'vim', full_name: 'asd/vim', name: 'vim', owner: { login: 'owner' })
- repos = [OpenStruct.new(objects: [repo_1]), OpenStruct.new(objects: [repo_2])].to_enum
+ context 'pagination' do
+ context 'when no page is specified' do
+ it 'requests first page' do
+ expect_next_instance_of(Octokit::Client) do |client|
+ expect(client).to receive(:repos).with(nil, { page: 1, per_page: 25 }).and_return([].to_enum)
+ end
- allow(stub_client).to receive(:each_page).and_return(repos)
+ get :status
+ end
+ end
- get :status, format: :json
+ context 'when page is specified' do
+ it 'requests repos with specified page' do
+ expect_next_instance_of(Octokit::Client) do |client|
+ expect(client).to receive(:repos).with(nil, { page: 2, per_page: 25 }).and_return([].to_enum)
+ end
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response.dig('provider_repos').count).to eq(2)
- expect(json_response.dig('provider_repos', 0, 'id')).to eq(repo_1.id)
- expect(json_response.dig('provider_repos', 1, 'id')).to eq(repo_2.id)
+ get :status, params: { page: 2 }
+ end
+ end
end
context 'when filtering' do
@@ -150,6 +157,7 @@ RSpec.describe Import::GithubController do
let(:user_login) { 'user' }
let(:collaborations_subquery) { 'repo:repo1 repo:repo2' }
let(:organizations_subquery) { 'org:org1 org:org2' }
+ let(:search_query) { "test in:name is:public,private user:#{user_login} #{collaborations_subquery} #{organizations_subquery}" }
before do
allow_next_instance_of(Octokit::Client) do |client|
@@ -158,20 +166,56 @@ RSpec.describe Import::GithubController do
end
it 'makes request to github search api' do
- expected_query = "test in:name is:public,private user:#{user_login} #{collaborations_subquery} #{organizations_subquery}"
+ expect_next_instance_of(Octokit::Client) do |client|
+ expect(client).to receive(:user).and_return(double(login: user_login))
+ expect(client).to receive(:search_repositories).with(search_query, { page: 1, per_page: 25 }).and_return({ items: [].to_enum })
+ end
expect_next_instance_of(Gitlab::GithubImport::Client) do |client|
expect(client).to receive(:collaborations_subquery).and_return(collaborations_subquery)
expect(client).to receive(:organizations_subquery).and_return(organizations_subquery)
- expect(client).to receive(:each_page).with(:search_repositories, expected_query).and_return([].to_enum)
end
get :status, params: { filter: filter }, format: :json
end
+ context 'pagination' do
+ context 'when no page is specified' do
+ it 'requests first page' do
+ expect_next_instance_of(Octokit::Client) do |client|
+ expect(client).to receive(:user).and_return(double(login: user_login))
+ expect(client).to receive(:search_repositories).with(search_query, { page: 1, per_page: 25 }).and_return({ items: [].to_enum })
+ end
+
+ expect_next_instance_of(Gitlab::GithubImport::Client) do |client|
+ expect(client).to receive(:collaborations_subquery).and_return(collaborations_subquery)
+ expect(client).to receive(:organizations_subquery).and_return(organizations_subquery)
+ end
+
+ get :status, params: { filter: filter }, format: :json
+ end
+ end
+
+ context 'when page is specified' do
+ it 'requests repos with specified page' do
+ expect_next_instance_of(Octokit::Client) do |client|
+ expect(client).to receive(:user).and_return(double(login: user_login))
+ expect(client).to receive(:search_repositories).with(search_query, { page: 2, per_page: 25 }).and_return({ items: [].to_enum })
+ end
+
+ expect_next_instance_of(Gitlab::GithubImport::Client) do |client|
+ expect(client).to receive(:collaborations_subquery).and_return(collaborations_subquery)
+ expect(client).to receive(:organizations_subquery).and_return(organizations_subquery)
+ end
+
+ get :status, params: { filter: filter, page: 2 }, format: :json
+ end
+ end
+ end
+
context 'when user input contains colons and spaces' do
before do
- stub_client(search_repos_by_name: [])
+ allow(controller).to receive(:client_repos).and_return([])
end
it 'sanitizes user input' do
diff --git a/spec/factories/experiment_subjects.rb b/spec/factories/experiment_subjects.rb
new file mode 100644
index 00000000000..c35bc370bad
--- /dev/null
+++ b/spec/factories/experiment_subjects.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :experiment_subject do
+ experiment
+ user
+ variant { :control }
+ end
+end
diff --git a/spec/features/admin/users/user_spec.rb b/spec/features/admin/users/user_spec.rb
index 46380218e91..e7dd50ed514 100644
--- a/spec/features/admin/users/user_spec.rb
+++ b/spec/features/admin/users/user_spec.rb
@@ -9,6 +9,7 @@ RSpec.describe 'Admin::Users::User' do
before do
sign_in(current_user)
gitlab_enable_admin_mode_sign_in(current_user)
+ stub_feature_flags(vue_admin_users: false)
end
describe 'GET /admin/users/:id' do
diff --git a/spec/features/admin/users/users_spec.rb b/spec/features/admin/users/users_spec.rb
index afabdcf4fb7..9482b4f8603 100644
--- a/spec/features/admin/users/users_spec.rb
+++ b/spec/features/admin/users/users_spec.rb
@@ -15,6 +15,7 @@ RSpec.describe 'Admin::Users' do
describe 'GET /admin/users' do
before do
+ stub_feature_flags(vue_admin_users: false)
visit admin_users_path
end
@@ -418,6 +419,7 @@ RSpec.describe 'Admin::Users' do
describe 'GET /admin/users/:id/edit' do
before do
+ stub_feature_flags(vue_admin_users: false)
visit admin_users_path
click_link "edit_user_#{user.id}"
end
diff --git a/spec/features/profiles/user_edit_profile_spec.rb b/spec/features/profiles/user_edit_profile_spec.rb
index f341709b73d..239bc04a9cb 100644
--- a/spec/features/profiles/user_edit_profile_spec.rb
+++ b/spec/features/profiles/user_edit_profile_spec.rb
@@ -228,7 +228,7 @@ RSpec.describe 'User edit profile' do
end
def open_edit_status_modal
- open_modal 'Edit status'
+ open_modal 'Edit status'
end
def set_user_status_in_modal
@@ -291,6 +291,10 @@ RSpec.describe 'User edit profile' do
toggle_busy_status
set_user_status_in_modal
+
+ wait_for_requests
+ visit root_path(user)
+
open_edit_status_modal
expect(busy_status.checked?).to eq(true)
@@ -368,26 +372,37 @@ RSpec.describe 'User edit profile' do
expect(page).not_to have_selector '.cover-status'
end
- it 'clears the user status with the "Remove status" button' do
- user_status = create(:user_status, user: user, message: 'Eating bread', emoji: 'stuffed_flatbread')
+ context 'Remove status button' do
+ before do
+ user.status = UserStatus.new(message: 'Eating bread', emoji: 'stuffed_flatbread')
- visit_user
- wait_for_requests
+ visit_user
+ wait_for_requests
- within('.cover-status') do
- expect(page).to have_emoji(user_status.emoji)
- expect(page).to have_content user_status.message
+ open_edit_status_modal
+
+ page.within "#set-user-status-modal" do
+ click_button 'Remove status'
+ end
+
+ wait_for_requests
end
- open_edit_status_modal
+ it 'clears the user status with the "Remove status" button' do
+ visit_user
- page.within "#set-user-status-modal" do
- click_button 'Remove status'
+ expect(page).not_to have_selector '.cover-status'
end
- visit_user
+ it 'shows the "Set status" menu item in the user menu' do
+ visit root_path(user)
- expect(page).not_to have_selector '.cover-status'
+ find('.header-user-dropdown-toggle').click
+
+ page.within ".header-user" do
+ expect(page).to have_content('Set status')
+ end
+ end
end
it 'displays a default emoji if only message is entered' do
diff --git a/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb b/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb
index 189aa45ff75..ca8e4fdac57 100644
--- a/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb
+++ b/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb
@@ -165,7 +165,7 @@ RSpec.describe 'Projects > Show > User sees setup shortcut buttons' do
context 'when the project does not have a README' do
it 'shows the single file editor "Add README" button' do
- allow(project.repository).to receive(:readme).and_return(nil)
+ allow(project.repository).to receive(:readme_path).and_return(nil)
visit project_path(project)
diff --git a/spec/fixtures/api/schemas/entities/admin_users_data_attributes_paths.json b/spec/fixtures/api/schemas/entities/admin_users_data_attributes_paths.json
new file mode 100644
index 00000000000..eab8b626876
--- /dev/null
+++ b/spec/fixtures/api/schemas/entities/admin_users_data_attributes_paths.json
@@ -0,0 +1,30 @@
+{
+ "type": "object",
+ "properties": {
+ "edit": { "type": "string" },
+ "approve": { "type": "string" },
+ "reject": { "type": "string" },
+ "unblock": { "type": "string" },
+ "block": { "type": "string" },
+ "deactivate": { "type": "string" },
+ "activate": { "type": "string" },
+ "unlock": { "type": "string" },
+ "delete": { "type": "string" },
+ "delete_with_contributions": { "type": "string" },
+ "admin_user": { "type": "string" }
+ },
+ "required": [
+ "edit",
+ "approve",
+ "reject",
+ "unblock",
+ "block",
+ "deactivate",
+ "activate",
+ "unlock",
+ "delete",
+ "delete_with_contributions",
+ "admin_user"
+ ],
+ "additionalProperties": false
+}
diff --git a/spec/frontend/admin/users/index_spec.js b/spec/frontend/admin/users/index_spec.js
new file mode 100644
index 00000000000..171d54c8f4f
--- /dev/null
+++ b/spec/frontend/admin/users/index_spec.js
@@ -0,0 +1,35 @@
+import { createWrapper } from '@vue/test-utils';
+import initAdminUsers from '~/admin/users';
+import AdminUsersApp from '~/admin/users/components/app.vue';
+import { users, paths } from './mock_data';
+
+describe('initAdminUsersApp', () => {
+ let wrapper;
+ let el;
+
+ const findApp = () => wrapper.find(AdminUsersApp);
+
+ beforeEach(() => {
+ el = document.createElement('div');
+ el.setAttribute('data-users', JSON.stringify(users));
+ el.setAttribute('data-paths', JSON.stringify(paths));
+
+ document.body.appendChild(el);
+
+ wrapper = createWrapper(initAdminUsers(el));
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ el.remove();
+ el = null;
+ });
+
+ it('parses and passes props', () => {
+ expect(findApp().props()).toMatchObject({
+ users,
+ paths,
+ });
+ });
+});
diff --git a/spec/frontend/admin/users/mock_data.js b/spec/frontend/admin/users/mock_data.js
new file mode 100644
index 00000000000..b80d04454b0
--- /dev/null
+++ b/spec/frontend/admin/users/mock_data.js
@@ -0,0 +1,29 @@
+export const users = [
+ {
+ id: 2177,
+ name: 'Nikki',
+ createdAt: '2020-11-13T12:26:54.177Z',
+ email: 'nikki@example.com',
+ username: 'nikki',
+ lastActivityOn: null,
+ avatarUrl:
+ 'https://secure.gravatar.com/avatar/054f062d8b1a42b123f17e13a173cda8?s=80\\u0026d=identicon',
+ badges: [],
+ projectsCount: 0,
+ actions: [],
+ },
+];
+
+export const paths = {
+ edit: '/admin/users/id/edit',
+ approve: '/admin/users/id/approve',
+ reject: '/admin/users/id/reject',
+ unblock: '/admin/users/id/unblock',
+ block: '/admin/users/id/block',
+ deactivate: '/admin/users/id/deactivate',
+ activate: '/admin/users/id/activate',
+ unlock: '/admin/users/id/unlock',
+ delete: '/admin/users/id',
+ deleteWithContributions: '/admin/users/id',
+ adminUser: '/admin/users/id',
+};
diff --git a/spec/frontend/boards/stores/actions_spec.js b/spec/frontend/boards/stores/actions_spec.js
index 360665c77be..c55b01da5cc 100644
--- a/spec/frontend/boards/stores/actions_spec.js
+++ b/spec/frontend/boards/stores/actions_spec.js
@@ -18,6 +18,9 @@ import issueMoveListMutation from '~/boards/graphql/issue_move_list.mutation.gra
import destroyBoardListMutation from '~/boards/graphql/board_list_destroy.mutation.graphql';
import updateAssignees from '~/vue_shared/components/sidebar/queries/updateAssignees.mutation.graphql';
import { fullBoardId, formatListIssues, formatBoardLists } from '~/boards/boards_util';
+import createFlash from '~/flash';
+
+jest.mock('~/flash');
const expectNotImplemented = action => {
it('is not implemented', () => {
@@ -666,46 +669,59 @@ describe('setAssignees', () => {
const refPath = `${projectPath}#3`;
const iid = '1';
- beforeEach(() => {
- jest.spyOn(gqlClient, 'mutate').mockResolvedValue({
- data: { issueSetAssignees: { issue: { assignees: { nodes: [{ ...node }] } } } },
+ describe('when succeeds', () => {
+ beforeEach(() => {
+ jest.spyOn(gqlClient, 'mutate').mockResolvedValue({
+ data: { issueSetAssignees: { issue: { assignees: { nodes: [{ ...node }] } } } },
+ });
});
- });
- it('calls mutate with the correct values', async () => {
- await actions.setAssignees(
- { commit: () => {}, getters: { activeIssue: { iid, referencePath: refPath } } },
- [name],
- );
+ it('calls mutate with the correct values', async () => {
+ await actions.setAssignees(
+ { commit: () => {}, getters: { activeIssue: { iid, referencePath: refPath } } },
+ [name],
+ );
- expect(gqlClient.mutate).toHaveBeenCalledWith({
- mutation: updateAssignees,
- variables: { iid, assigneeUsernames: [name], projectPath },
+ expect(gqlClient.mutate).toHaveBeenCalledWith({
+ mutation: updateAssignees,
+ variables: { iid, assigneeUsernames: [name], projectPath },
+ });
+ });
+
+ it('calls the correct mutation with the correct values', done => {
+ testAction(
+ actions.setAssignees,
+ {},
+ { activeIssue: { iid, referencePath: refPath }, commit: () => {} },
+ [
+ { type: types.SET_ASSIGNEE_LOADING, payload: true },
+ {
+ type: 'UPDATE_ISSUE_BY_ID',
+ payload: { prop: 'assignees', issueId: undefined, value: [node] },
+ },
+ { type: types.SET_ASSIGNEE_LOADING, payload: false },
+ ],
+ [],
+ done,
+ );
});
});
- it('calls the correct mutation with the correct values', done => {
- testAction(
- actions.setAssignees,
- {},
- { activeIssue: { iid, referencePath: refPath }, commit: () => {} },
- [
- {
- type: 'SET_ASSIGNEE_LOADING',
- payload: true,
- },
- {
- type: 'UPDATE_ISSUE_BY_ID',
- payload: { prop: 'assignees', issueId: undefined, value: [node] },
- },
- {
- type: 'SET_ASSIGNEE_LOADING',
- payload: false,
- },
- ],
- [],
- done,
- );
+ describe('when fails', () => {
+ beforeEach(() => {
+ jest.spyOn(gqlClient, 'mutate').mockRejectedValue();
+ });
+
+ it('calls createFlash', async () => {
+ await actions.setAssignees({
+ commit: () => {},
+ getters: { activeIssue: { iid, referencePath: refPath } },
+ });
+
+ expect(createFlash).toHaveBeenCalledWith({
+ message: 'An error occurred while updating assignees.',
+ });
+ });
});
});
diff --git a/spec/frontend/helpers/vue_test_utils_helper.js b/spec/frontend/helpers/vue_test_utils_helper.js
index ead898f04d3..0e9127b5c65 100644
--- a/spec/frontend/helpers/vue_test_utils_helper.js
+++ b/spec/frontend/helpers/vue_test_utils_helper.js
@@ -1,3 +1,5 @@
+import { isArray } from 'lodash';
+
const vNodeContainsText = (vnode, text) =>
(vnode.text && vnode.text.includes(text)) ||
(vnode.children && vnode.children.filter(child => vNodeContainsText(child, text)).length);
@@ -34,9 +36,18 @@ export const waitForMutation = (store, expectedMutationType) =>
});
});
-export const extendedWrapper = wrapper =>
- Object.defineProperty(wrapper, 'findByTestId', {
+export const extendedWrapper = wrapper => {
+ if (isArray(wrapper) || !wrapper?.find) {
+ // eslint-disable-next-line no-console
+ console.warn(
+ '[vue-test-utils-helper]: you are trying to extend an object that is not a VueWrapper.',
+ );
+ return wrapper;
+ }
+
+ return Object.defineProperty(wrapper, 'findByTestId', {
value(id) {
return this.find(`[data-testid="${id}"]`);
},
});
+};
diff --git a/spec/frontend/helpers/vue_test_utils_helper_spec.js b/spec/frontend/helpers/vue_test_utils_helper_spec.js
index 41714066da5..31c4ccd5dbb 100644
--- a/spec/frontend/helpers/vue_test_utils_helper_spec.js
+++ b/spec/frontend/helpers/vue_test_utils_helper_spec.js
@@ -1,5 +1,5 @@
import { shallowMount } from '@vue/test-utils';
-import { shallowWrapperContainsSlotText } from './vue_test_utils_helper';
+import { extendedWrapper, shallowWrapperContainsSlotText } from './vue_test_utils_helper';
describe('Vue test utils helpers', () => {
describe('shallowWrapperContainsSlotText', () => {
@@ -45,4 +45,48 @@ describe('Vue test utils helpers', () => {
expect(shallowWrapperContainsSlotText(mockComponent, 'namedSlot', searchText)).toBe(false);
});
});
+
+ describe('extendedWrapper', () => {
+ describe('when an invalid wrapper is provided', () => {
+ beforeEach(() => {
+ // eslint-disable-next-line no-console
+ console.warn = jest.fn();
+ });
+
+ it.each`
+ wrapper
+ ${{}}
+ ${[]}
+ ${null}
+ ${undefined}
+ ${1}
+ ${''}
+ `('should warn with an error when the wrapper is $wrapper', ({ wrapper }) => {
+ extendedWrapper(wrapper);
+ /* eslint-disable no-console */
+ expect(console.warn).toHaveBeenCalled();
+ expect(console.warn).toHaveBeenCalledWith(
+ '[vue-test-utils-helper]: you are trying to extend an object that is not a VueWrapper.',
+ );
+ /* eslint-enable no-console */
+ });
+ });
+
+ describe('findByTestId', () => {
+ const testId = 'a-component';
+ let mockComponent;
+
+ beforeEach(() => {
+ mockComponent = extendedWrapper(
+ shallowMount({
+ template: `<div data-testid="${testId}"></div>`,
+ }),
+ );
+ });
+
+ it('should find the component by test id', () => {
+ expect(mockComponent.findByTestId(testId).exists()).toBe(true);
+ });
+ });
+ });
});
diff --git a/spec/frontend/packages/details/components/package_history_spec.js b/spec/frontend/packages/details/components/package_history_spec.js
index f745a457b0a..311aa9e7e68 100644
--- a/spec/frontend/packages/details/components/package_history_spec.js
+++ b/spec/frontend/packages/details/components/package_history_spec.js
@@ -2,6 +2,7 @@ import { shallowMount } from '@vue/test-utils';
import { GlLink, GlSprintf } from '@gitlab/ui';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import HistoryItem from '~/vue_shared/components/registry/history_item.vue';
+import { HISTORY_PIPELINES_LIMIT } from '~/packages/details/constants';
import component from '~/packages/details/components/package_history.vue';
import { mavenPackage, mockPipelineInfo } from '../../mock_data';
@@ -13,6 +14,9 @@ describe('Package History', () => {
packageEntity: { ...mavenPackage },
};
+ const createPipelines = amount =>
+ [...Array(amount)].map((x, index) => ({ ...mockPipelineInfo, id: index + 1 }));
+
const mountComponent = props => {
wrapper = shallowMount(component, {
propsData: { ...defaultProps, ...props },
@@ -56,55 +60,58 @@ describe('Package History', () => {
expect.arrayContaining(['timeline', 'main-notes-list', 'notes']),
);
});
-
describe.each`
- name | icon | text | timeAgoTooltip | link
- ${'created-on'} | ${'clock'} | ${'Test package version 1.0.0 was created'} | ${mavenPackage.created_at} | ${null}
- ${'updated-at'} | ${'pencil'} | ${'Test package version 1.0.0 was updated'} | ${mavenPackage.updated_at} | ${null}
- ${'commit'} | ${'commit'} | ${'Commit sha-baz on branch branch-name'} | ${null} | ${mockPipelineInfo.project.commit_url}
- ${'pipeline'} | ${'pipeline'} | ${'Pipeline #1 triggered by foo'} | ${mockPipelineInfo.created_at} | ${mockPipelineInfo.project.pipeline_url}
- ${'published'} | ${'package'} | ${'Published to the baz project Package Registry'} | ${mavenPackage.created_at} | ${null}
- `('history element $name', ({ name, icon, text, timeAgoTooltip, link }) => {
- let element;
-
- beforeEach(() => {
- mountComponent({ packageEntity: { ...mavenPackage, pipeline: mockPipelineInfo } });
- element = findHistoryElement(name);
- });
-
- it('has the correct icon', () => {
- expect(element.props('icon')).toBe(icon);
- });
-
- it('has the correct text', () => {
- expect(element.text()).toBe(text);
- });
-
- it('time-ago tooltip', () => {
- const timeAgo = findElementTimeAgo(element);
- const exist = Boolean(timeAgoTooltip);
-
- expect(timeAgo.exists()).toBe(exist);
- if (exist) {
- expect(timeAgo.props('time')).toBe(timeAgoTooltip);
- }
- });
-
- it('link', () => {
- const linkElement = findElementLink(element);
- const exist = Boolean(link);
-
- expect(linkElement.exists()).toBe(exist);
- if (exist) {
- expect(linkElement.attributes('href')).toBe(link);
- }
- });
- });
-
- describe('when pipelineInfo is missing', () => {
- it.each(['commit', 'pipeline'])('%s history element is hidden', name => {
- mountComponent();
- expect(findHistoryElement(name).exists()).toBe(false);
- });
- });
+ name | amount | icon | text | timeAgoTooltip | link
+ ${'created-on'} | ${HISTORY_PIPELINES_LIMIT + 2} | ${'clock'} | ${'Test package version 1.0.0 was first created'} | ${mavenPackage.created_at} | ${null}
+ ${'first-pipeline-commit'} | ${HISTORY_PIPELINES_LIMIT + 2} | ${'commit'} | ${'Created by commit #sha-baz on branch branch-name'} | ${null} | ${mockPipelineInfo.project.commit_url}
+ ${'first-pipeline-pipeline'} | ${HISTORY_PIPELINES_LIMIT + 2} | ${'pipeline'} | ${'Built by pipeline #1 triggered by foo'} | ${mockPipelineInfo.created_at} | ${mockPipelineInfo.project.pipeline_url}
+ ${'published'} | ${HISTORY_PIPELINES_LIMIT + 2} | ${'package'} | ${'Published to the baz project Package Registry'} | ${mavenPackage.created_at} | ${null}
+ ${'archived'} | ${HISTORY_PIPELINES_LIMIT + 2} | ${'history'} | ${'Package has 1 archived update'} | ${null} | ${null}
+ ${'archived'} | ${HISTORY_PIPELINES_LIMIT + 3} | ${'history'} | ${'Package has 2 archived updates'} | ${null} | ${null}
+ ${'pipeline-entry'} | ${HISTORY_PIPELINES_LIMIT + 2} | ${'pencil'} | ${'Package updated by commit #sha-baz on branch branch-name, built by pipeline #3, and published to the registry'} | ${mavenPackage.created_at} | ${mockPipelineInfo.project.commit_url}
+ `(
+ 'with $amount pipelines history element $name',
+ ({ name, icon, text, timeAgoTooltip, link, amount }) => {
+ let element;
+
+ beforeEach(() => {
+ mountComponent({
+ packageEntity: { ...mavenPackage, pipelines: createPipelines(amount) },
+ });
+ element = findHistoryElement(name);
+ });
+
+ it('exists', () => {
+ expect(element.exists()).toBe(true);
+ });
+
+ it('has the correct icon', () => {
+ expect(element.props('icon')).toBe(icon);
+ });
+
+ it('has the correct text', () => {
+ expect(element.text()).toBe(text);
+ });
+
+ it('time-ago tooltip', () => {
+ const timeAgo = findElementTimeAgo(element);
+ const exist = Boolean(timeAgoTooltip);
+
+ expect(timeAgo.exists()).toBe(exist);
+ if (exist) {
+ expect(timeAgo.props('time')).toBe(timeAgoTooltip);
+ }
+ });
+
+ it('link', () => {
+ const linkElement = findElementLink(element);
+ const exist = Boolean(link);
+
+ expect(linkElement.exists()).toBe(exist);
+ if (exist) {
+ expect(linkElement.attributes('href')).toBe(link);
+ }
+ });
+ },
+ );
});
diff --git a/spec/helpers/tree_helper_spec.rb b/spec/helpers/tree_helper_spec.rb
index 620bf248d7b..136ec07e73d 100644
--- a/spec/helpers/tree_helper_spec.rb
+++ b/spec/helpers/tree_helper_spec.rb
@@ -216,6 +216,24 @@ RSpec.describe TreeHelper do
web_ide_url: "/-/ide/project/#{project.full_path}/edit/#{sha}/-/#{@path}"
)
end
+
+ it 'does not load blob from repository again' do
+ blob
+
+ expect(repository).not_to receive(:blob_at)
+
+ subject
+ end
+ end
+
+ context 'nil blob is passed' do
+ let(:blob) { nil }
+
+ it 'does not load blob from repository' do
+ expect(repository).not_to receive(:blob_at)
+
+ subject
+ end
end
context 'user does not have write access but a personal fork exists' do
diff --git a/spec/helpers/users_helper_spec.rb b/spec/helpers/users_helper_spec.rb
index 5b559e40a80..c92c6e6e78e 100644
--- a/spec/helpers/users_helper_spec.rb
+++ b/spec/helpers/users_helper_spec.rb
@@ -333,4 +333,21 @@ RSpec.describe UsersHelper do
allow(helper).to receive(:can?).with(current_user, :read_user_profile, user).and_return(allowed)
end
end
+
+ describe '#admin_users_data_attributes' do
+ subject(:data) { helper.admin_users_data_attributes([user]) }
+
+ it 'users matches the serialized json' do
+ entity = double
+ expect_next_instance_of(Admin::UserSerializer) do |instance|
+ expect(instance).to receive(:represent).with([user]).and_return(entity)
+ end
+ expect(entity).to receive(:to_json).and_return("{\"username\":\"admin\"}")
+ expect(data[:users]).to eq "{\"username\":\"admin\"}"
+ end
+
+ it 'paths matches the schema' do
+ expect(data[:paths]).to match_schema('entities/admin_users_data_attributes_paths')
+ end
+ end
end
diff --git a/spec/lib/gitlab/danger/commit_linter_spec.rb b/spec/lib/gitlab/danger/commit_linter_spec.rb
index 35d9f5021cc..368193decda 100644
--- a/spec/lib/gitlab/danger/commit_linter_spec.rb
+++ b/spec/lib/gitlab/danger/commit_linter_spec.rb
@@ -192,7 +192,9 @@ RSpec.describe Gitlab::Danger::CommitLinter do
'[Ci skip] A commit message',
'[API] A commit message',
'api: A commit message',
- 'API: A commit message'
+ 'API: A commit message',
+ 'API: a commit message',
+ 'API: a commit message'
].each do |message|
context "when subject is '#{message}'" do
let(:commit_message) { message }
@@ -209,8 +211,6 @@ RSpec.describe Gitlab::Danger::CommitLinter do
'[ci skip]A commit message',
'[Ci skip] A commit message',
'[ci skip] a commit message',
- 'API: a commit message',
- 'API: a commit message',
'api: a commit message',
'! A commit message'
].each do |message|
diff --git a/spec/lib/gitlab/github_import/client_spec.rb b/spec/lib/gitlab/github_import/client_spec.rb
index e6dd156f3d8..4000e0b2611 100644
--- a/spec/lib/gitlab/github_import/client_spec.rb
+++ b/spec/lib/gitlab/github_import/client_spec.rb
@@ -500,7 +500,7 @@ RSpec.describe Gitlab::GithubImport::Client do
it 'searches for repositories based on name' do
expected_search_query = 'test in:name is:public,private user:user repo:repo1 repo:repo2 org:org1 org:org2'
- expect(client).to receive(:each_page).with(:search_repositories, expected_search_query)
+ expect(client.octokit).to receive(:search_repositories).with(expected_search_query, {})
client.search_repos_by_name('test')
end
diff --git a/spec/models/experiment_spec.rb b/spec/models/experiment_spec.rb
index 4106630ea20..1bf7b8b4850 100644
--- a/spec/models/experiment_spec.rb
+++ b/spec/models/experiment_spec.rb
@@ -7,6 +7,7 @@ RSpec.describe Experiment do
describe 'associations' do
it { is_expected.to have_many(:experiment_users) }
+ it { is_expected.to have_many(:experiment_subjects) }
end
describe 'validations' do
diff --git a/spec/models/experiment_subject_spec.rb b/spec/models/experiment_subject_spec.rb
new file mode 100644
index 00000000000..4850814c5f5
--- /dev/null
+++ b/spec/models/experiment_subject_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ExperimentSubject, type: :model do
+ describe 'associations' do
+ it { is_expected.to belong_to(:experiment) }
+ it { is_expected.to belong_to(:user) }
+ it { is_expected.to belong_to(:group) }
+ it { is_expected.to belong_to(:project) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:experiment) }
+
+ describe 'must_have_one_subject_present' do
+ let(:experiment_subject) { build(:experiment_subject, user: nil, group: nil, project: nil) }
+ let(:error_message) { 'Must have exactly one of User, Group, or Project.' }
+
+ it 'fails when no subject is present' do
+ expect(experiment_subject).not_to be_valid
+ expect(experiment_subject.errors[:base]).to include(error_message)
+ end
+
+ it 'passes when user subject is present' do
+ experiment_subject.user = build(:user)
+ expect(experiment_subject).to be_valid
+ end
+
+ it 'passes when group subject is present' do
+ experiment_subject.group = build(:group)
+ expect(experiment_subject).to be_valid
+ end
+
+ it 'passes when project subject is present' do
+ experiment_subject.project = build(:project)
+ expect(experiment_subject).to be_valid
+ end
+
+ it 'fails when more than one subject is present', :aggregate_failures do
+ # two subjects
+ experiment_subject.user = build(:user)
+ experiment_subject.group = build(:group)
+ expect(experiment_subject).not_to be_valid
+ expect(experiment_subject.errors[:base]).to include(error_message)
+
+ # three subjects
+ experiment_subject.project = build(:project)
+ expect(experiment_subject).not_to be_valid
+ expect(experiment_subject.errors[:base]).to include(error_message)
+ end
+ end
+ end
+end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 31211f8ff2c..c1f073e26d1 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -2335,7 +2335,7 @@ RSpec.describe Repository do
end
it 'caches the response' do
- expect(repository).to receive(:readme).and_call_original.once
+ expect(repository.head_tree).to receive(:readme_path).and_call_original.once
2.times do
expect(repository.readme_path).to eq("README.md")
diff --git a/spec/presenters/project_presenter_spec.rb b/spec/presenters/project_presenter_spec.rb
index b7fee5253f8..a9050c233af 100644
--- a/spec/presenters/project_presenter_spec.rb
+++ b/spec/presenters/project_presenter_spec.rb
@@ -375,7 +375,7 @@ RSpec.describe ProjectPresenter do
it 'returns anchor data' do
project.add_developer(user)
- allow(project.repository).to receive(:readme).and_return(nil)
+ allow(project.repository).to receive(:readme_path).and_return(nil)
expect(presenter.readme_anchor_data).to have_attributes(
is_link: false,
@@ -387,7 +387,7 @@ RSpec.describe ProjectPresenter do
context 'when README exists' do
it 'returns anchor data' do
- allow(project.repository).to receive(:readme).and_return(double(name: 'readme'))
+ allow(project.repository).to receive(:readme_path).and_return('readme')
expect(presenter.readme_anchor_data).to have_attributes(
is_link: false,
@@ -561,7 +561,7 @@ RSpec.describe ProjectPresenter do
let(:project) { build_stubbed(:project) }
it 'orders the items correctly' do
- allow(project.repository).to receive(:readme).and_return(double(name: 'readme'))
+ allow(project.repository).to receive(:readme_path).and_return('readme')
allow(project.repository).to receive(:license_blob).and_return(nil)
allow(project.repository).to receive(:changelog).and_return(nil)
allow(project.repository).to receive(:contribution_guide).and_return(double(name: 'foo'))