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-09-12 03:08:44 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-09-12 03:08:44 +0300
commit0f5dcf55e543e2cb30697d4c8ea8ce509cf25375 (patch)
tree213f71f90ed2f6d356a90b0d09e8dbf5d14e816e /spec
parenta66475b6beb46d77b9ff3fe30453be2d52779048 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/features/admin/admin_groups_spec.rb2
-rw-r--r--spec/features/groups/members/filter_members_spec.rb2
-rw-r--r--spec/features/groups/members/leave_group_spec.rb2
-rw-r--r--spec/features/groups/members/list_members_spec.rb2
-rw-r--r--spec/features/groups/members/manage_groups_spec.rb2
-rw-r--r--spec/features/groups/members/manage_members_spec.rb2
-rw-r--r--spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb2
-rw-r--r--spec/features/groups/members/master_manages_access_requests_spec.rb4
-rw-r--r--spec/features/groups/members/search_members_spec.rb2
-rw-r--r--spec/features/groups/members/sort_members_spec.rb2
-rw-r--r--spec/features/markdown/keyboard_shortcuts_spec.rb101
-rw-r--r--spec/fixtures/api/schemas/entities/group_group_link.json29
-rw-r--r--spec/fixtures/api/schemas/group_group_links.json6
-rw-r--r--spec/fixtures/api/schemas/group_member.json78
-rw-r--r--spec/fixtures/api/schemas/group_members.json6
-rw-r--r--spec/frontend/design_management/pages/design/index_spec.js4
-rw-r--r--spec/frontend/design_management/router_spec.js5
-rw-r--r--spec/frontend/groups/members/index_spec.js58
-rw-r--r--spec/frontend/groups/members/mock_data.js33
-rw-r--r--spec/frontend/shortcuts_spec.js85
-rw-r--r--spec/frontend/vue_shared/components/markdown/header_spec.js58
-rw-r--r--spec/frontend/vue_shared/components/markdown/toolbar_button_spec.js47
-rw-r--r--spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb7
-rw-r--r--spec/graphql/types/merge_request_type_spec.rb1
-rw-r--r--spec/graphql/types/permission_types/merge_request_spec.rb3
-rw-r--r--spec/haml_lint/linter/documentation_links_spec.rb7
-rw-r--r--spec/helpers/groups/group_members_helper_spec.rb48
-rw-r--r--spec/lib/gitlab/utils/markdown_spec.rb16
-rw-r--r--spec/requests/api/graphql/project/merge_request_spec.rb3
-rw-r--r--spec/serializers/group_group_link_entity_spec.rb13
-rw-r--r--spec/serializers/group_group_link_serializer_spec.rb13
-rw-r--r--spec/spec_helper.rb4
-rw-r--r--spec/support/shared_contexts/serializers/group_group_link_shared_context.rb17
33 files changed, 621 insertions, 43 deletions
diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb
index 9cd335ffb8c..f5c5a73c042 100644
--- a/spec/features/admin/admin_groups_spec.rb
+++ b/spec/features/admin/admin_groups_spec.rb
@@ -11,6 +11,8 @@ RSpec.describe 'Admin Groups' do
let!(:current_user) { create(:admin) }
before do
+ stub_feature_flags(vue_group_members_list: false)
+
sign_in(current_user)
stub_application_setting(default_group_visibility: internal)
end
diff --git a/spec/features/groups/members/filter_members_spec.rb b/spec/features/groups/members/filter_members_spec.rb
index 643c8407578..d667690af29 100644
--- a/spec/features/groups/members/filter_members_spec.rb
+++ b/spec/features/groups/members/filter_members_spec.rb
@@ -10,6 +10,8 @@ RSpec.describe 'Groups > Members > Filter members' do
let(:nested_group) { create(:group, parent: group) }
before do
+ stub_feature_flags(vue_group_members_list: false)
+
group.add_owner(user)
group.add_maintainer(user_with_2fa)
nested_group.add_maintainer(nested_group_user)
diff --git a/spec/features/groups/members/leave_group_spec.rb b/spec/features/groups/members/leave_group_spec.rb
index fecc90f20c7..9eb5cc15c5e 100644
--- a/spec/features/groups/members/leave_group_spec.rb
+++ b/spec/features/groups/members/leave_group_spec.rb
@@ -8,6 +8,8 @@ RSpec.describe 'Groups > Members > Leave group' do
let(:group) { create(:group) }
before do
+ stub_feature_flags(vue_group_members_list: false)
+
gitlab_sign_in(user)
end
diff --git a/spec/features/groups/members/list_members_spec.rb b/spec/features/groups/members/list_members_spec.rb
index 415c6927320..bcec2b50a24 100644
--- a/spec/features/groups/members/list_members_spec.rb
+++ b/spec/features/groups/members/list_members_spec.rb
@@ -12,6 +12,8 @@ RSpec.describe 'Groups > Members > List members' do
let(:nested_group) { create(:group, parent: group) }
before do
+ stub_feature_flags(vue_group_members_list: false)
+
sign_in(user1)
end
diff --git a/spec/features/groups/members/manage_groups_spec.rb b/spec/features/groups/members/manage_groups_spec.rb
index faf455e4ed9..e3bbbd4d73b 100644
--- a/spec/features/groups/members/manage_groups_spec.rb
+++ b/spec/features/groups/members/manage_groups_spec.rb
@@ -11,6 +11,8 @@ RSpec.describe 'Groups > Members > Manage groups', :js do
let(:shared_group) { create(:group) }
before do
+ stub_feature_flags(vue_group_members_list: false)
+
shared_group.add_owner(user)
sign_in(user)
end
diff --git a/spec/features/groups/members/manage_members_spec.rb b/spec/features/groups/members/manage_members_spec.rb
index 0267bea2f53..aedb7c170f8 100644
--- a/spec/features/groups/members/manage_members_spec.rb
+++ b/spec/features/groups/members/manage_members_spec.rb
@@ -11,6 +11,8 @@ RSpec.describe 'Groups > Members > Manage members' do
let(:group) { create(:group) }
before do
+ stub_feature_flags(vue_group_members_list: false)
+
sign_in(user1)
end
diff --git a/spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb b/spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb
index f80925186ed..d94cc85f411 100644
--- a/spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb
+++ b/spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb
@@ -11,6 +11,8 @@ RSpec.describe 'Groups > Members > Owner adds member with expiration date', :js
let(:group) { create(:group) }
before do
+ stub_feature_flags(vue_group_members_list: false)
+
group.add_owner(user1)
sign_in(user1)
end
diff --git a/spec/features/groups/members/master_manages_access_requests_spec.rb b/spec/features/groups/members/master_manages_access_requests_spec.rb
index 71c9b280ebe..44fd7380b79 100644
--- a/spec/features/groups/members/master_manages_access_requests_spec.rb
+++ b/spec/features/groups/members/master_manages_access_requests_spec.rb
@@ -3,6 +3,10 @@
require 'spec_helper'
RSpec.describe 'Groups > Members > Maintainer manages access requests' do
+ before do
+ stub_feature_flags(vue_group_members_list: false)
+ end
+
it_behaves_like 'Maintainer manages access requests' do
let(:has_tabs) { true }
let(:entity) { create(:group, :public) }
diff --git a/spec/features/groups/members/search_members_spec.rb b/spec/features/groups/members/search_members_spec.rb
index ad4f5c0b579..a95b59cece1 100644
--- a/spec/features/groups/members/search_members_spec.rb
+++ b/spec/features/groups/members/search_members_spec.rb
@@ -14,6 +14,8 @@ RSpec.describe 'Search group member' do
end
before do
+ stub_feature_flags(vue_group_members_list: false)
+
sign_in(user)
visit group_group_members_path(guest_group)
end
diff --git a/spec/features/groups/members/sort_members_spec.rb b/spec/features/groups/members/sort_members_spec.rb
index cfc0e421aeb..d940550b18a 100644
--- a/spec/features/groups/members/sort_members_spec.rb
+++ b/spec/features/groups/members/sort_members_spec.rb
@@ -8,6 +8,8 @@ RSpec.describe 'Groups > Members > Sort members' do
let(:group) { create(:group) }
before do
+ stub_feature_flags(vue_group_members_list: false)
+
create(:group_member, :owner, user: owner, group: group, created_at: 5.days.ago)
create(:group_member, :developer, user: developer, group: group, created_at: 3.days.ago)
diff --git a/spec/features/markdown/keyboard_shortcuts_spec.rb b/spec/features/markdown/keyboard_shortcuts_spec.rb
new file mode 100644
index 00000000000..ff028a0281f
--- /dev/null
+++ b/spec/features/markdown/keyboard_shortcuts_spec.rb
@@ -0,0 +1,101 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Markdown keyboard shortcuts', :js do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { create(:user) }
+
+ before do
+ project.add_developer(user)
+
+ gitlab_sign_in(user)
+
+ visit path_to_visit
+
+ wait_for_requests
+ end
+
+ shared_examples 'keyboard shortcuts for modifier key' do
+ it 'bolds text when <modifier>+B is pressed' do
+ type_and_select('bold')
+
+ markdown_field.send_keys([modifier_key, 'b'])
+
+ expect(markdown_field.value).to eq('**bold**')
+ end
+
+ it 'italicizes text when <modifier>+I is pressed' do
+ type_and_select('italic')
+
+ markdown_field.send_keys([modifier_key, 'i'])
+
+ expect(markdown_field.value).to eq('_italic_')
+ end
+
+ it 'links text when <modifier>+K is pressed' do
+ type_and_select('link')
+
+ markdown_field.send_keys([modifier_key, 'k'])
+
+ expect(markdown_field.value).to eq('[link](url)')
+
+ # Type some more text to ensure the cursor
+ # and selection are set correctly
+ markdown_field.send_keys('https://example.com')
+
+ expect(markdown_field.value).to eq('[link](https://example.com)')
+ end
+
+ it 'does not affect non-markdown fields on the same page' do
+ non_markdown_field.send_keys('some text')
+
+ non_markdown_field.send_keys([modifier_key, 'b'])
+
+ expect(focused_element).to eq(non_markdown_field.native)
+ expect(markdown_field.value).to eq('')
+ end
+ end
+
+ shared_examples 'keyboard shortcuts for implementation' do
+ context 'Ctrl key' do
+ let(:modifier_key) { :control }
+
+ it_behaves_like 'keyboard shortcuts for modifier key'
+ end
+
+ context '⌘ key' do
+ let(:modifier_key) { :command }
+
+ it_behaves_like 'keyboard shortcuts for modifier key'
+ end
+ end
+
+ context 'Vue.js markdown editor' do
+ let(:path_to_visit) { new_project_release_path(project) }
+ let(:markdown_field) { find_field('Release notes') }
+ let(:non_markdown_field) { find_field('Release title') }
+
+ it_behaves_like 'keyboard shortcuts for implementation'
+ end
+
+ context 'Haml markdown editor' do
+ let(:path_to_visit) { new_project_issue_path(project) }
+ let(:markdown_field) { find_field('Description') }
+ let(:non_markdown_field) { find_field('Title') }
+
+ it_behaves_like 'keyboard shortcuts for implementation'
+ end
+
+ def type_and_select(text)
+ markdown_field.send_keys(text)
+
+ text.length.times do
+ markdown_field.send_keys([:shift, :arrow_left])
+ end
+ end
+
+ def focused_element
+ page.driver.browser.switch_to.active_element
+ end
+end
diff --git a/spec/fixtures/api/schemas/entities/group_group_link.json b/spec/fixtures/api/schemas/entities/group_group_link.json
new file mode 100644
index 00000000000..4c9aae140d2
--- /dev/null
+++ b/spec/fixtures/api/schemas/entities/group_group_link.json
@@ -0,0 +1,29 @@
+{
+ "type": "object",
+ "required": ["id", "created_at", "expires_at", "access_level"],
+ "properties": {
+ "id": { "type": "integer" },
+ "created_at": { "type": "date-time" },
+ "expires_at": { "type": ["date-time", "null"] },
+ "access_level": {
+ "type": "object",
+ "required": ["integer_value", "string_value"],
+ "properties": {
+ "integer_value": { "type": "integer" },
+ "string_value": { "type": "string" }
+ }
+ },
+ "shared_with_group": {
+ "type": "object",
+ "required": ["id", "name", "full_name", "full_path", "avatar_url", "web_url"],
+ "properties": {
+ "id": { "type": "integer" },
+ "name": { "type": "string" },
+ "full_name": { "type": "string" },
+ "full_path": { "type": "string" },
+ "avatar_url": { "type": ["string", "null"] },
+ "web_url": { "type": "string" }
+ }
+ }
+ }
+}
diff --git a/spec/fixtures/api/schemas/group_group_links.json b/spec/fixtures/api/schemas/group_group_links.json
new file mode 100644
index 00000000000..f8b4e7f035b
--- /dev/null
+++ b/spec/fixtures/api/schemas/group_group_links.json
@@ -0,0 +1,6 @@
+{
+ "type": "array",
+ "items": {
+ "$ref": "entities/group_group_link.json"
+ }
+}
diff --git a/spec/fixtures/api/schemas/group_member.json b/spec/fixtures/api/schemas/group_member.json
new file mode 100644
index 00000000000..035c862d229
--- /dev/null
+++ b/spec/fixtures/api/schemas/group_member.json
@@ -0,0 +1,78 @@
+{
+ "type": "object",
+ "required": [
+ "id",
+ "created_at",
+ "expires_at",
+ "access_level",
+ "requested_at",
+ "source",
+ "can_update",
+ "can_remove",
+ "can_override"
+ ],
+ "properties": {
+ "id": { "type": "integer" },
+ "created_at": { "type": "date-time" },
+ "expires_at": { "type": ["date-time", "null"] },
+ "requested_at": { "type": ["date-time", "null"] },
+ "can_update": { "type": "boolean" },
+ "can_remove": { "type": "boolean" },
+ "can_override": { "type": "boolean" },
+ "access_level": {
+ "type": "object",
+ "required": ["integer_value", "string_value"],
+ "properties": {
+ "integer_value": { "type": "integer" },
+ "string_value": { "type": "string" }
+ }
+ },
+ "source": {
+ "type": "object",
+ "required": ["id", "name", "web_url"],
+ "properties": {
+ "id": { "type": "integer" },
+ "name": { "type": "string" },
+ "web_url": { "type": "string" }
+ }
+ },
+ "created_by": {
+ "type": "object",
+ "required": ["name", "web_url"],
+ "properties": {
+ "name": { "type": "string" },
+ "web_url": { "type": "string" }
+ }
+ },
+ "user": {
+ "type": "object",
+ "required": [
+ "id",
+ "name",
+ "username",
+ "avatar_url",
+ "web_url",
+ "blocked",
+ "two_factor_enabled"
+ ],
+ "properties": {
+ "id": { "type": "integer" },
+ "name": { "type": "string" },
+ "username": { "type": "string" },
+ "avatar_url": { "type": ["string", "null"] },
+ "web_url": { "type": "string" },
+ "blocked": { "type": "boolean" },
+ "two_factor_enabled": { "type": "boolean" }
+ }
+ },
+ "invite": {
+ "type": "object",
+ "required": ["email", "avatar_url", "can_resend"],
+ "properties": {
+ "email": { "type": "string" },
+ "avatar_url": { "type": "string" },
+ "can_resend": { "type": "boolean" }
+ }
+ }
+ }
+}
diff --git a/spec/fixtures/api/schemas/group_members.json b/spec/fixtures/api/schemas/group_members.json
new file mode 100644
index 00000000000..6268c7ef4d8
--- /dev/null
+++ b/spec/fixtures/api/schemas/group_members.json
@@ -0,0 +1,6 @@
+{
+ "type": "array",
+ "items": {
+ "$ref": "group_member.json"
+ }
+}
diff --git a/spec/frontend/design_management/pages/design/index_spec.js b/spec/frontend/design_management/pages/design/index_spec.js
index d78d3dc7edd..d9f7146d258 100644
--- a/spec/frontend/design_management/pages/design/index_spec.js
+++ b/spec/frontend/design_management/pages/design/index_spec.js
@@ -22,10 +22,6 @@ import mockResponseNoDesigns from '../../mock_data/no_designs';
import mockAllVersions from '../../mock_data/all_versions';
jest.mock('~/flash');
-jest.mock('mousetrap', () => ({
- bind: jest.fn(),
- unbind: jest.fn(),
-}));
const focusInput = jest.fn();
diff --git a/spec/frontend/design_management/router_spec.js b/spec/frontend/design_management/router_spec.js
index 2b8c7ee959b..d4cb9f75a77 100644
--- a/spec/frontend/design_management/router_spec.js
+++ b/spec/frontend/design_management/router_spec.js
@@ -35,11 +35,6 @@ function factory(routeArg) {
});
}
-jest.mock('mousetrap', () => ({
- bind: jest.fn(),
- unbind: jest.fn(),
-}));
-
describe('Design management router', () => {
afterEach(() => {
window.location.hash = '';
diff --git a/spec/frontend/groups/members/index_spec.js b/spec/frontend/groups/members/index_spec.js
new file mode 100644
index 00000000000..8673f257a1f
--- /dev/null
+++ b/spec/frontend/groups/members/index_spec.js
@@ -0,0 +1,58 @@
+import { createWrapper } from '@vue/test-utils';
+import initGroupMembersApp from '~/groups/members';
+import GroupMembersApp from '~/groups/members/components/app.vue';
+import { membersJsonString, membersParsed } from './mock_data';
+
+describe('initGroupMembersApp', () => {
+ let el;
+ let wrapper;
+
+ const setup = () => {
+ const vm = initGroupMembersApp(el);
+ wrapper = createWrapper(vm);
+ };
+
+ const getGroupMembersApp = () => wrapper.find(GroupMembersApp);
+
+ beforeEach(() => {
+ el = document.createElement('div');
+ el.setAttribute('data-members', membersJsonString);
+ el.setAttribute('data-current-user-id', '123');
+ el.setAttribute('data-group-id', '234');
+
+ document.body.appendChild(el);
+ });
+
+ afterEach(() => {
+ document.body.innerHTML = '';
+ el = null;
+
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it('parses and passes `currentUserId` prop to `GroupMembersApp`', () => {
+ setup();
+
+ expect(getGroupMembersApp().props('currentUserId')).toBe(123);
+ });
+
+ it('does not pass `currentUserId` prop if not provided by the data attribute (user is not logged in)', () => {
+ el.removeAttribute('data-current-user-id');
+ setup();
+
+ expect(getGroupMembersApp().props('currentUserId')).toBeNull();
+ });
+
+ it('parses and passes `groupId` prop to `GroupMembersApp`', () => {
+ setup();
+
+ expect(getGroupMembersApp().props('groupId')).toBe(234);
+ });
+
+ it('parses and passes `members` prop to `GroupMembersApp`', () => {
+ setup();
+
+ expect(getGroupMembersApp().props('members')).toEqual(membersParsed);
+ });
+});
diff --git a/spec/frontend/groups/members/mock_data.js b/spec/frontend/groups/members/mock_data.js
new file mode 100644
index 00000000000..b84c9c6d446
--- /dev/null
+++ b/spec/frontend/groups/members/mock_data.js
@@ -0,0 +1,33 @@
+export const membersJsonString =
+ '[{"requested_at":null,"can_update":true,"can_remove":true,"can_override":false,"access_level":{"integer_value":50,"string_value":"Owner"},"source":{"id":323,"name":"My group / my subgroup","web_url":"http://127.0.0.1:3000/groups/my-group/my-subgroup"},"user":{"id":1,"name":"Administrator","username":"root","web_url":"http://127.0.0.1:3000/root","avatar_url":"https://www.gravatar.com/avatar/4816142ef496f956a277bedf1a40607b?s=80\u0026d=identicon","blocked":false,"two_factor_enabled":false},"id":524,"created_at":"2020-08-21T21:33:27.631Z","expires_at":null,"using_license":false,"group_sso":false,"group_managed_account":false}]';
+
+export const membersParsed = [
+ {
+ requestedAt: null,
+ canUpdate: true,
+ canRemove: true,
+ canOverride: false,
+ accessLevel: { integerValue: 50, stringValue: 'Owner' },
+ source: {
+ id: 323,
+ name: 'My group / my subgroup',
+ webUrl: 'http://127.0.0.1:3000/groups/my-group/my-subgroup',
+ },
+ user: {
+ id: 1,
+ name: 'Administrator',
+ username: 'root',
+ webUrl: 'http://127.0.0.1:3000/root',
+ avatarUrl:
+ 'https://www.gravatar.com/avatar/4816142ef496f956a277bedf1a40607b?s=80&d=identicon',
+ blocked: false,
+ twoFactorEnabled: false,
+ },
+ id: 524,
+ createdAt: '2020-08-21T21:33:27.631Z',
+ expiresAt: null,
+ usingLicense: false,
+ groupSso: false,
+ groupManagedAccount: false,
+ },
+];
diff --git a/spec/frontend/shortcuts_spec.js b/spec/frontend/shortcuts_spec.js
index 3d16074154c..21258e09356 100644
--- a/spec/frontend/shortcuts_spec.js
+++ b/spec/frontend/shortcuts_spec.js
@@ -1,6 +1,18 @@
import $ from 'jquery';
+import { flatten } from 'lodash';
import Shortcuts from '~/behaviors/shortcuts/shortcuts';
+const mockMousetrap = {
+ bind: jest.fn(),
+ unbind: jest.fn(),
+};
+
+jest.mock('mousetrap', () => {
+ return jest.fn().mockImplementation(() => mockMousetrap);
+});
+
+jest.mock('mousetrap/plugins/pause/mousetrap-pause', () => {});
+
describe('Shortcuts', () => {
const fixtureName = 'snippets/show.html';
const createEvent = (type, target) =>
@@ -10,16 +22,16 @@ describe('Shortcuts', () => {
preloadFixtures(fixtureName);
- describe('toggleMarkdownPreview', () => {
- beforeEach(() => {
- loadFixtures(fixtureName);
+ beforeEach(() => {
+ loadFixtures(fixtureName);
- jest.spyOn(document.querySelector('.js-new-note-form .js-md-preview-button'), 'focus');
- jest.spyOn(document.querySelector('.edit-note .js-md-preview-button'), 'focus');
+ jest.spyOn(document.querySelector('.js-new-note-form .js-md-preview-button'), 'focus');
+ jest.spyOn(document.querySelector('.edit-note .js-md-preview-button'), 'focus');
- new Shortcuts(); // eslint-disable-line no-new
- });
+ new Shortcuts(); // eslint-disable-line no-new
+ });
+ describe('toggleMarkdownPreview', () => {
it('focuses preview button in form', () => {
Shortcuts.toggleMarkdownPreview(
createEvent('KeyboardEvent', document.querySelector('.js-new-note-form .js-note-text')),
@@ -43,4 +55,63 @@ describe('Shortcuts', () => {
expect(document.querySelector('.edit-note .js-md-preview-button').focus).toHaveBeenCalled();
});
});
+
+ describe('markdown shortcuts', () => {
+ let shortcuts;
+
+ beforeEach(() => {
+ // Get all shortcuts specified with md-shortcuts attributes in the fixture.
+ // `shortcuts` will look something like this:
+ // [
+ // [ 'command+b', 'ctrl+b' ],
+ // [ 'command+i', 'ctrl+i' ],
+ // [ 'command+k', 'ctrl+k' ]
+ // ]
+ shortcuts = $('.edit-note .js-md')
+ .map(function getShortcutsFromToolbarBtn() {
+ const mdShortcuts = $(this).data('md-shortcuts');
+
+ // jQuery.map() automatically unwraps arrays, so we
+ // have to double wrap the array to counteract this:
+ // https://stackoverflow.com/a/4875669/1063392
+ return mdShortcuts ? [mdShortcuts] : undefined;
+ })
+ .get();
+ });
+
+ describe('initMarkdownEditorShortcuts', () => {
+ beforeEach(() => {
+ Shortcuts.initMarkdownEditorShortcuts($('.edit-note textarea'));
+ });
+
+ it('attaches a Mousetrap handler for every markdown shortcut specified with md-shortcuts', () => {
+ const expectedCalls = shortcuts.map(s => [s, expect.any(Function)]);
+
+ expect(mockMousetrap.bind.mock.calls).toEqual(expectedCalls);
+ });
+
+ it('attaches a stopCallback that allows each markdown shortcut specified with md-shortcuts', () => {
+ flatten(shortcuts).forEach(s => {
+ expect(mockMousetrap.stopCallback(null, null, s)).toBe(false);
+ });
+ });
+ });
+
+ describe('removeMarkdownEditorShortcuts', () => {
+ it('does nothing if initMarkdownEditorShortcuts was not previous called', () => {
+ Shortcuts.removeMarkdownEditorShortcuts($('.edit-note textarea'));
+
+ expect(mockMousetrap.unbind.mock.calls).toEqual([]);
+ });
+
+ it('removes Mousetrap handlers for every markdown shortcut specified with md-shortcuts', () => {
+ Shortcuts.initMarkdownEditorShortcuts($('.edit-note textarea'));
+ Shortcuts.removeMarkdownEditorShortcuts($('.edit-note textarea'));
+
+ const expectedCalls = shortcuts.map(s => [s]);
+
+ expect(mockMousetrap.unbind.mock.calls).toEqual(expectedCalls);
+ });
+ });
+ });
});
diff --git a/spec/frontend/vue_shared/components/markdown/header_spec.js b/spec/frontend/vue_shared/components/markdown/header_spec.js
index 551d781d296..82bc9b9fe08 100644
--- a/spec/frontend/vue_shared/components/markdown/header_spec.js
+++ b/spec/frontend/vue_shared/components/markdown/header_spec.js
@@ -22,6 +22,12 @@ describe('Markdown field header component', () => {
.at(0);
beforeEach(() => {
+ window.gl = {
+ client: {
+ isMac: true,
+ },
+ };
+
createWrapper();
});
@@ -30,24 +36,40 @@ describe('Markdown field header component', () => {
wrapper = null;
});
- it('renders markdown header buttons', () => {
- const buttons = [
- 'Add bold text',
- 'Add italic text',
- 'Insert a quote',
- 'Insert suggestion',
- 'Insert code',
- 'Add a link',
- 'Add a bullet list',
- 'Add a numbered list',
- 'Add a task list',
- 'Add a table',
- 'Go full screen',
- ];
- const elements = findToolbarButtons();
-
- elements.wrappers.forEach((buttonEl, index) => {
- expect(buttonEl.props('buttonTitle')).toBe(buttons[index]);
+ describe('markdown header buttons', () => {
+ it('renders the buttons with the correct title', () => {
+ const buttons = [
+ 'Add bold text (⌘B)',
+ 'Add italic text (⌘I)',
+ 'Insert a quote',
+ 'Insert suggestion',
+ 'Insert code',
+ 'Add a link (⌘K)',
+ 'Add a bullet list',
+ 'Add a numbered list',
+ 'Add a task list',
+ 'Add a table',
+ 'Go full screen',
+ ];
+ const elements = findToolbarButtons();
+
+ elements.wrappers.forEach((buttonEl, index) => {
+ expect(buttonEl.props('buttonTitle')).toBe(buttons[index]);
+ });
+ });
+
+ describe('when the user is on a non-Mac', () => {
+ beforeEach(() => {
+ delete window.gl.client.isMac;
+
+ createWrapper();
+ });
+
+ it('renders keyboard shortcuts with Ctrl+ instead of ⌘', () => {
+ const boldButton = findToolbarButtonByProp('icon', 'bold');
+
+ expect(boldButton.props('buttonTitle')).toBe('Add bold text (Ctrl+B)');
+ });
});
});
diff --git a/spec/frontend/vue_shared/components/markdown/toolbar_button_spec.js b/spec/frontend/vue_shared/components/markdown/toolbar_button_spec.js
new file mode 100644
index 00000000000..8a7946fd7b1
--- /dev/null
+++ b/spec/frontend/vue_shared/components/markdown/toolbar_button_spec.js
@@ -0,0 +1,47 @@
+import { shallowMount } from '@vue/test-utils';
+import ToolbarButton from '~/vue_shared/components/markdown/toolbar_button.vue';
+
+describe('toolbar_button', () => {
+ let wrapper;
+
+ const defaultProps = {
+ buttonTitle: 'test button',
+ icon: 'rocket',
+ tag: 'test tag',
+ };
+
+ const createComponent = propUpdates => {
+ wrapper = shallowMount(ToolbarButton, {
+ propsData: {
+ ...defaultProps,
+ ...propUpdates,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ const getButtonShortcutsAttr = () => {
+ return wrapper.find('button').attributes('data-md-shortcuts');
+ };
+
+ describe('keyboard shortcuts', () => {
+ it.each`
+ shortcutsProp | mdShortcutsAttr
+ ${undefined} | ${JSON.stringify([])}
+ ${[]} | ${JSON.stringify([])}
+ ${'command+b'} | ${JSON.stringify(['command+b'])}
+ ${['command+b', 'ctrl+b']} | ${JSON.stringify(['command+b', 'ctrl+b'])}
+ `(
+ 'adds the attribute data-md-shortcuts="$mdShortcutsAttr" to the button when the shortcuts prop is $shortcutsProp',
+ ({ shortcutsProp, mdShortcutsAttr }) => {
+ createComponent({ shortcuts: shortcutsProp });
+
+ expect(getButtonShortcutsAttr()).toBe(mdShortcutsAttr);
+ },
+ );
+ });
+});
diff --git a/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb b/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb
index 2fe3e86ec14..ae3097c1d9e 100644
--- a/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb
+++ b/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb
@@ -29,4 +29,11 @@ RSpec.describe Resolvers::MergeRequestPipelinesResolver do
it 'resolves only MRs for the passed merge request' do
expect(resolve_pipelines).to contain_exactly(pipeline)
end
+
+ describe 'with archived project' do
+ let(:archived_project) { create(:project, :archived) }
+ let(:merge_request) { create(:merge_request, source_project: archived_project) }
+
+ it { expect(resolve_pipelines).not_to contain_exactly(pipeline) }
+ end
end
diff --git a/spec/graphql/types/merge_request_type_spec.rb b/spec/graphql/types/merge_request_type_spec.rb
index 1279f01f104..7272a952f77 100644
--- a/spec/graphql/types/merge_request_type_spec.rb
+++ b/spec/graphql/types/merge_request_type_spec.rb
@@ -27,6 +27,7 @@ RSpec.describe GitlabSchema.types['MergeRequest'] do
upvotes downvotes head_pipeline pipelines task_completion_status
milestone assignees participants subscribed labels discussion_locked time_estimate
total_time_spent reference author merged_at commit_count current_user_todos
+ conflicts auto_merge_enabled
]
if Gitlab.ee?
diff --git a/spec/graphql/types/permission_types/merge_request_spec.rb b/spec/graphql/types/permission_types/merge_request_spec.rb
index 73a178540a6..2849dead9a8 100644
--- a/spec/graphql/types/permission_types/merge_request_spec.rb
+++ b/spec/graphql/types/permission_types/merge_request_spec.rb
@@ -7,7 +7,8 @@ RSpec.describe Types::PermissionTypes::MergeRequest do
expected_permissions = [
:read_merge_request, :admin_merge_request, :update_merge_request,
:create_note, :push_to_source_branch, :remove_source_branch,
- :cherry_pick_on_current_merge_request, :revert_on_current_merge_request
+ :cherry_pick_on_current_merge_request, :revert_on_current_merge_request,
+ :can_merge
]
expect(described_class).to have_graphql_fields(expected_permissions)
diff --git a/spec/haml_lint/linter/documentation_links_spec.rb b/spec/haml_lint/linter/documentation_links_spec.rb
index aa4c5dd7c39..68de8317b82 100644
--- a/spec/haml_lint/linter/documentation_links_spec.rb
+++ b/spec/haml_lint/linter/documentation_links_spec.rb
@@ -20,13 +20,6 @@ RSpec.describe HamlLint::Linter::DocumentationLinks do
it { is_expected.not_to report_lint }
end
- # TODO: Remove me after https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39715 is merged
- context 'when link_to points to the existing file with partially matching anchor' do
- let(:haml) { "= link_to 'Description', help_page_path('README.md', anchor: 'overview-premium'), target: '_blank'" }
-
- it { is_expected.not_to report_lint }
- end
-
context 'when link_to points to the existing file path without .md extension' do
let(:haml) { "= link_to 'Description', help_page_path('README')" }
diff --git a/spec/helpers/groups/group_members_helper_spec.rb b/spec/helpers/groups/group_members_helper_spec.rb
index 90792331d9b..a25bf1c4157 100644
--- a/spec/helpers/groups/group_members_helper_spec.rb
+++ b/spec/helpers/groups/group_members_helper_spec.rb
@@ -3,6 +3,8 @@
require "spec_helper"
RSpec.describe Groups::GroupMembersHelper do
+ include MembersPresentation
+
describe '.group_member_select_options' do
let(:group) { create(:group) }
@@ -14,4 +16,50 @@ RSpec.describe Groups::GroupMembersHelper do
expect(helper.group_member_select_options).to include(multiple: true, scope: :all, email_user: true)
end
end
+
+ describe '#linked_groups_data_json' do
+ include_context 'group_group_link'
+
+ it 'matches json schema' do
+ json = helper.linked_groups_data_json(shared_group.shared_with_group_links)
+
+ expect(json).to match_schema('group_group_links')
+ end
+ end
+
+ describe '#members_data_json' do
+ let(:current_user) { create(:user) }
+ let(:group) { create(:group) }
+
+ before do
+ allow(helper).to receive(:can?).with(current_user, :owner_access, group).and_return(true)
+ allow(helper).to receive(:current_user).and_return(current_user)
+ end
+
+ shared_examples 'group_members.json' do
+ it 'matches json schema' do
+ json = helper.members_data_json(group, present_members([group_member]))
+
+ expect(json).to match_schema('group_members')
+ end
+ end
+
+ context 'for a group member' do
+ let(:group_member) { create(:group_member, group: group, created_by: current_user) }
+
+ it_behaves_like 'group_members.json'
+ end
+
+ context 'for an invited group member' do
+ let(:group_member) { create(:group_member, :invited, group: group, created_by: current_user) }
+
+ it_behaves_like 'group_members.json'
+ end
+
+ context 'for an access request' do
+ let(:group_member) { create(:group_member, :access_request, group: group, created_by: current_user) }
+
+ it_behaves_like 'group_members.json'
+ end
+ end
end
diff --git a/spec/lib/gitlab/utils/markdown_spec.rb b/spec/lib/gitlab/utils/markdown_spec.rb
index 0ac594ca830..93d91f7ed90 100644
--- a/spec/lib/gitlab/utils/markdown_spec.rb
+++ b/spec/lib/gitlab/utils/markdown_spec.rb
@@ -66,6 +66,22 @@ RSpec.describe Gitlab::Utils::Markdown do
is_expected.to eq 'my-header'
end
end
+
+ context 'with "*" around a product suffix' do
+ let(:string) { 'My Header **(STARTER)**' }
+
+ it 'ignores a product suffix' do
+ is_expected.to eq 'my-header'
+ end
+ end
+
+ context 'with "*" around a product suffix and only modifier' do
+ let(:string) { 'My Header **(STARTER ONLY)**' }
+
+ it 'ignores a product suffix' do
+ is_expected.to eq 'my-header'
+ end
+ end
end
context 'when string is empty' do
diff --git a/spec/requests/api/graphql/project/merge_request_spec.rb b/spec/requests/api/graphql/project/merge_request_spec.rb
index c39358a2db1..fae52fe814d 100644
--- a/spec/requests/api/graphql/project/merge_request_spec.rb
+++ b/spec/requests/api/graphql/project/merge_request_spec.rb
@@ -124,7 +124,8 @@ RSpec.describe 'getting merge request information nested in a project' do
'removeSourceBranch' => false,
'cherryPickOnCurrentMergeRequest' => false,
'revertOnCurrentMergeRequest' => false,
- 'updateMergeRequest' => false
+ 'updateMergeRequest' => false,
+ 'canMerge' => false
}
post_graphql(query, current_user: current_user)
diff --git a/spec/serializers/group_group_link_entity_spec.rb b/spec/serializers/group_group_link_entity_spec.rb
new file mode 100644
index 00000000000..8384563e3e6
--- /dev/null
+++ b/spec/serializers/group_group_link_entity_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GroupGroupLinkEntity do
+ include_context 'group_group_link'
+
+ subject(:json) { described_class.new(group_group_link).to_json }
+
+ it 'matches json schema' do
+ expect(json).to match_schema('entities/group_group_link')
+ end
+end
diff --git a/spec/serializers/group_group_link_serializer_spec.rb b/spec/serializers/group_group_link_serializer_spec.rb
new file mode 100644
index 00000000000..0d977ea0a9a
--- /dev/null
+++ b/spec/serializers/group_group_link_serializer_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GroupGroupLinkSerializer do
+ include_context 'group_group_link'
+
+ subject(:json) { described_class.new.represent(shared_group.shared_with_group_links).to_json }
+
+ it 'matches json schema' do
+ expect(json).to match_schema('group_group_links')
+ end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 9e3e4cc6d64..11a83bd9501 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -204,6 +204,10 @@ RSpec.configure do |config|
# unified diff lines works as expected
stub_feature_flags(unified_diff_lines: false)
+ # Merge request widget GraphQL requests are disabled in the tests
+ # for now whilst we migrate as much as we can over the GraphQL
+ stub_feature_flags(merge_request_widget_graphql: false)
+
enable_rugged = example.metadata[:enable_rugged].present?
# Disable Rugged features by default
diff --git a/spec/support/shared_contexts/serializers/group_group_link_shared_context.rb b/spec/support/shared_contexts/serializers/group_group_link_shared_context.rb
new file mode 100644
index 00000000000..fce78957eba
--- /dev/null
+++ b/spec/support/shared_contexts/serializers/group_group_link_shared_context.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+RSpec.shared_context 'group_group_link' do
+ let(:shared_with_group) { create(:group) }
+ let(:shared_group) { create(:group) }
+
+ let!(:group_group_link) do
+ create(
+ :group_group_link,
+ {
+ shared_group: shared_group,
+ shared_with_group: shared_with_group,
+ expires_at: '2020-05-12'
+ }
+ )
+ end
+end