Merge pull request #108 from basecamp/events-system
Spike events system
This commit is contained in:
@@ -2,6 +2,6 @@ class BoostsController < ApplicationController
|
||||
include BubbleScoped, BucketScoped
|
||||
|
||||
def create
|
||||
@bubble.boosts.create!
|
||||
@bubble.boost!
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,12 +2,7 @@ class CommentsController < ApplicationController
|
||||
include BubbleScoped, BucketScoped
|
||||
|
||||
def create
|
||||
@bubble.comments.create!(comment_params)
|
||||
@bubble.comment! params.dig(:comment, :body).presence
|
||||
redirect_to bucket_bubble_url(@bucket, @bubble)
|
||||
end
|
||||
|
||||
private
|
||||
def comment_params
|
||||
params.require(:comment).permit(:body)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -8,7 +8,7 @@ module BubblesHelper
|
||||
end
|
||||
|
||||
def bubble_size(bubble)
|
||||
activity = bubble.boosts.size + bubble.comments.size
|
||||
activity = bubble.boost_count + bubble.comments.size
|
||||
rank =
|
||||
case activity
|
||||
when 0..5 then "one"
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
module CommentsHelper
|
||||
def render_comments_and_boosts(bubble)
|
||||
combined_collection = combine_and_sort_items(bubble)
|
||||
|
||||
safe_join([
|
||||
render_creator_summary(bubble, combined_collection),
|
||||
render_remaining_items(combined_collection)
|
||||
])
|
||||
end
|
||||
|
||||
private
|
||||
def combine_and_sort_items(bubble)
|
||||
(bubble.comments + bubble.boosts + bubble.assignments).sort_by(&:created_at)
|
||||
end
|
||||
|
||||
def render_creator_summary(bubble, combined_collection)
|
||||
content_tag(:div, class: "comment--upvotes flex-inline flex-wrap align-start gap fill-white border-radius center position-relative") do
|
||||
[
|
||||
creator_info(bubble),
|
||||
initial_assignment_info(combined_collection),
|
||||
render_initial_boosts(combined_collection)
|
||||
].compact.join(", ").html_safe
|
||||
end
|
||||
end
|
||||
|
||||
def creator_info(bubble)
|
||||
"Added by #{bubble.creator.name} #{time_ago_in_words(bubble.created_at)} ago"
|
||||
end
|
||||
|
||||
def initial_assignment_info(combined_collection)
|
||||
initial_assignment = combined_collection.find { |item| item.is_a?(Assignment) }
|
||||
"assigned to #{initial_assignment.assignee.name}" if initial_assignment
|
||||
end
|
||||
|
||||
def render_initial_boosts(combined_collection)
|
||||
grouped_boosts = combined_collection.take_while { |item| item.is_a?(Boost) }
|
||||
return if grouped_boosts.empty?
|
||||
|
||||
user_boosts = grouped_boosts.group_by(&:creator).transform_values(&:count)
|
||||
boost_summaries = user_boosts.map { |user, count| "#{user.name} +#{count}" }
|
||||
boost_summaries.to_sentence
|
||||
end
|
||||
|
||||
def render_remaining_items(combined_collection)
|
||||
initial_count = combined_collection.take_while { |item| !item.is_a?(Comment) }.count
|
||||
items = combined_collection.drop(initial_count)
|
||||
|
||||
safe_join(items.chunk_while { |i, j| grouped_item?(i) && grouped_item?(j) }.map do |chunk|
|
||||
if chunk.first.is_a?(Comment)
|
||||
render "comments/comment", comment: chunk.first
|
||||
else
|
||||
render_grouped_items(chunk)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
def grouped_item?(item)
|
||||
item.is_a?(Boost) || item.is_a?(Assignment)
|
||||
end
|
||||
|
||||
def render_grouped_items(items)
|
||||
return if items.empty?
|
||||
|
||||
content_tag(:div, class: "comment--upvotes flex-inline flex-wrap align-start gap fill-white border-radius center position-relative") do
|
||||
[
|
||||
render_grouped_boosts(items.select { |item| item.is_a?(Boost) }),
|
||||
render_grouped_assignments(items.select { |item| item.is_a?(Assignment) })
|
||||
].flatten.compact.to_sentence.html_safe
|
||||
end
|
||||
end
|
||||
|
||||
def render_grouped_boosts(boosts)
|
||||
return if boosts.empty?
|
||||
boosts.group_by(&:creator).map { |user, user_boosts| "#{user.name} +#{user_boosts.count}" }
|
||||
end
|
||||
|
||||
def render_grouped_assignments(assignments)
|
||||
assignments.map { |assignment| "Assigned to #{assignment.assignee.name} #{time_ago_in_words(assignment.created_at)} ago" }
|
||||
end
|
||||
end
|
||||
@@ -1,4 +0,0 @@
|
||||
class Boost < ApplicationRecord
|
||||
belongs_to :bubble
|
||||
belongs_to :creator, class_name: "User", default: -> { Current.user }
|
||||
end
|
||||
@@ -1,12 +1,9 @@
|
||||
class Bubble < ApplicationRecord
|
||||
include Assignable, Colored, Poppable, Searchable, Taggable
|
||||
include Assignable, Boostable, Colored, Commentable, Eventable, Poppable, Searchable, Taggable, Threaded
|
||||
|
||||
belongs_to :bucket
|
||||
belongs_to :creator, class_name: "User", default: -> { Current.user }
|
||||
|
||||
has_many :comments, dependent: :destroy
|
||||
has_many :boosts, dependent: :destroy
|
||||
|
||||
has_one_attached :image, dependent: :purge_later
|
||||
|
||||
searchable_by :title, using: :bubbles_search_index
|
||||
@@ -14,7 +11,7 @@ class Bubble < ApplicationRecord
|
||||
before_save :set_default_title
|
||||
|
||||
scope :reverse_chronologically, -> { order(created_at: :desc, id: :desc) }
|
||||
scope :ordered_by_activity, -> { left_joins(:comments, :boosts).group(:id).order(Arel.sql("COUNT(comments.id) + COUNT(boosts.id) DESC")) }
|
||||
scope :ordered_by_activity, -> { left_joins(:comments).group(:id).order(Arel.sql("COUNT(comments.id) + boost_count DESC")) }
|
||||
|
||||
scope :mentioning, ->(query) do
|
||||
bubbles = search(query).select(:id).to_sql
|
||||
|
||||
@@ -2,7 +2,7 @@ module Bubble::Assignable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
has_many :assignments, dependent: :destroy
|
||||
has_many :assignments, dependent: :delete_all
|
||||
|
||||
has_many :assignees, through: :assignments
|
||||
has_many :assigners, through: :assignments
|
||||
@@ -12,8 +12,9 @@ module Bubble::Assignable
|
||||
end
|
||||
|
||||
def assign(users, assigner: Current.user)
|
||||
(Array(users) - assignees).tap do |users|
|
||||
users.each { |user| assignments.create!(assignee: user, assigner: assigner) }
|
||||
transaction do
|
||||
Assignment.insert_all Array(users).collect { |user| { assignee_id: user.id, assigner_id: assigner.id, bubble_id: id } }
|
||||
track_event :assigned, assignee_ids: Array(users).map(&:id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
module Bubble::Boostable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def boost!
|
||||
transaction do
|
||||
increment! :boost_count
|
||||
track_event :boosted
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,11 @@
|
||||
module Bubble::Commentable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
has_many :comments, dependent: :destroy
|
||||
end
|
||||
|
||||
def comment!(body)
|
||||
comments.create! body:
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,14 @@
|
||||
module Bubble::Eventable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
has_many :events, dependent: :delete_all
|
||||
|
||||
after_create -> { track_event :created }
|
||||
end
|
||||
|
||||
private
|
||||
def track_event(action, creator: Current.user, **particulars)
|
||||
events.create! action:, creator:, particulars:
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,35 @@
|
||||
class Bubble::Thread
|
||||
attr_reader :bubble
|
||||
|
||||
def initialize(bubble)
|
||||
@bubble = bubble
|
||||
end
|
||||
|
||||
def entries
|
||||
sorted_entries.chunk_while { |a, b| consecutive_events?(a, b) }.map.with_index { |entries, index| roll_up(entries, index) }
|
||||
end
|
||||
|
||||
def to_partial_path
|
||||
"bubbles/threads/thread"
|
||||
end
|
||||
|
||||
private
|
||||
delegate :events, :comments, to: :bubble, private: true
|
||||
|
||||
def sorted_entries
|
||||
(events + comments).sort_by(&:created_at)
|
||||
end
|
||||
|
||||
def consecutive_events?(a, b)
|
||||
[ a, b ] in [ Event, Event ]
|
||||
end
|
||||
|
||||
def roll_up(entries, position)
|
||||
case entries.first
|
||||
when Comment
|
||||
entries.sole
|
||||
when Event
|
||||
Rollup.new self, entries, first_position: position.zero?
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,55 @@
|
||||
class Bubble::Thread::Rollup
|
||||
def initialize(thread, entries, first_position: false)
|
||||
@thread = thread
|
||||
@entries = entries
|
||||
@first_position = first_position
|
||||
end
|
||||
|
||||
def body
|
||||
collapsed_entries.map { |entry, chunk_size| summarize(entry, chunk_size) }.to_sentence.upcase_first
|
||||
end
|
||||
|
||||
def to_partial_path
|
||||
"bubbles/threads/rollup"
|
||||
end
|
||||
|
||||
private
|
||||
attr_reader :thread, :entries, :first_position
|
||||
|
||||
delegate :time_ago_in_words, to: "ApplicationController.helpers", private: true
|
||||
|
||||
def collapsed_entries
|
||||
sorted_entries.chunk_while { |a, b| repeated_boosts?(a, b) }.map { |chunk| [ chunk.last, chunk.size ] }
|
||||
end
|
||||
|
||||
def sorted_entries
|
||||
entries.sort_by do |entry|
|
||||
case entry.action
|
||||
when "created" then [ 1, entry.created_at ]
|
||||
when "assigned" then [ 2, entry.created_at ]
|
||||
when "boosted" then [ 3, entry.creator, entry.created_at ]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def repeated_boosts?(a, b)
|
||||
a.action == "boosted" && a.slice(:action, :creator_id) == b.slice(:action, :creator_id)
|
||||
end
|
||||
|
||||
def summarize(entry, chunk_size)
|
||||
case entry.action
|
||||
when "created"
|
||||
"added by #{entry.creator.name} #{time_ago_in_words(entry.created_at)} ago"
|
||||
when "assigned"
|
||||
summary = "assigned to #{entry.assignee_names.to_sentence}"
|
||||
summary += " #{time_ago_in_words(entry.created_at)} ago" unless first_position?
|
||||
summary
|
||||
when "boosted"
|
||||
"#{entry.creator.name} +#{chunk_size}"
|
||||
end
|
||||
end
|
||||
|
||||
def first_position?
|
||||
first_position
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,5 @@
|
||||
module Bubble::Threaded
|
||||
def thread
|
||||
Bubble::Thread.new self
|
||||
end
|
||||
end
|
||||
@@ -1,6 +1,10 @@
|
||||
class Current < ActiveSupport::CurrentAttributes
|
||||
attribute :session
|
||||
attribute :session, :user
|
||||
|
||||
delegate :user, to: :session, allow_nil: true
|
||||
delegate :account, to: :user, allow_nil: true
|
||||
|
||||
def session=(session)
|
||||
super
|
||||
self.user = session.user
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
class Event < ApplicationRecord
|
||||
THREADABLE_ACTIONS = %w[ assigned boosted created ]
|
||||
|
||||
include Assignments
|
||||
|
||||
belongs_to :creator, class_name: "User"
|
||||
belongs_to :bubble
|
||||
|
||||
scope :threadable, -> { where action: THREADABLE_ACTIONS }
|
||||
end
|
||||
@@ -0,0 +1,16 @@
|
||||
module Event::Assignments
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
store_accessor :particulars, :assignee_ids
|
||||
end
|
||||
|
||||
def assignee_names
|
||||
assignees.map &:name
|
||||
end
|
||||
|
||||
private
|
||||
def assignees
|
||||
@assignees ||= creator.account.users.find assignee_ids
|
||||
end
|
||||
end
|
||||
@@ -1,6 +1,6 @@
|
||||
<%= button_to bucket_bubble_boosts_path(bubble.bucket, bubble),
|
||||
class: "btn btn--plain",
|
||||
data: { action: "toggle-class#toggle" },
|
||||
form_class: "full-width" do %>
|
||||
+ <%= bubble.boosts.size if bubble.boosts.any? %>
|
||||
class: "btn btn--plain",
|
||||
data: { action: "toggle-class#toggle" },
|
||||
form_class: "full-width" do %>
|
||||
+ <%= bubble.boost_count if bubble.boost_count.positive? %>
|
||||
<% end %>
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
<%= turbo_stream.update dom_id(@bubble, :boosts) do %>
|
||||
<%= render "boosts/boosts", bubble: @bubble %>
|
||||
<% end %>
|
||||
|
||||
<%= turbo_stream.replace dom_id(@bubble, :thread) do %>
|
||||
<%= render @bubble.thread %>
|
||||
<% end %>
|
||||
|
||||
@@ -25,7 +25,4 @@
|
||||
<%= render "bubbles/bubble", bubble: @bubble %>
|
||||
</div>
|
||||
|
||||
<section class="comments align-center center borderless margin flex flex-column gap-half" style="--bubble-color: <%= @bubble.color %>;">
|
||||
<%= render_comments_and_boosts(@bubble) %>
|
||||
<%= render "comments/new", bubble: @bubble %>
|
||||
</section>
|
||||
<%= render @bubble.thread %>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<div class="comment--upvotes flex-inline flex-wrap align-start fill-white border-radius center position-relative">
|
||||
<%= rollup.body %>
|
||||
</div>
|
||||
@@ -0,0 +1,6 @@
|
||||
<% bubble = thread.bubble %>
|
||||
|
||||
<section id="<%= dom_id(bubble, :thread) %>" class="comments align-center center borderless margin flex flex-column gap-half" style="--bubble-color: <%= bubble.color %>;">
|
||||
<%= render thread.entries %>
|
||||
<%= render "comments/new", bubble: bubble %>
|
||||
</section>
|
||||
@@ -20,7 +20,7 @@
|
||||
<%= link_to bucket_bubbles_path(bucket), class: "windshield__container flex justify-center align-center position-relative" do %>
|
||||
<div class="windshield bucket__windshield flex flex-wrap gap justify-center align-end" style="view-transition-name: windshield_<%= bucket.id %>">
|
||||
|
||||
<% bucket.bubbles.not_popped.left_joins(:comments, :boosts).group(:id).order(Arel.sql("COUNT(comments.id) + COUNT(boosts.id) DESC")).limit(10).each do |bubble| %>
|
||||
<% bucket.bubbles.not_popped.ordered_by_activity.limit(10).each do |bubble| %>
|
||||
<div class="bubble" style="--bubble-color: <%= bubble.color %>; <%= bubble_rotation(bubble) %> <%= bubble_size(bubble) %>">
|
||||
<svg class="bubble__svg" style="fill: <%= bubble.color %>; stroke: <%= bubble.color %>;" viewBox="0 0 990 990" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m0 0h990v990h-990z" fill="none" stroke="none" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="comment flex align-start full-width <%= "comment--mine" if comment.creator == Current.user %>" id="<%= dom_id(comment) %>">
|
||||
<%= tag.div id: dom_id(comment), class: [ "comment flex align-start full-width", { "comment--mine": Current.user == comment.creator } ] do %>
|
||||
<figure class="comment__avatar flex-item-no-shrink">
|
||||
<%= avatar_tag comment.creator, loading: :lazy %>
|
||||
</figure>
|
||||
@@ -16,4 +16,4 @@
|
||||
<%= simple_format comment.body %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
+35
-1
@@ -22,8 +22,42 @@
|
||||
89
|
||||
],
|
||||
"note": ""
|
||||
},
|
||||
{
|
||||
"warning_type": "Dynamic Render Path",
|
||||
"warning_code": 15,
|
||||
"fingerprint": "344f91887bfa44bd0a64504b78280b988ab09d000518dafc8169215cdbc2b7b2",
|
||||
"check_name": "Render",
|
||||
"message": "Render path contains parameter value",
|
||||
"file": "app/views/bubbles/show.html.erb",
|
||||
"line": 28,
|
||||
"link": "https://brakemanscanner.org/docs/warning_types/dynamic_render_path/",
|
||||
"code": "render(action => @bucket.bubbles.find(params[:id]).thread, {})",
|
||||
"render_path": [
|
||||
{
|
||||
"type": "controller",
|
||||
"class": "BubblesController",
|
||||
"method": "show",
|
||||
"line": 35,
|
||||
"file": "app/controllers/bubbles_controller.rb",
|
||||
"rendered": {
|
||||
"name": "bubbles/show",
|
||||
"file": "app/views/bubbles/show.html.erb"
|
||||
}
|
||||
}
|
||||
],
|
||||
"location": {
|
||||
"type": "template",
|
||||
"template": "bubbles/show"
|
||||
},
|
||||
"user_input": "params[:id]",
|
||||
"confidence": "Weak",
|
||||
"cwe_id": [
|
||||
22
|
||||
],
|
||||
"note": ""
|
||||
}
|
||||
],
|
||||
"updated": "2024-10-03 15:28:53 -0400",
|
||||
"updated": "2024-10-11 20:28:34 -0500",
|
||||
"brakeman_version": "6.2.1"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
class AddUniqueIndexToAssignments < ActiveRecord::Migration[8.0]
|
||||
def change
|
||||
add_index :assignments, %i[ assignee_id bubble_id ], unique: true
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,5 @@
|
||||
class MakeCommentBodiesNotNull < ActiveRecord::Migration[8.0]
|
||||
def change
|
||||
change_column_null :comments, :body, false
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,14 @@
|
||||
class CreateEvents < ActiveRecord::Migration[8.0]
|
||||
def change
|
||||
create_table :events do |t|
|
||||
t.references :bubble, null: false, index: false
|
||||
t.references :creator, null: false
|
||||
t.json :particulars, default: {}
|
||||
t.string :action, null: false
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
add_index :events, %i[ bubble_id action ], name: "index_events_on_bubble_id_and_action"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,5 @@
|
||||
class DropBoosts < ActiveRecord::Migration[8.0]
|
||||
def change
|
||||
drop_table :boosts
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,5 @@
|
||||
class AddBoostCountToBubbles < ActiveRecord::Migration[8.0]
|
||||
def change
|
||||
add_column :bubbles, :boost_count, :integer, null: false, default: 0
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,5 @@
|
||||
class RemoveAssignmentsIndexOnAssigneeId < ActiveRecord::Migration[8.0]
|
||||
def change
|
||||
remove_index :assignments, :assignee_id
|
||||
end
|
||||
end
|
||||
Generated
+15
-11
@@ -10,7 +10,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[8.0].define(version: 2024_10_02_163242) do
|
||||
ActiveRecord::Schema[8.0].define(version: 2024_10_09_211414) do
|
||||
create_table "accesses", force: :cascade do |t|
|
||||
t.integer "bucket_id", null: false
|
||||
t.integer "user_id", null: false
|
||||
@@ -64,18 +64,10 @@ ActiveRecord::Schema[8.0].define(version: 2024_10_02_163242) do
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.integer "assigner_id", null: false
|
||||
t.index ["assignee_id"], name: "index_assignments_on_assignee_id"
|
||||
t.index ["assignee_id", "bubble_id"], name: "index_assignments_on_assignee_id_and_bubble_id", unique: true
|
||||
t.index ["bubble_id"], name: "index_assignments_on_bubble_id"
|
||||
end
|
||||
|
||||
create_table "boosts", force: :cascade do |t|
|
||||
t.integer "creator_id", null: false
|
||||
t.integer "bubble_id", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["bubble_id"], name: "index_boosts_on_bubble_id"
|
||||
end
|
||||
|
||||
create_table "bubbles", force: :cascade do |t|
|
||||
t.string "title"
|
||||
t.string "color"
|
||||
@@ -84,6 +76,7 @@ ActiveRecord::Schema[8.0].define(version: 2024_10_02_163242) do
|
||||
t.integer "creator_id", null: false
|
||||
t.date "due_on"
|
||||
t.integer "bucket_id", null: false
|
||||
t.integer "boost_count", default: 0, null: false
|
||||
t.index ["bucket_id"], name: "index_bubbles_on_bucket_id"
|
||||
end
|
||||
|
||||
@@ -98,13 +91,24 @@ ActiveRecord::Schema[8.0].define(version: 2024_10_02_163242) do
|
||||
end
|
||||
|
||||
create_table "comments", force: :cascade do |t|
|
||||
t.text "body"
|
||||
t.text "body", null: false
|
||||
t.integer "creator_id", null: false
|
||||
t.integer "bubble_id", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
end
|
||||
|
||||
create_table "events", force: :cascade do |t|
|
||||
t.integer "bubble_id", null: false
|
||||
t.integer "creator_id", null: false
|
||||
t.json "particulars", default: {}
|
||||
t.string "action", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["bubble_id", "action"], name: "index_events_on_bubble_id_and_action"
|
||||
t.index ["creator_id"], name: "index_events_on_creator_id"
|
||||
end
|
||||
|
||||
create_table "pops", force: :cascade do |t|
|
||||
t.integer "bubble_id", null: false
|
||||
t.integer "user_id"
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
require "test_helper"
|
||||
|
||||
class BoostsControllerTest < ActionDispatch::IntegrationTest
|
||||
# test "the truth" do
|
||||
# assert true
|
||||
# end
|
||||
setup do
|
||||
sign_in_as :kevin
|
||||
end
|
||||
|
||||
test "create" do
|
||||
assert_difference "bubbles(:logo).reload.boost_count", +1 do
|
||||
post bucket_bubble_boosts_url(buckets(:writebook), bubbles(:logo), format: :turbo_stream)
|
||||
end
|
||||
|
||||
assert_turbo_stream action: :update, target: dom_id(bubbles(:logo), :boosts)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
require "test_helper"
|
||||
|
||||
class CommentsControllerTest < ActionDispatch::IntegrationTest
|
||||
# test "the truth" do
|
||||
# assert true
|
||||
# end
|
||||
setup do
|
||||
sign_in_as :kevin
|
||||
end
|
||||
|
||||
test "create" do
|
||||
assert_difference "bubbles(:logo).comments.count", +1 do
|
||||
post bucket_bubble_comments_url(buckets(:writebook), bubbles(:logo), params: { comment: { body: "Agreed." } })
|
||||
end
|
||||
|
||||
assert_redirected_to bucket_bubble_url(buckets(:writebook), bubbles(:logo))
|
||||
end
|
||||
end
|
||||
|
||||
Vendored
+7
@@ -1,7 +1,14 @@
|
||||
logo_jz:
|
||||
assigner: david
|
||||
assignee: jz
|
||||
bubble: logo
|
||||
created_at: <%= 1.week.ago %>
|
||||
|
||||
logo_kevin:
|
||||
assigner: david
|
||||
assignee: kevin
|
||||
bubble: logo
|
||||
created_at: <%= 1.day.ago %>
|
||||
|
||||
layout_jz:
|
||||
assigner: david
|
||||
|
||||
Vendored
-15
@@ -1,15 +0,0 @@
|
||||
logo_jz_one:
|
||||
bubble: logo
|
||||
creator: jz
|
||||
|
||||
logo_jz_two:
|
||||
bubble: logo
|
||||
creator: jz
|
||||
|
||||
logo_kevin:
|
||||
bubble: logo
|
||||
creator: kevin
|
||||
|
||||
layout_jz:
|
||||
bubble: layout
|
||||
creator: jz
|
||||
Vendored
+5
@@ -4,21 +4,26 @@ logo:
|
||||
title: The logo isn't big enough
|
||||
color: "#ED8008"
|
||||
due_on: <%= 3.days.from_now %>
|
||||
created_at: <%= 1.week.ago %>
|
||||
boost_count: 5
|
||||
|
||||
layout:
|
||||
bucket: writebook
|
||||
creator: david
|
||||
title: Layout is broken
|
||||
color: "#698F9C"
|
||||
created_at: <%= 1.week.ago %>
|
||||
|
||||
text:
|
||||
bucket: writebook
|
||||
creator: kevin
|
||||
title: The text is too small
|
||||
color: "#3B4B59"
|
||||
created_at: <%= 1.week.ago %>
|
||||
|
||||
shipping:
|
||||
bucket: writebook
|
||||
creator: kevin
|
||||
title: We need to ship the app
|
||||
color: "#ED8008"
|
||||
created_at: <%= 1.week.ago %>
|
||||
|
||||
Vendored
+1
@@ -2,6 +2,7 @@ logo_agreement_jz:
|
||||
body: I agree.
|
||||
creator: jz
|
||||
bubble: logo
|
||||
created_at: <%= 2.days.ago %>
|
||||
|
||||
logo_agreement_kevin:
|
||||
body: Same, let’s do it.
|
||||
|
||||
Vendored
+72
@@ -0,0 +1,72 @@
|
||||
logo_created:
|
||||
creator: david
|
||||
bubble: logo
|
||||
action: created
|
||||
created_at: <%= 1.week.ago %>
|
||||
|
||||
logo_assignment_jz:
|
||||
creator: david
|
||||
bubble: logo
|
||||
action: assigned
|
||||
particulars: <%= { assignee_ids: [ ActiveRecord::FixtureSet.identify(:jz) ] }.to_json %>
|
||||
created_at: <%= 1.week.ago %>
|
||||
|
||||
logo_assignment_km:
|
||||
creator: david
|
||||
bubble: logo
|
||||
action: assigned
|
||||
particulars: <%= { assignee_ids: [ ActiveRecord::FixtureSet.identify(:kevin) ] }.to_json %>
|
||||
created_at: <%= 1.day.ago %>
|
||||
|
||||
logo_boost_dhh:
|
||||
creator: david
|
||||
bubble: logo
|
||||
action: boosted
|
||||
created_at: <%= 1.week.ago %>
|
||||
|
||||
logo_boost_km1:
|
||||
creator: kevin
|
||||
bubble: logo
|
||||
action: boosted
|
||||
created_at: <%= 1.day.ago %>
|
||||
|
||||
logo_boost_km2:
|
||||
creator: kevin
|
||||
bubble: logo
|
||||
action: boosted
|
||||
created_at: <%= 1.day.ago %>
|
||||
|
||||
logo_boost_jz1:
|
||||
creator: jz
|
||||
bubble: logo
|
||||
action: boosted
|
||||
created_at: <%= 1.day.ago %>
|
||||
|
||||
logo_boost_jz2:
|
||||
creator: jz
|
||||
bubble: logo
|
||||
action: boosted
|
||||
|
||||
layout_created:
|
||||
creator: david
|
||||
bubble: layout
|
||||
action: created
|
||||
created_at: <%= 1.week.ago %>
|
||||
|
||||
layout_assignment_jz:
|
||||
creator: david
|
||||
bubble: layout
|
||||
action: assigned
|
||||
particulars: <%= { assignee_ids: [ ActiveRecord::FixtureSet.identify(:jz) ] }.to_json %>
|
||||
|
||||
text_created:
|
||||
creator: kevin
|
||||
bubble: text
|
||||
action: created
|
||||
created_at: <%= 1.week.ago %>
|
||||
|
||||
shipping_created:
|
||||
creator: kevin
|
||||
bubble: shipping
|
||||
action: created
|
||||
created_at: <%= 1.week.ago %>
|
||||
@@ -1,6 +1,31 @@
|
||||
require "test_helper"
|
||||
|
||||
class BubbleTest < ActiveSupport::TestCase
|
||||
setup do
|
||||
Current.user = users(:kevin)
|
||||
end
|
||||
|
||||
test "boosting" do
|
||||
assert_difference %w[ bubbles(:logo).boost_count bubbles(:logo).events.count ], +1 do
|
||||
bubbles(:logo).boost!
|
||||
end
|
||||
end
|
||||
|
||||
test "commenting" do
|
||||
bubbles(:logo).comment! "Agreed."
|
||||
|
||||
assert_equal "Agreed.", bubbles(:logo).comments.last.body
|
||||
end
|
||||
|
||||
test "assigning" do
|
||||
bubbles(:logo).assign users(:david)
|
||||
|
||||
assert_equal users(:kevin, :jz, :david), bubbles(:logo).assignees
|
||||
assert_equal users(:david, :kevin), bubbles(:logo).assigners.uniq
|
||||
assert_equal [ users(:david).id ], bubbles(:logo).events.last.assignee_ids
|
||||
assert_equal [ "David" ], bubbles(:logo).events.last.assignee_names
|
||||
end
|
||||
|
||||
test "searchable by title" do
|
||||
bubble = buckets(:writebook).bubbles.create! title: "Insufficient haggis", creator: users(:kevin)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user