Commit 24fed567 authored by Rémy Coutable's avatar Rémy Coutable

Merge branch 'and-you-get-awards' into 'master'

And Snippets get awards

## What does this MR do?

Makes snippets more awesome, by making them awardables

## Why was this MR needed?

Because Snippets were left behind.

## What are the relevant issue numbers?

Closes #17878

See merge request !4456
parents ab49c1a3 e41a3912
...@@ -52,6 +52,7 @@ v 8.12.0 (unreleased) ...@@ -52,6 +52,7 @@ v 8.12.0 (unreleased)
- Move parsing of sidekiq ps into helper !6245 (pascalbetz) - Move parsing of sidekiq ps into helper !6245 (pascalbetz)
- Added go to issue boards keyboard shortcut - Added go to issue boards keyboard shortcut
- Expose `sha` and `merge_commit_sha` in merge request API (Ben Boeckel) - Expose `sha` and `merge_commit_sha` in merge request API (Ben Boeckel)
- Emoji can be awarded on Snippets !4456
- Set path for all JavaScript cookies to honor GitLab's subdirectory setting !5627 (Mike Greiling) - Set path for all JavaScript cookies to honor GitLab's subdirectory setting !5627 (Mike Greiling)
- Fix blame table layout width - Fix blame table layout width
- Spec testing if issue authors can read issues on private projects - Spec testing if issue authors can read issues on private projects
......
...@@ -12,11 +12,18 @@ ...@@ -12,11 +12,18 @@
.snippet-file-content { .snippet-file-content {
border-radius: 3px; border-radius: 3px;
margin-bottom: $gl-padding;
.btn-clipboard { .btn-clipboard {
@extend .btn; @extend .btn;
} }
} }
.project-snippets .awards {
border-bottom: 1px solid $table-border-color;
padding-bottom: $gl-padding;
}
.snippet-title { .snippet-title {
font-size: 24px; font-size: 24px;
font-weight: 600; font-weight: 600;
......
...@@ -10,7 +10,9 @@ module ToggleAwardEmoji ...@@ -10,7 +10,9 @@ module ToggleAwardEmoji
if awardable.user_can_award?(current_user, name) if awardable.user_can_award?(current_user, name)
awardable.toggle_award_emoji(name, current_user) awardable.toggle_award_emoji(name, current_user)
TodoService.new.new_award_emoji(to_todoable(awardable), current_user)
todoable = to_todoable(awardable)
TodoService.new.new_award_emoji(todoable, current_user) if todoable
render json: { ok: true } render json: { ok: true }
else else
...@@ -24,8 +26,10 @@ module ToggleAwardEmoji ...@@ -24,8 +26,10 @@ module ToggleAwardEmoji
case awardable case awardable
when Note when Note
awardable.noteable awardable.noteable
else when MergeRequest, Issue
awardable awardable
when Snippet
nil
end end
end end
......
class Projects::SnippetsController < Projects::ApplicationController class Projects::SnippetsController < Projects::ApplicationController
include ToggleAwardEmoji
before_action :module_enabled before_action :module_enabled
before_action :snippet, only: [:show, :edit, :destroy, :update, :raw] before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :toggle_award_emoji]
# Allow read any snippet # Allow read any snippet
before_action :authorize_read_project_snippet!, except: [:new, :create, :index] before_action :authorize_read_project_snippet!, except: [:new, :create, :index]
...@@ -80,6 +82,7 @@ class Projects::SnippetsController < Projects::ApplicationController ...@@ -80,6 +82,7 @@ class Projects::SnippetsController < Projects::ApplicationController
def snippet def snippet
@snippet ||= @project.snippets.find(params[:id]) @snippet ||= @project.snippets.find(params[:id])
end end
alias_method :awardable, :snippet
def authorize_read_project_snippet! def authorize_read_project_snippet!
return render_404 unless can?(current_user, :read_project_snippet, @snippet) return render_404 unless can?(current_user, :read_project_snippet, @snippet)
......
class SnippetsController < ApplicationController class SnippetsController < ApplicationController
include ToggleAwardEmoji
before_action :snippet, only: [:show, :edit, :destroy, :update, :raw] before_action :snippet, only: [:show, :edit, :destroy, :update, :raw]
# Allow read snippet # Allow read snippet
...@@ -85,6 +87,7 @@ class SnippetsController < ApplicationController ...@@ -85,6 +87,7 @@ class SnippetsController < ApplicationController
PersonalSnippet.find(params[:id]) PersonalSnippet.find(params[:id])
end end
end end
alias_method :awardable, :snippet
def authorize_read_snippet! def authorize_read_snippet!
authenticate_user! unless can?(current_user, :read_personal_snippet, @snippet) authenticate_user! unless can?(current_user, :read_personal_snippet, @snippet)
......
module AwardEmojiHelper
def toggle_award_url(awardable)
if @project
url_for([:toggle_award_emoji, @project.namespace.becomes(Namespace), @project, awardable])
else
url_for([:toggle_award_emoji, awardable])
end
end
end
...@@ -106,6 +106,14 @@ module GitlabRoutingHelper ...@@ -106,6 +106,14 @@ module GitlabRoutingHelper
end end
end end
def toggle_award_emoji_personal_snippet_path(*args)
toggle_award_emoji_snippet_path(*args)
end
def toggle_award_emoji_namespace_project_project_snippet_path(*args)
toggle_award_emoji_namespace_project_snippet_path(*args)
end
## Members ## Members
def project_members_url(project, *args) def project_members_url(project, *args)
namespace_project_project_members_url(project.namespace, project) namespace_project_project_members_url(project.namespace, project)
......
...@@ -71,6 +71,12 @@ module Awardable ...@@ -71,6 +71,12 @@ module Awardable
end end
end end
def user_authored?(current_user)
author = self.respond_to?(:author) ? self.author : self.user
author == current_user
end
def awarded_emoji?(emoji_name, current_user) def awarded_emoji?(emoji_name, current_user)
award_emoji.where(name: emoji_name, user: current_user).exists? award_emoji.where(name: emoji_name, user: current_user).exists?
end end
......
...@@ -200,10 +200,6 @@ module Issuable ...@@ -200,10 +200,6 @@ module Issuable
end end
end end
def user_authored?(user)
user == author
end
def subscribed_without_subscriptions?(user) def subscribed_without_subscriptions?(user)
participants(user).include?(user) participants(user).include?(user)
end end
......
...@@ -223,10 +223,6 @@ class Note < ActiveRecord::Base ...@@ -223,10 +223,6 @@ class Note < ActiveRecord::Base
end end
end end
def user_authored?(user)
user == author
end
def award_emoji? def award_emoji?
can_be_award_emoji? && contains_emoji_only? can_be_award_emoji? && contains_emoji_only?
end end
......
...@@ -4,6 +4,7 @@ class Snippet < ActiveRecord::Base ...@@ -4,6 +4,7 @@ class Snippet < ActiveRecord::Base
include Participable include Participable
include Referable include Referable
include Sortable include Sortable
include Awardable
default_value_for :visibility_level, Snippet::PRIVATE default_value_for :visibility_level, Snippet::PRIVATE
......
- grouped_emojis = awardable.grouped_awards(with_thumbs: inline) - grouped_emojis = awardable.grouped_awards(with_thumbs: inline)
.awards.js-awards-block{ class: ("hidden" if !inline && grouped_emojis.empty?), data: { award_url: url_for([:toggle_award_emoji, @project.namespace.becomes(Namespace), @project, awardable]) } } .awards.js-awards-block{ class: ("hidden" if !inline && grouped_emojis.empty?), data: { award_url: toggle_award_url(awardable) } }
- awards_sort(grouped_emojis).each do |emoji, awards| - awards_sort(grouped_emojis).each do |emoji, awards|
%button.btn.award-control.js-emoji-btn.has-tooltip{ type: "button", class: (award_active_class(awards, current_user)), data: { placement: "bottom", title: award_user_list(awards, current_user) } } %button.btn.award-control.js-emoji-btn.has-tooltip{ type: "button", class: (award_active_class(awards, current_user)), data: { placement: "bottom", title: award_user_list(awards, current_user) } }
= emoji_icon(emoji, sprite: false) = emoji_icon(emoji, sprite: false)
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
= link_to new_namespace_project_snippet_path(@project.namespace, @project), class: 'btn btn-grouped btn-create new-snippet-link', title: "New Snippet" do = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: 'btn btn-grouped btn-create new-snippet-link', title: "New Snippet" do
New Snippet New Snippet
- if can?(current_user, :update_project_snippet, @snippet) - if can?(current_user, :update_project_snippet, @snippet)
= link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-warning", title: 'Delete Snippet' do = link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-danger", title: 'Delete Snippet' do
Delete Delete
- if can?(current_user, :update_project_snippet, @snippet) - if can?(current_user, :update_project_snippet, @snippet)
= link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-grouped snippable-edit" do = link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-grouped snippable-edit" do
......
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
= render 'shared/snippets/header' = render 'shared/snippets/header'
%article.file-holder.snippet-file-content .project-snippets
%article.file-holder.snippet-file-content
.file-title .file-title
= blob_icon 0, @snippet.file_name = blob_icon 0, @snippet.file_name
= @snippet.file_name = @snippet.file_name
...@@ -11,4 +12,6 @@ ...@@ -11,4 +12,6 @@
= link_to 'Raw', raw_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", target: "_blank" = link_to 'Raw', raw_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", target: "_blank"
= render 'shared/snippets/blob' = render 'shared/snippets/blob'
%div#notes= render "projects/notes/notes_with_form" = render 'award_emoji/awards_block', awardable: @snippet, inline: true
%div#notes= render "projects/notes/notes_with_form"
...@@ -10,3 +10,5 @@ ...@@ -10,3 +10,5 @@
= clipboard_button(clipboard_target: ".blob-content[data-blob-id='#{@snippet.id}']") = clipboard_button(clipboard_target: ".blob-content[data-blob-id='#{@snippet.id}']")
= link_to 'Raw', raw_snippet_path(@snippet), class: "btn btn-sm", target: "_blank" = link_to 'Raw', raw_snippet_path(@snippet), class: "btn btn-sm", target: "_blank"
= render 'shared/snippets/blob' = render 'shared/snippets/blob'
= render 'award_emoji/awards_block', awardable: @snippet, inline: true
\ No newline at end of file
...@@ -35,6 +35,10 @@ Rails.application.routes.draw do ...@@ -35,6 +35,10 @@ Rails.application.routes.draw do
post :approve_access_request, on: :member post :approve_access_request, on: :member
end end
concern :awardable do
post :toggle_award_emoji, on: :member
end
namespace :ci do namespace :ci do
# CI API # CI API
Ci::API::API.logger Rails.logger Ci::API::API.logger Rails.logger
...@@ -98,7 +102,7 @@ Rails.application.routes.draw do ...@@ -98,7 +102,7 @@ Rails.application.routes.draw do
# #
# Global snippets # Global snippets
# #
resources :snippets do resources :snippets, concerns: :awardable do
member do member do
get 'raw' get 'raw'
end end
...@@ -110,7 +114,6 @@ Rails.application.routes.draw do ...@@ -110,7 +114,6 @@ Rails.application.routes.draw do
# #
# Invites # Invites
# #
resources :invites, only: [:show], constraints: { id: /[A-Za-z0-9_-]+/ } do resources :invites, only: [:show], constraints: { id: /[A-Za-z0-9_-]+/ } do
member do member do
post :accept post :accept
...@@ -662,7 +665,7 @@ Rails.application.routes.draw do ...@@ -662,7 +665,7 @@ Rails.application.routes.draw do
end end
end end
resources :snippets, constraints: { id: /\d+/ } do resources :snippets, concerns: :awardable, constraints: { id: /\d+/ } do
member do member do
get 'raw' get 'raw'
end end
...@@ -724,7 +727,7 @@ Rails.application.routes.draw do ...@@ -724,7 +727,7 @@ Rails.application.routes.draw do
end end
end end
resources :merge_requests, constraints: { id: /\d+/ } do resources :merge_requests, concerns: :awardable, constraints: { id: /\d+/ } do
member do member do
get :commits get :commits
get :diffs get :diffs
...@@ -736,7 +739,6 @@ Rails.application.routes.draw do ...@@ -736,7 +739,6 @@ Rails.application.routes.draw do
post :cancel_merge_when_build_succeeds post :cancel_merge_when_build_succeeds
get :ci_status get :ci_status
post :toggle_subscription post :toggle_subscription
post :toggle_award_emoji
post :remove_wip post :remove_wip
get :diff_for_path get :diff_for_path
post :resolve_conflicts post :resolve_conflicts
...@@ -840,10 +842,9 @@ Rails.application.routes.draw do ...@@ -840,10 +842,9 @@ Rails.application.routes.draw do
end end
end end
resources :issues, constraints: { id: /\d+/ } do resources :issues, concerns: :awardable, constraints: { id: /\d+/ } do
member do member do
post :toggle_subscription post :toggle_subscription
post :toggle_award_emoji
post :mark_as_spam post :mark_as_spam
get :referenced_merge_requests get :referenced_merge_requests
get :related_branches get :related_branches
...@@ -871,9 +872,8 @@ Rails.application.routes.draw do ...@@ -871,9 +872,8 @@ Rails.application.routes.draw do
resources :group_links, only: [:index, :create, :destroy], constraints: { id: /\d+/ } resources :group_links, only: [:index, :create, :destroy], constraints: { id: /\d+/ }
resources :notes, only: [:index, :create, :destroy, :update], constraints: { id: /\d+/ } do resources :notes, only: [:index, :create, :destroy, :update], concerns: :awardable, constraints: { id: /\d+/ } do
member do member do
post :toggle_award_emoji
delete :delete_attachment delete :delete_attachment
post :resolve post :resolve
delete :resolve, action: :unresolve delete :resolve, action: :unresolve
......
# Award Emoji # Award Emoji
> [Introduced][ce-4575] in GitLab 8.9. > [Introduced][ce-4575] in GitLab 8.9, Snippet support in 8.12
An awarded emoji tells a thousand words, and can be awarded on issues, merge An awarded emoji tells a thousand words, and can be awarded on issues, merge
requests and notes/comments. Issues, merge requests and notes are further called requests, snippets, and notes/comments. Issues, merge requests, snippets, and notes are further called
`awardables`. `awardables`.
## Issues and merge requests ## Issues, merge requests, and snippets
### List an awardable's award emoji ### List an awardable's award emoji
...@@ -15,6 +16,7 @@ Gets a list of all award emoji ...@@ -15,6 +16,7 @@ Gets a list of all award emoji
``` ```
GET /projects/:id/issues/:issue_id/award_emoji GET /projects/:id/issues/:issue_id/award_emoji
GET /projects/:id/merge_requests/:merge_request_id/award_emoji GET /projects/:id/merge_requests/:merge_request_id/award_emoji
GET /projects/:id/snippets/:snippet_id/award_emoji
``` ```
Parameters: Parameters:
...@@ -69,11 +71,12 @@ Example Response: ...@@ -69,11 +71,12 @@ Example Response:
### Get single award emoji ### Get single award emoji
Gets a single award emoji from an issue or merge request. Gets a single award emoji from an issue, snippet, or merge request.
``` ```
GET /projects/:id/issues/:issue_id/award_emoji/:award_id GET /projects/:id/issues/:issue_id/award_emoji/:award_id
GET /projects/:id/merge_requests/:merge_request_id/award_emoji/:award_id GET /projects/:id/merge_requests/:merge_request_id/award_emoji/:award_id
GET /projects/:id/snippets/:snippet_id/award_emoji/:award_id
``` ```
Parameters: Parameters:
...@@ -116,6 +119,7 @@ This end point creates an award emoji on the specified resource ...@@ -116,6 +119,7 @@ This end point creates an award emoji on the specified resource
``` ```
POST /projects/:id/issues/:issue_id/award_emoji POST /projects/:id/issues/:issue_id/award_emoji
POST /projects/:id/merge_requests/:merge_request_id/award_emoji POST /projects/:id/merge_requests/:merge_request_id/award_emoji
POST /projects/:id/snippets/:snippet_id/award_emoji
``` ```
Parameters: Parameters:
...@@ -159,6 +163,7 @@ admins or the author of the award. Status code 200 on success, 401 if unauthoriz ...@@ -159,6 +163,7 @@ admins or the author of the award. Status code 200 on success, 401 if unauthoriz
``` ```
DELETE /projects/:id/issues/:issue_id/award_emoji/:award_id DELETE /projects/:id/issues/:issue_id/award_emoji/:award_id
DELETE /projects/:id/merge_requests/:merge_request_id/award_emoji/:award_id DELETE /projects/:id/merge_requests/:merge_request_id/award_emoji/:award_id
DELETE /projects/:id/snippets/:snippet_id/award_emoji/:award_id
``` ```
Parameters: Parameters:
...@@ -197,7 +202,7 @@ Example Response: ...@@ -197,7 +202,7 @@ Example Response:
## Award Emoji on Notes ## Award Emoji on Notes
The endpoints documented above are available for Notes as well. Notes The endpoints documented above are available for Notes as well. Notes
are a sub-resource of Issues and Merge Requests. The examples below are a sub-resource of Issues, Merge Requests, or Snippets. The examples below
describe working with Award Emoji on notes for an Issue, but can be describe working with Award Emoji on notes for an Issue, but can be
easily adapted for notes on a Merge Request. easily adapted for notes on a Merge Request.
......
module API module API
class AwardEmoji < Grape::API class AwardEmoji < Grape::API
before { authenticate! } before { authenticate! }
AWARDABLES = [Issue, MergeRequest] AWARDABLES = %w[issue merge_request snippet]
resource :projects do resource :projects do
AWARDABLES.each do |awardable_type| AWARDABLES.each do |awardable_type|
awardable_string = awardable_type.to_s.underscore.pluralize awardable_string = awardable_type.pluralize
awardable_id_string = "#{awardable_type.to_s.underscore}_id" awardable_id_string = "#{awardable_type}_id"
[ ":id/#{awardable_string}/:#{awardable_id_string}/award_emoji", [ ":id/#{awardable_string}/:#{awardable_id_string}/award_emoji",
":id/#{awardable_string}/:#{awardable_id_string}/notes/:note_id/award_emoji" ":id/#{awardable_string}/:#{awardable_id_string}/notes/:note_id/award_emoji"
...@@ -87,9 +87,7 @@ module API ...@@ -87,9 +87,7 @@ module API
helpers do helpers do
def can_read_awardable? def can_read_awardable?
ability = "read_#{awardable.class.to_s.underscore}".to_sym can?(current_user, read_ability(awardable), awardable)
can?(current_user, ability, awardable)
end end
def can_award_awardable? def can_award_awardable?
...@@ -100,18 +98,25 @@ module API ...@@ -100,18 +98,25 @@ module API
@awardable ||= @awardable ||=
begin begin
if params.include?(:note_id) if params.include?(:note_id)
noteable.notes.find(params[:note_id]) note_id = params.delete(:note_id)
awardable.notes.find(note_id)
elsif params.include?(:issue_id)
user_project.issues.find(params[:issue_id])
elsif params.include?(:merge_request_id)
user_project.merge_requests.find(params[:merge_request_id])
else else
noteable user_project.snippets.find(params[:snippet_id])
end end
end end
end end
def noteable def read_ability(awardable)
if params.include?(:issue_id) case awardable
user_project.issues.find(params[:issue_id]) when Note
read_ability(awardable.noteable)
else else
user_project.merge_requests.find(params[:merge_request_id]) :"read_#{awardable.class.to_s.underscore}"
end end
end end
end end
......
require 'spec_helper' require 'spec_helper'
describe SnippetsController do describe SnippetsController do
describe 'GET #show' do
let(:user) { create(:user) } let(:user) { create(:user) }
describe 'GET #show' do
context 'when the personal snippet is private' do context 'when the personal snippet is private' do
let(:personal_snippet) { create(:personal_snippet, :private, author: user) } let(:personal_snippet) { create(:personal_snippet, :private, author: user) }
...@@ -230,4 +230,33 @@ describe SnippetsController do ...@@ -230,4 +230,33 @@ describe SnippetsController do
end end
end end
end end
context 'award emoji on snippets' do
let(:personal_snippet) { create(:personal_snippet, :public, author: user) }
let(:another_user) { create(:user) }
before do
sign_in(another_user)
end
describe 'POST #toggle_award_emoji' do
it "toggles the award emoji" do
expect do
post(:toggle_award_emoji, id: personal_snippet.to_param, name: "thumbsup")
end.to change { personal_snippet.award_emoji.count }.from(0).to(1)
expect(response.status).to eq(200)
end
it "removes the already awarded emoji" do
post(:toggle_award_emoji, id: personal_snippet.to_param, name: "thumbsup")
expect do
post(:toggle_award_emoji, id: personal_snippet.to_param, name: "thumbsup")
end.to change { personal_snippet.award_emoji.count }.from(1).to(0)
expect(response.status).to eq(200)
end
end
end
end end
...@@ -9,12 +9,14 @@ describe Snippet, models: true do ...@@ -9,12 +9,14 @@ describe Snippet, models: true do
it { is_expected.to include_module(Participable) } it { is_expected.to include_module(Participable) }
it { is_expected.to include_module(Referable) } it { is_expected.to include_module(Referable) }
it { is_expected.to include_module(Sortable) } it { is_expected.to include_module(Sortable) }
it { is_expected.to include_module(Awardable) }
end end
describe 'associations' do describe 'associations' do
it { is_expected.to belong_to(:author).class_name('User') } it { is_expected.to belong_to(:author).class_name('User') }
it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:project) }
it { is_expected.to have_many(:notes).dependent(:destroy) } it { is_expected.to have_many(:notes).dependent(:destroy) }
it { is_expected.to have_many(:award_emoji).dependent(:destroy) }
end end
describe 'validation' do describe 'validation' do
......
...@@ -3,7 +3,7 @@ require 'spec_helper' ...@@ -3,7 +3,7 @@ require 'spec_helper'
describe API::API, api: true do describe API::API, api: true do
include ApiHelpers include ApiHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
let!(:project) { create(:project) } let!(:project) { create(:empty_project) }
let(:issue) { create(:issue, project: project) } let(:issue) { create(:issue, project: project) }
let!(:award_emoji) { create(:award_emoji, awardable: issue, user: user) } let!(:award_emoji) { create(:award_emoji, awardable: issue, user: user) }
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) } let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
...@@ -39,6 +39,19 @@ describe API::API, api: true do ...@@ -39,6 +39,19 @@ describe API::API, api: true do
end end
end end
context 'on a snippet' do
let(:snippet) { create(:project_snippet, :public, project: project) }
let!(:award) { create(:award_emoji, awardable: snippet) }
it 'returns the awarded emoji' do
get api("/projects/#{project.id}/snippets/#{snippet.id}/award_emoji", user)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.first['name']).to eq(award.name)
end
end
context 'when the user has no access' do context 'when the user has no access' do
it 'returns a status code 404' do it 'returns a status code 404' do
user1 = create(:user) user1 = create(:user)
...@@ -91,6 +104,20 @@ describe API::API, api: true do ...@@ -91,6 +104,20 @@ describe API::API, api: true do
end end
end end
context 'on a snippet' do
let(:snippet) { create(:project_snippet, :public, project: project) }
let!(:award) { create(:award_emoji, awardable: snippet) }
it 'returns the awarded emoji' do
get api("/projects/#{project.id}/snippets/#{snippet.id}/award_emoji/#{award.id}", user)
expect(response).to have_http_status(200)
expect(json_response['name']).to eq(award.name)
expect(json_response['awardable_id']).to eq(snippet.id)
expect(json_response['awardable_type']).to eq("Snippet")
end
end
context 'when the user has no access' do context 'when the user has no access' do
it 'returns a status code 404' do it 'returns a status code 404' do
user1 = create(:user) user1 = create(:user)
...@@ -160,6 +187,18 @@ describe API::API, api: true do ...@@ -160,6 +187,18 @@ describe API::API, api: true do
end end
end end
end end
context 'on a snippet' do
it 'creates a new award emoji' do
snippet = create(:project_snippet, :public, project: project)
post api("/projects/#{project.id}/snippets/#{snippet.id}/award_emoji", user), name: 'blowfish'
expect(response).to have_http_status(201)
expect(json_response['name']).to eq('blowfish')
expect(json_response['user']['username']).to eq(user.username)
end
end
end end
describe "POST /projects/:id/awardable/:awardable_id/notes/:note_id/award_emoji" do describe "POST /projects/:id/awardable/:awardable_id/notes/:note_id/award_emoji" do
...@@ -229,6 +268,19 @@ describe API::API, api: true do ...@@ -229,6 +268,19 @@ describe API::API, api: true do
expect(response).to have_http_status(404) expect(response).to have_http_status(404)
end end
end end
context 'when the awardable is a Snippet' do
let(:snippet) { create(:project_snippet, :public, project: project) }
let!(:award) { create(:award_emoji, awardable: snippet, user: user) }
it 'deletes the award' do
expect do
delete api("/projects/#{project.id}/snippets/#{snippet.id}/award_emoji/#{award.id}", user)
end.to change { snippet.award_emoji.count }.from(1).to(0)
expect(response).to have_http_status(200)
end
end
end end
describe 'DELETE /projects/:id/awardable/:awardable_id/award_emoji/:award_emoji_id' do describe 'DELETE /projects/:id/awardable/:awardable_id/award_emoji/:award_emoji_id' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment