summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouwe Maan <douwe@gitlab.com>2017-03-06 21:13:27 +0000
committerDouwe Maan <douwe@gitlab.com>2017-03-06 21:13:27 +0000
commitd617182a1a6f9b19a8a6d117fa3e507fa2b338a8 (patch)
treee24db462cfb8ed1854e2203a78d6d22b071232e5
parentf96d5112dca596fb85718b3cca2a7b30b3877207 (diff)
parentb70d151db99d6d64f3514006c4fa6c8142e1b785 (diff)
downloadgitlab-ce-rs-carrierwave-db.tar.gz
Merge branch 'master' into 'rs-carrierwave-db'rs-carrierwave-db
# Conflicts: # spec/models/group_spec.rb
-rw-r--r--app/assets/javascripts/behaviors/bind_in_out.js47
-rw-r--r--app/assets/javascripts/behaviors/toggler_behavior.js3
-rw-r--r--app/assets/javascripts/dispatcher.js8
-rw-r--r--app/assets/javascripts/main.js1
-rw-r--r--app/assets/stylesheets/pages/groups.scss16
-rw-r--r--app/controllers/groups_controller.rb12
-rw-r--r--app/models/chat_team.rb5
-rw-r--r--app/models/group.rb10
-rw-r--r--app/models/namespace.rb1
-rw-r--r--app/services/groups/create_service.rb15
-rw-r--r--app/services/mattermost/create_team_service.rb14
-rw-r--r--app/views/groups/_create_chat_team.html.haml16
-rw-r--r--app/views/groups/new.html.haml2
-rw-r--r--app/views/shared/_group_form.html.haml3
-rw-r--r--app/views/shared/icons/_icon_mattermost.svg1
-rw-r--r--db/migrate/20170120131253_create_chat_teams.rb18
-rw-r--r--db/schema.rb11
-rw-r--r--lib/mattermost/client.rb2
-rw-r--r--lib/mattermost/session.rb2
-rw-r--r--lib/mattermost/team.rb11
-rw-r--r--spec/features/groups_spec.rb38
-rw-r--r--spec/javascripts/behaviors/bind_in_out_spec.js189
-rw-r--r--spec/models/chat_team_spec.rb10
-rw-r--r--spec/models/group_spec.rb1
-rw-r--r--spec/services/groups/create_service_spec.rb22
25 files changed, 446 insertions, 12 deletions
diff --git a/app/assets/javascripts/behaviors/bind_in_out.js b/app/assets/javascripts/behaviors/bind_in_out.js
new file mode 100644
index 00000000000..886f127b06b
--- /dev/null
+++ b/app/assets/javascripts/behaviors/bind_in_out.js
@@ -0,0 +1,47 @@
+class BindInOut {
+ constructor(bindIn, bindOut) {
+ this.in = bindIn;
+ this.out = bindOut;
+
+ this.eventWrapper = {};
+ this.eventType = /(INPUT|TEXTAREA)/.test(bindIn.tagName) ? 'keyup' : 'change';
+ }
+
+ addEvents() {
+ this.eventWrapper.updateOut = this.updateOut.bind(this);
+
+ this.in.addEventListener(this.eventType, this.eventWrapper.updateOut);
+
+ return this;
+ }
+
+ updateOut() {
+ this.out.textContent = this.in.value;
+
+ return this;
+ }
+
+ removeEvents() {
+ this.in.removeEventListener(this.eventType, this.eventWrapper.updateOut);
+
+ return this;
+ }
+
+ static initAll() {
+ const ins = document.querySelectorAll('*[data-bind-in]');
+
+ return [].map.call(ins, anIn => BindInOut.init(anIn));
+ }
+
+ static init(anIn, anOut) {
+ const out = anOut || document.querySelector(`*[data-bind-out="${anIn.dataset.bindIn}"]`);
+
+ if (!out) return null;
+
+ const bindInOut = new BindInOut(anIn, out);
+
+ return bindInOut.addEvents().updateOut();
+ }
+}
+
+export default BindInOut;
diff --git a/app/assets/javascripts/behaviors/toggler_behavior.js b/app/assets/javascripts/behaviors/toggler_behavior.js
index a7181904ac9..0726c6c9636 100644
--- a/app/assets/javascripts/behaviors/toggler_behavior.js
+++ b/app/assets/javascripts/behaviors/toggler_behavior.js
@@ -21,8 +21,7 @@
// %a.js-toggle-button
// %div.js-toggle-content
//
- $('body').on('click', '.js-toggle-button', function(e) {
- e.preventDefault();
+ $('body').on('click', '.js-toggle-button', function() {
toggleContainer($(this).closest('.js-toggle-container'));
});
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index ef5785b5532..31f10f89245 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -35,6 +35,7 @@
/* global Labels */
/* global Shortcuts */
+import BindInOut from './behaviors/bind_in_out';
import GroupsList from './groups_list';
import ProjectsList from './projects_list';
@@ -229,9 +230,14 @@ const UserCallout = require('./user_callout');
new UsersSelect();
break;
case 'groups:new':
+ case 'admin:groups:new':
+ case 'groups:create':
+ case 'admin:groups:create':
+ BindInOut.initAll();
+ case 'groups:new':
+ case 'admin:groups:new':
case 'groups:edit':
case 'admin:groups:edit':
- case 'admin:groups:new':
new GroupAvatar();
break;
case 'projects:tree:show':
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index 798553c16ac..6ac659fd290 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -41,6 +41,7 @@ require('./behaviors/details_behavior');
require('./behaviors/quick_submit');
require('./behaviors/requires_input');
require('./behaviors/toggler_behavior');
+require('./behaviors/bind_in_out');
// blob
require('./blob/blob_ci_yaml');
diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss
index d377526e655..84d21e48463 100644
--- a/app/assets/stylesheets/pages/groups.scss
+++ b/app/assets/stylesheets/pages/groups.scss
@@ -73,3 +73,19 @@
}
}
}
+
+.mattermost-icon svg {
+ width: 16px;
+ height: 16px;
+ vertical-align: text-bottom;
+}
+
+.mattermost-team-name {
+ color: $gl-text-color-secondary;
+}
+
+.mattermost-info {
+ display: block;
+ color: $gl-text-color-secondary;
+ margin-top: 10px;
+}
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 15db5b7762d..4663b6e7fc6 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -32,7 +32,13 @@ class GroupsController < Groups::ApplicationController
@group = Groups::CreateService.new(current_user, group_params).execute
if @group.persisted?
- redirect_to @group, notice: "Group '#{@group.name}' was successfully created."
+ notice = if @group.chat_team.present?
+ "Group '#{@group.name}' and its Mattermost team were successfully created."
+ else
+ "Group '#{@group.name}' was successfully created."
+ end
+
+ redirect_to @group, notice: notice
else
render action: "new"
end
@@ -142,7 +148,9 @@ class GroupsController < Groups::ApplicationController
:request_access_enabled,
:share_with_group_lock,
:visibility_level,
- :parent_id
+ :parent_id,
+ :create_chat_team,
+ :chat_team_name
]
end
diff --git a/app/models/chat_team.rb b/app/models/chat_team.rb
new file mode 100644
index 00000000000..7952141a0d6
--- /dev/null
+++ b/app/models/chat_team.rb
@@ -0,0 +1,5 @@
+class ChatTeam < ActiveRecord::Base
+ validates :team_id, presence: true
+
+ belongs_to :namespace
+end
diff --git a/app/models/group.rb b/app/models/group.rb
index a1f12ed55ea..bd0ecae3da4 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -213,4 +213,14 @@ class Group < Namespace
def users_with_parents
User.where(id: members_with_parents.select(:user_id))
end
+
+ def mattermost_team_params
+ max_length = 59
+
+ {
+ name: path[0..max_length],
+ display_name: name[0..max_length],
+ type: public? ? 'O' : 'I' # Open vs Invite-only
+ }
+ end
end
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 3137dd32f93..d350f1d6770 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -20,6 +20,7 @@ class Namespace < ActiveRecord::Base
belongs_to :parent, class_name: "Namespace"
has_many :children, class_name: "Namespace", foreign_key: :parent_id
+ has_one :chat_team, dependent: :destroy
validates :owner, presence: true, unless: ->(n) { n.type == "Group" }
validates :name,
diff --git a/app/services/groups/create_service.rb b/app/services/groups/create_service.rb
index febeb661fb5..c4e9b8fd8e0 100644
--- a/app/services/groups/create_service.rb
+++ b/app/services/groups/create_service.rb
@@ -2,6 +2,7 @@ module Groups
class CreateService < Groups::BaseService
def initialize(user, params = {})
@current_user, @params = user, params.dup
+ @chat_team = @params.delete(:create_chat_team)
end
def execute
@@ -20,9 +21,23 @@ module Groups
end
@group.name ||= @group.path.dup
+
+ if create_chat_team?
+ response = Mattermost::CreateTeamService.new(@group, current_user).execute
+ return @group if @group.errors.any?
+
+ @group.build_chat_team(name: response['name'], team_id: response['id'])
+ end
+
@group.save
@group.add_owner(current_user)
@group
end
+
+ private
+
+ def create_chat_team?
+ Gitlab.config.mattermost.enabled && @chat_team && group.chat_team.nil?
+ end
end
end
diff --git a/app/services/mattermost/create_team_service.rb b/app/services/mattermost/create_team_service.rb
new file mode 100644
index 00000000000..e3206810f3a
--- /dev/null
+++ b/app/services/mattermost/create_team_service.rb
@@ -0,0 +1,14 @@
+module Mattermost
+ class CreateTeamService < ::BaseService
+ def initialize(group, current_user)
+ @group, @current_user = group, current_user
+ end
+
+ def execute
+ # The user that creates the team will be Team Admin
+ Mattermost::Team.new(current_user).create(@group.mattermost_team_params)
+ rescue Mattermost::ClientError => e
+ @group.errors.add(:mattermost_team, e.message)
+ end
+ end
+end
diff --git a/app/views/groups/_create_chat_team.html.haml b/app/views/groups/_create_chat_team.html.haml
new file mode 100644
index 00000000000..20de1b4c973
--- /dev/null
+++ b/app/views/groups/_create_chat_team.html.haml
@@ -0,0 +1,16 @@
+.form-group
+ = f.label :create_chat_team, class: 'control-label' do
+ %span.mattermost-icon
+ = custom_icon('icon_mattermost')
+ Mattermost
+ .col-sm-10
+ .checkbox.js-toggle-container
+ = f.label :create_chat_team do
+ .js-toggle-button= f.check_box(:create_chat_team, { checked: true }, true, false)
+ Create a Mattermost team for this group
+ %br
+ %small.light.js-toggle-content
+ Mattermost URL:
+ = Settings.mattermost.host
+ %span> /
+ %span{ "data-bind-out" => "create_chat_team" }
diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml
index 38d63fd9acc..000c7af2326 100644
--- a/app/views/groups/new.html.haml
+++ b/app/views/groups/new.html.haml
@@ -16,6 +16,8 @@
= render 'shared/visibility_level', f: f, visibility_level: default_group_visibility, can_change_visibility_level: true, form_model: @group
+ = render 'create_chat_team', f: f if Gitlab.config.mattermost.enabled
+
.form-group
.col-sm-offset-2.col-sm-10
= render 'shared/group_tips'
diff --git a/app/views/shared/_group_form.html.haml b/app/views/shared/_group_form.html.haml
index 02b7b2447ed..c2d9ac87b20 100644
--- a/app/views/shared/_group_form.html.haml
+++ b/app/views/shared/_group_form.html.haml
@@ -18,7 +18,8 @@
= f.text_field :path, placeholder: 'open-source', class: 'form-control',
autofocus: local_assigns[:autofocus] || false, required: true,
pattern: Gitlab::Regex::NAMESPACE_REGEX_STR_JS,
- title: 'Please choose a group name with no special characters.'
+ title: 'Please choose a group name with no special characters.',
+ "data-bind-in" => "#{'create_chat_team' if Gitlab.config.mattermost.enabled}"
- if parent
= f.hidden_field :parent_id, value: parent.id
diff --git a/app/views/shared/icons/_icon_mattermost.svg b/app/views/shared/icons/_icon_mattermost.svg
new file mode 100644
index 00000000000..d1c541523ab
--- /dev/null
+++ b/app/views/shared/icons/_icon_mattermost.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500"><path d="M250.05 34c1.9.04 3.8.11 5.6.2l-29.79 35.51c-.07.01-.15.03-.23.04C149.26 84.1 98.22 146.5 98.22 222.97c0 41.56 23.07 90.5 59.75 119.1 28.61 22.32 64.29 36.9 101.21 36.9 93.4 0 160.15-68.61 160.15-156 0-34.91-15.99-72.77-41.76-100.76l-1.63-47.39c54.45 39.15 89.95 103.02 90.06 175.17v.01c0 119.29-96.7 216-216 216-119.29 0-216-96.71-216-216S130.71 34 250 34h.05zm64.1 20.29c.66-.04 1.32.03 1.96.25 3.01 1 3.85 3.57 3.93 6.45l3.84 146.88c.76 28.66-17.16 68.44-60.39 68.56-30.97.08-63.68-20.83-63.68-60.13.01-14.73 5.61-31.26 19.25-48.11l90.03-111.18c1.15-1.42 3.08-2.58 5.06-2.72z"/></svg>
diff --git a/db/migrate/20170120131253_create_chat_teams.rb b/db/migrate/20170120131253_create_chat_teams.rb
new file mode 100644
index 00000000000..7995d383986
--- /dev/null
+++ b/db/migrate/20170120131253_create_chat_teams.rb
@@ -0,0 +1,18 @@
+class CreateChatTeams < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = true
+ DOWNTIME_REASON = "Adding a foreign key"
+
+ disable_ddl_transaction!
+
+ def change
+ create_table :chat_teams do |t|
+ t.references :namespace, null: false, index: { unique: true }, foreign_key: { on_delete: :cascade }
+ t.string :team_id
+ t.string :name
+
+ t.timestamps null: false
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 9f31ee8e6c5..624cf9432d0 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -175,6 +175,16 @@ ActiveRecord::Schema.define(version: 20170306170512) do
add_index "chat_names", ["service_id", "team_id", "chat_id"], name: "index_chat_names_on_service_id_and_team_id_and_chat_id", unique: true, using: :btree
add_index "chat_names", ["user_id", "service_id"], name: "index_chat_names_on_user_id_and_service_id", unique: true, using: :btree
+ create_table "chat_teams", force: :cascade do |t|
+ t.integer "namespace_id", null: false
+ t.string "team_id"
+ t.string "name"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ end
+
+ add_index "chat_teams", ["namespace_id"], name: "index_chat_teams_on_namespace_id", unique: true, using: :btree
+
create_table "ci_application_settings", force: :cascade do |t|
t.boolean "all_broken_builds"
t.boolean "add_pusher"
@@ -1352,6 +1362,7 @@ ActiveRecord::Schema.define(version: 20170306170512) do
add_index "web_hooks", ["project_id"], name: "index_web_hooks_on_project_id", using: :btree
add_foreign_key "boards", "projects"
+ add_foreign_key "chat_teams", "namespaces", on_delete: :cascade
add_foreign_key "ci_triggers", "users", column: "owner_id", name: "fk_e8e10d1964", on_delete: :cascade
add_foreign_key "issue_metrics", "issues", on_delete: :cascade
add_foreign_key "label_priorities", "labels", on_delete: :cascade
diff --git a/lib/mattermost/client.rb b/lib/mattermost/client.rb
index ad6df246091..3d60618006c 100644
--- a/lib/mattermost/client.rb
+++ b/lib/mattermost/client.rb
@@ -26,7 +26,7 @@ module Mattermost
def session_get(path, options = {})
with_session do |session|
- get(session, path, options)
+ get(session, path, options)
end
end
diff --git a/lib/mattermost/session.rb b/lib/mattermost/session.rb
index 5388966605d..688a79c0441 100644
--- a/lib/mattermost/session.rb
+++ b/lib/mattermost/session.rb
@@ -153,7 +153,7 @@ module Mattermost
yield
rescue HTTParty::Error => e
raise Mattermost::ConnectionError.new(e.message)
- rescue Errno::ECONNREFUSED
+ rescue Errno::ECONNREFUSED => e
raise Mattermost::ConnectionError.new(e.message)
end
end
diff --git a/lib/mattermost/team.rb b/lib/mattermost/team.rb
index afc152aa02e..2cdbbdece16 100644
--- a/lib/mattermost/team.rb
+++ b/lib/mattermost/team.rb
@@ -1,7 +1,18 @@
module Mattermost
class Team < Client
+ # Returns **all** teams for an admin
def all
session_get('/api/v3/teams/all').values
end
+
+ # Creates a team on the linked Mattermost instance, the team admin will be the
+ # `current_user` passed to the Mattermost::Client instance
+ def create(name:, display_name:, type:)
+ session_post('/api/v3/teams/create', body: {
+ name: name,
+ display_name: display_name,
+ type: type
+ }.to_json)
+ end
end
end
diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb
index 37b7c20239f..9f173dbc99d 100644
--- a/spec/features/groups_spec.rb
+++ b/spec/features/groups_spec.rb
@@ -43,6 +43,44 @@ feature 'Group', feature: true do
expect(page).to have_namespace_error_message
end
end
+
+ describe 'Mattermost team creation' do
+ before do
+ allow(Settings.mattermost).to receive_messages(enabled: mattermost_enabled)
+
+ visit new_group_path
+ end
+
+ context 'Mattermost enabled' do
+ let(:mattermost_enabled) { true }
+
+ it 'displays a team creation checkbox' do
+ expect(page).to have_selector('#group_create_chat_team')
+ end
+
+ it 'checks the checkbox by default' do
+ expect(find('#group_create_chat_team')['checked']).to eq(true)
+ end
+
+ it 'updates the team URL on graph path update', :js do
+ out_span = find('span[data-bind-out="create_chat_team"]')
+
+ expect(out_span.text).to be_empty
+
+ fill_in('group_path', with: 'test-group')
+
+ expect(out_span.text).to eq('test-group')
+ end
+ end
+
+ context 'Mattermost disabled' do
+ let(:mattermost_enabled) { false }
+
+ it 'doesnt show a team creation checkbox if Mattermost not enabled' do
+ expect(page).not_to have_selector('#group_create_chat_team')
+ end
+ end
+ end
end
describe 'create a nested group' do
diff --git a/spec/javascripts/behaviors/bind_in_out_spec.js b/spec/javascripts/behaviors/bind_in_out_spec.js
new file mode 100644
index 00000000000..dd9ab33289f
--- /dev/null
+++ b/spec/javascripts/behaviors/bind_in_out_spec.js
@@ -0,0 +1,189 @@
+import BindInOut from '~/behaviors/bind_in_out';
+import ClassSpecHelper from '../helpers/class_spec_helper';
+
+describe('BindInOut', function () {
+ describe('.constructor', function () {
+ beforeEach(function () {
+ this.in = {};
+ this.out = {};
+
+ this.bindInOut = new BindInOut(this.in, this.out);
+ });
+
+ it('should set .in', function () {
+ expect(this.bindInOut.in).toBe(this.in);
+ });
+
+ it('should set .out', function () {
+ expect(this.bindInOut.out).toBe(this.out);
+ });
+
+ it('should set .eventWrapper', function () {
+ expect(this.bindInOut.eventWrapper).toEqual({});
+ });
+
+ describe('if .in is an input', function () {
+ beforeEach(function () {
+ this.bindInOut = new BindInOut({ tagName: 'INPUT' });
+ });
+
+ it('should set .eventType to keyup ', function () {
+ expect(this.bindInOut.eventType).toEqual('keyup');
+ });
+ });
+
+ describe('if .in is a textarea', function () {
+ beforeEach(function () {
+ this.bindInOut = new BindInOut({ tagName: 'TEXTAREA' });
+ });
+
+ it('should set .eventType to keyup ', function () {
+ expect(this.bindInOut.eventType).toEqual('keyup');
+ });
+ });
+
+ describe('if .in is not an input or textarea', function () {
+ beforeEach(function () {
+ this.bindInOut = new BindInOut({ tagName: 'SELECT' });
+ });
+
+ it('should set .eventType to change ', function () {
+ expect(this.bindInOut.eventType).toEqual('change');
+ });
+ });
+ });
+
+ describe('.addEvents', function () {
+ beforeEach(function () {
+ this.in = jasmine.createSpyObj('in', ['addEventListener']);
+
+ this.bindInOut = new BindInOut(this.in);
+
+ this.addEvents = this.bindInOut.addEvents();
+ });
+
+ it('should set .eventWrapper.updateOut', function () {
+ expect(this.bindInOut.eventWrapper.updateOut).toEqual(jasmine.any(Function));
+ });
+
+ it('should call .addEventListener', function () {
+ expect(this.in.addEventListener)
+ .toHaveBeenCalledWith(
+ this.bindInOut.eventType,
+ this.bindInOut.eventWrapper.updateOut,
+ );
+ });
+
+ it('should return the instance', function () {
+ expect(this.addEvents).toBe(this.bindInOut);
+ });
+ });
+
+ describe('.updateOut', function () {
+ beforeEach(function () {
+ this.in = { value: 'the-value' };
+ this.out = { textContent: 'not-the-value' };
+
+ this.bindInOut = new BindInOut(this.in, this.out);
+
+ this.updateOut = this.bindInOut.updateOut();
+ });
+
+ it('should set .out.textContent to .in.value', function () {
+ expect(this.out.textContent).toBe(this.in.value);
+ });
+
+ it('should return the instance', function () {
+ expect(this.updateOut).toBe(this.bindInOut);
+ });
+ });
+
+ describe('.removeEvents', function () {
+ beforeEach(function () {
+ this.in = jasmine.createSpyObj('in', ['removeEventListener']);
+ this.updateOut = () => {};
+
+ this.bindInOut = new BindInOut(this.in);
+ this.bindInOut.eventWrapper.updateOut = this.updateOut;
+
+ this.removeEvents = this.bindInOut.removeEvents();
+ });
+
+ it('should call .removeEventListener', function () {
+ expect(this.in.removeEventListener)
+ .toHaveBeenCalledWith(
+ this.bindInOut.eventType,
+ this.updateOut,
+ );
+ });
+
+ it('should return the instance', function () {
+ expect(this.removeEvents).toBe(this.bindInOut);
+ });
+ });
+
+ describe('.initAll', function () {
+ beforeEach(function () {
+ this.ins = [0, 1, 2];
+ this.instances = [];
+
+ spyOn(document, 'querySelectorAll').and.returnValue(this.ins);
+ spyOn(Array.prototype, 'map').and.callThrough();
+ spyOn(BindInOut, 'init');
+
+ this.initAll = BindInOut.initAll();
+ });
+
+ ClassSpecHelper.itShouldBeAStaticMethod(BindInOut, 'initAll');
+
+ it('should call .querySelectorAll', function () {
+ expect(document.querySelectorAll).toHaveBeenCalledWith('*[data-bind-in]');
+ });
+
+ it('should call .map', function () {
+ expect(Array.prototype.map).toHaveBeenCalledWith(jasmine.any(Function));
+ });
+
+ it('should call .init for each element', function () {
+ expect(BindInOut.init.calls.count()).toEqual(3);
+ });
+
+ it('should return an array of instances', function () {
+ expect(this.initAll).toEqual(jasmine.any(Array));
+ });
+ });
+
+ describe('.init', function () {
+ beforeEach(function () {
+ spyOn(BindInOut.prototype, 'addEvents').and.callFake(function () { return this; });
+ spyOn(BindInOut.prototype, 'updateOut').and.callFake(function () { return this; });
+
+ this.init = BindInOut.init({}, {});
+ });
+
+ ClassSpecHelper.itShouldBeAStaticMethod(BindInOut, 'init');
+
+ it('should call .addEvents', function () {
+ expect(BindInOut.prototype.addEvents).toHaveBeenCalled();
+ });
+
+ it('should call .updateOut', function () {
+ expect(BindInOut.prototype.updateOut).toHaveBeenCalled();
+ });
+
+ describe('if no anOut is provided', function () {
+ beforeEach(function () {
+ this.anIn = { dataset: { bindIn: 'the-data-bind-in' } };
+
+ spyOn(document, 'querySelector');
+
+ BindInOut.init(this.anIn);
+ });
+
+ it('should call .querySelector', function () {
+ expect(document.querySelector)
+ .toHaveBeenCalledWith(`*[data-bind-out="${this.anIn.dataset.bindIn}"]`);
+ });
+ });
+ });
+});
diff --git a/spec/models/chat_team_spec.rb b/spec/models/chat_team_spec.rb
new file mode 100644
index 00000000000..1aab161ec13
--- /dev/null
+++ b/spec/models/chat_team_spec.rb
@@ -0,0 +1,10 @@
+require 'spec_helper'
+
+describe ChatTeam, type: :model do
+ # Associations
+ it { is_expected.to belong_to(:namespace) }
+
+ # Fields
+ it { is_expected.to respond_to(:name) }
+ it { is_expected.to respond_to(:team_id) }
+end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index eac3d962dd1..5d87938235a 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -14,6 +14,7 @@ describe Group, models: true do
it { is_expected.to have_many(:notification_settings).dependent(:destroy) }
it { is_expected.to have_many(:labels).class_name('GroupLabel') }
it { is_expected.to have_many(:uploads).dependent(:destroy) }
+ it { is_expected.to have_one(:chat_team) }
describe '#members & #requesters' do
let(:requester) { create(:user) }
diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb
index 14717a7455d..ec89b540e6a 100644
--- a/spec/services/groups/create_service_spec.rb
+++ b/spec/services/groups/create_service_spec.rb
@@ -4,11 +4,11 @@ describe Groups::CreateService, '#execute', services: true do
let!(:user) { create(:user) }
let!(:group_params) { { path: "group_path", visibility_level: Gitlab::VisibilityLevel::PUBLIC } }
+ subject { service.execute }
+
describe 'visibility level restrictions' do
let!(:service) { described_class.new(user, group_params) }
- subject { service.execute }
-
context "create groups without restricted visibility level" do
it { is_expected.to be_persisted }
end
@@ -24,8 +24,6 @@ describe Groups::CreateService, '#execute', services: true do
let!(:group) { create(:group) }
let!(:service) { described_class.new(user, group_params.merge(parent_id: group.id)) }
- subject { service.execute }
-
context 'as group owner' do
before { group.add_owner(user) }
@@ -40,4 +38,20 @@ describe Groups::CreateService, '#execute', services: true do
end
end
end
+
+ describe 'creating a mattermost team' do
+ let!(:params) { group_params.merge(create_chat_team: "true") }
+ let!(:service) { described_class.new(user, params) }
+
+ before do
+ Settings.mattermost['enabled'] = true
+ end
+
+ it 'create the chat team with the group' do
+ allow_any_instance_of(Mattermost::Team).to receive(:create)
+ .and_return({ 'name' => 'tanuki', 'id' => 'lskdjfwlekfjsdifjj' })
+
+ expect { subject }.to change { ChatTeam.count }.from(0).to(1)
+ end
+ end
end