mirror of
https://github.com/Retrospring/retrospring.git
synced 2025-02-13 21:33:20 +01:00
Merge branch 'main' into bugfix/inbox-sharing
This commit is contained in:
commit
6e8f8bcc67
35 changed files with 299 additions and 204 deletions
|
@ -1,4 +1,4 @@
|
||||||
FROM ruby:3.1
|
FROM ruby:3.2
|
||||||
|
|
||||||
USER root
|
USER root
|
||||||
|
|
||||||
|
|
1
.git-blame-ignore-revs
Normal file
1
.git-blame-ignore-revs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
71894d6c4987547533606258447b576ecb604c2b
|
|
@ -46,7 +46,10 @@ Metrics/ClassLength:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Metrics/CyclomaticComplexity:
|
Metrics/CyclomaticComplexity:
|
||||||
Severity: refactor
|
Enabled: false
|
||||||
|
|
||||||
|
Metrics/PerceivedComplexity:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
Metrics/ModuleLength:
|
Metrics/ModuleLength:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
3.1.2
|
3.2.2
|
||||||
|
|
|
@ -8,8 +8,8 @@ LABEL org.opencontainers.image.vendor="The Retrospring team"
|
||||||
LABEL org.opencontainers.image.url="https://github.com/Retrospring/retrospring"
|
LABEL org.opencontainers.image.url="https://github.com/Retrospring/retrospring"
|
||||||
|
|
||||||
ARG RETROSPRING_VERSION=2023.0131.1
|
ARG RETROSPRING_VERSION=2023.0131.1
|
||||||
ARG RUBY_VERSION=3.1.2
|
ARG RUBY_VERSION=3.2.2
|
||||||
ARG RUBY_INSTALL_VERSION=0.9.0
|
ARG RUBY_INSTALL_VERSION=0.9.2
|
||||||
ARG BUNDLER_VERSION=2.3.18
|
ARG BUNDLER_VERSION=2.3.18
|
||||||
|
|
||||||
ENV RAILS_ENV=production
|
ENV RAILS_ENV=production
|
||||||
|
|
|
@ -470,7 +470,7 @@ GEM
|
||||||
timeout (0.4.0)
|
timeout (0.4.0)
|
||||||
tldv (0.1.0)
|
tldv (0.1.0)
|
||||||
tldv-data (~> 1.0)
|
tldv-data (~> 1.0)
|
||||||
tldv-data (1.0.2023031000)
|
tldv-data (1.0.2023080900)
|
||||||
turbo-rails (1.5.0)
|
turbo-rails (1.5.0)
|
||||||
actionpack (>= 6.0.0)
|
actionpack (>= 6.0.0)
|
||||||
activejob (>= 6.0.0)
|
activejob (>= 6.0.0)
|
||||||
|
@ -582,4 +582,4 @@ DEPENDENCIES
|
||||||
twitter-text
|
twitter-text
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.3.18
|
2.4.21
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module AjaxHelper
|
module AjaxHelper
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,13 +4,13 @@ module ApplicationHelper::GraphMethods
|
||||||
# Creates <meta> tags for OpenGraph properties from a hash
|
# Creates <meta> tags for OpenGraph properties from a hash
|
||||||
# @param values [Hash]
|
# @param values [Hash]
|
||||||
def opengraph_meta_tags(values)
|
def opengraph_meta_tags(values)
|
||||||
safe_join(values.map { |name, content| tag.meta(property: name, content: content) }, "\n")
|
safe_join(values.map { |name, content| tag.meta(property: name, content:) }, "\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
# Creates <meta> tags from a hash
|
# Creates <meta> tags from a hash
|
||||||
# @param values [Hash]
|
# @param values [Hash]
|
||||||
def meta_tags(values)
|
def meta_tags(values)
|
||||||
safe_join(values.map { |name, content| tag.meta(name: name, content: content) }, "\n")
|
safe_join(values.map { |name, content| tag.meta(name:, content:) }, "\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
# @param user [User]
|
# @param user [User]
|
||||||
|
@ -22,7 +22,7 @@ module ApplicationHelper::GraphMethods
|
||||||
"og:url": user_url(user),
|
"og:url": user_url(user),
|
||||||
"og:description": user.profile.description,
|
"og:description": user.profile.description,
|
||||||
"og:site_name": APP_CONFIG["site_name"],
|
"og:site_name": APP_CONFIG["site_name"],
|
||||||
"profile:username": user.screen_name
|
"profile:username": user.screen_name,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ module ApplicationHelper::GraphMethods
|
||||||
"twitter:site": "@retrospring",
|
"twitter:site": "@retrospring",
|
||||||
"twitter:title": user.profile.motivation_header.presence || "Ask me anything!",
|
"twitter:title": user.profile.motivation_header.presence || "Ask me anything!",
|
||||||
"twitter:description": "Ask #{user.profile.safe_name} anything on Retrospring",
|
"twitter:description": "Ask #{user.profile.safe_name} anything on Retrospring",
|
||||||
"twitter:image": full_profile_picture_url(user)
|
"twitter:image": full_profile_picture_url(user),
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ module ApplicationHelper::GraphMethods
|
||||||
"og:image": full_profile_picture_url(answer.user),
|
"og:image": full_profile_picture_url(answer.user),
|
||||||
"og:url": answer_url(answer.user.screen_name, answer.id),
|
"og:url": answer_url(answer.user.screen_name, answer.id),
|
||||||
"og:description": answer.content,
|
"og:description": answer.content,
|
||||||
"og:site_name": APP_CONFIG["site_name"]
|
"og:site_name": APP_CONFIG["site_name"],
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ module ApplicationHelper::TitleMethods
|
||||||
def question_title(question)
|
def question_title(question)
|
||||||
context_user = question.answers&.first&.user if question.direct
|
context_user = question.answers&.first&.user if question.direct
|
||||||
name = user_screen_name question.user,
|
name = user_screen_name question.user,
|
||||||
context_user: context_user,
|
context_user:,
|
||||||
author_identifier: question.author_is_anonymous ? question.author_identifier : nil,
|
author_identifier: question.author_is_anonymous ? question.author_identifier : nil,
|
||||||
url: false
|
url: false
|
||||||
generate_title name, "asked", question.content
|
generate_title name, "asked", question.content
|
||||||
|
|
|
@ -25,24 +25,24 @@ module BootstrapHelper
|
||||||
"#{content_tag(:i, '', class: "fa fa-#{options[:icon]}")} #{body}"
|
"#{content_tag(:i, '', class: "fa fa-#{options[:icon]}")} #{body}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if options[:badge].present? || options.dig(:badge_attr, :data)&.has_key?(:controller)
|
if options[:badge].present? || options.dig(:badge_attr, :data)&.key?(:controller)
|
||||||
badge_class = [
|
badge_class = [
|
||||||
"badge",
|
"badge",
|
||||||
("badge-#{options[:badge_color]}" unless options[:badge_color].nil?),
|
("badge-#{options[:badge_color]}" unless options[:badge_color].nil?),
|
||||||
("badge-pill" if options[:badge_pill])
|
("badge-pill" if options[:badge_pill])
|
||||||
].compact.join(" ")
|
].compact.join(" ")
|
||||||
|
|
||||||
body += " #{content_tag(:span, options[:badge], class: badge_class, **options[:badge_attr])}".html_safe
|
body += " #{content_tag(:span, options[:badge], class: badge_class, **options[:badge_attr])}".html_safe # rubocop:disable Rails/OutputSafety
|
||||||
end
|
end
|
||||||
|
|
||||||
content_tag(:li, link_to(body.html_safe, path, class: "nav-link", data: { hotkey: options[:hotkey] }), class: classes, id: options[:id])
|
content_tag(:li, link_to(body.html_safe, path, class: "nav-link", data: { hotkey: options[:hotkey] }), class: classes, id: options[:id]) # rubocop:disable Rails/OutputSafety
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_group_item(body, path, options = {})
|
def list_group_item(body, path, options = {})
|
||||||
options = {
|
options = {
|
||||||
badge: nil,
|
badge: nil,
|
||||||
badge_color: nil,
|
badge_color: nil,
|
||||||
class: ""
|
class: "",
|
||||||
}.merge(options)
|
}.merge(options)
|
||||||
|
|
||||||
classes = [
|
classes = [
|
||||||
|
@ -54,13 +54,18 @@ module BootstrapHelper
|
||||||
|
|
||||||
unless options[:badge].nil? || (options[:badge]).zero?
|
unless options[:badge].nil? || (options[:badge]).zero?
|
||||||
# TODO: make this prettier?
|
# TODO: make this prettier?
|
||||||
body << " #{
|
badge = content_tag(:span, options[:badge], class: "badge#{
|
||||||
content_tag(:span, options[:badge], class: "badge#{
|
|
||||||
" badge-#{options[:badge_color]}" unless options[:badge_color].nil?
|
" badge-#{options[:badge_color]}" unless options[:badge_color].nil?
|
||||||
}")}"
|
}",)
|
||||||
end
|
end
|
||||||
|
|
||||||
content_tag(:a, body.html_safe, href: path, class: classes)
|
html = if badge
|
||||||
|
"#{body} #{badge}"
|
||||||
|
else
|
||||||
|
body
|
||||||
|
end
|
||||||
|
|
||||||
|
content_tag(:a, html.html_safe, href: path, class: classes) # rubocop:disable Rails/OutputSafety
|
||||||
end
|
end
|
||||||
|
|
||||||
def tooltip(body, tooltip_content, placement = "bottom")
|
def tooltip(body, tooltip_content, placement = "bottom")
|
||||||
|
|
|
@ -8,7 +8,7 @@ module FeedbackHelper
|
||||||
avatarURL: current_user.profile_picture.url(:large),
|
avatarURL: current_user.profile_picture.url(:large),
|
||||||
name: current_user.screen_name,
|
name: current_user.screen_name,
|
||||||
id: current_user.id,
|
id: current_user.id,
|
||||||
email: current_user.email
|
email: current_user.email,
|
||||||
}
|
}
|
||||||
|
|
||||||
JWT.encode(user_data, APP_CONFIG.dig("canny", "sso"))
|
JWT.encode(user_data, APP_CONFIG.dig("canny", "sso"))
|
||||||
|
|
|
@ -30,7 +30,7 @@ module MarkdownHelper
|
||||||
def raw_markdown(content)
|
def raw_markdown(content)
|
||||||
renderer = Redcarpet::Render::HTML.new(**MARKDOWN_RENDERER_OPTS)
|
renderer = Redcarpet::Render::HTML.new(**MARKDOWN_RENDERER_OPTS)
|
||||||
md = Redcarpet::Markdown.new(renderer, **MARKDOWN_OPTS)
|
md = Redcarpet::Markdown.new(renderer, **MARKDOWN_OPTS)
|
||||||
raw md.render content
|
raw md.render content # rubocop:disable Rails/OutputSafety
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_markdown(path, relative_to = Rails.root)
|
def get_markdown(path, relative_to = Rails.root)
|
||||||
|
|
|
@ -15,7 +15,7 @@ module SocialHelper::TelegramMethods
|
||||||
id: answer.id,
|
id: answer.id,
|
||||||
username: answer.user.screen_name,
|
username: answer.user.screen_name,
|
||||||
host: APP_CONFIG["hostname"],
|
host: APP_CONFIG["hostname"],
|
||||||
protocol: (APP_CONFIG["https"] ? :https : :http)
|
protocol: (APP_CONFIG["https"] ? :https : :http),
|
||||||
)
|
)
|
||||||
|
|
||||||
%(https://t.me/share/url?url=#{CGI.escape(url)}&text=#{CGI.escape(telegram_text(answer))})
|
%(https://t.me/share/url?url=#{CGI.escape(url)}&text=#{CGI.escape(telegram_text(answer))})
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
require 'cgi'
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "cgi"
|
||||||
|
|
||||||
module SocialHelper::TumblrMethods
|
module SocialHelper::TumblrMethods
|
||||||
def tumblr_title(answer)
|
def tumblr_title(answer)
|
||||||
|
@ -13,10 +15,10 @@ module SocialHelper::TumblrMethods
|
||||||
|
|
||||||
def tumblr_body(answer)
|
def tumblr_body(answer)
|
||||||
answer_url = answer_url(
|
answer_url = answer_url(
|
||||||
id: answer.id,
|
id: answer.id,
|
||||||
username: answer.user.screen_name,
|
username: answer.user.screen_name,
|
||||||
host: APP_CONFIG['hostname'],
|
host: APP_CONFIG["hostname"],
|
||||||
protocol: (APP_CONFIG['https'] ? :https : :http)
|
protocol: (APP_CONFIG["https"] ? :https : :http),
|
||||||
)
|
)
|
||||||
|
|
||||||
"#{answer.content}\n\n[Smile or comment on the answer here](#{answer_url})"
|
"#{answer.content}\n\n[Smile or comment on the answer here](#{answer_url})"
|
||||||
|
@ -24,10 +26,10 @@ module SocialHelper::TumblrMethods
|
||||||
|
|
||||||
def tumblr_share_url(answer)
|
def tumblr_share_url(answer)
|
||||||
answer_url = answer_url(
|
answer_url = answer_url(
|
||||||
id: answer.id,
|
id: answer.id,
|
||||||
username: answer.user.screen_name,
|
username: answer.user.screen_name,
|
||||||
host: APP_CONFIG['hostname'],
|
host: APP_CONFIG["hostname"],
|
||||||
protocol: (APP_CONFIG['https'] ? :https : :http)
|
protocol: (APP_CONFIG["https"] ? :https : :http),
|
||||||
)
|
)
|
||||||
|
|
||||||
"https://www.tumblr.com/widgets/share/tool?shareSource=legacy&posttype=text&title=#{CGI.escape(tumblr_title(answer))}&url=#{CGI.escape(answer_url)}&caption=&content=#{CGI.escape(tumblr_body(answer))}"
|
"https://www.tumblr.com/widgets/share/tool?shareSource=legacy&posttype=text&title=#{CGI.escape(tumblr_title(answer))}&url=#{CGI.escape(answer_url)}&caption=&content=#{CGI.escape(tumblr_body(answer))}"
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
require 'cgi'
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "cgi"
|
||||||
|
|
||||||
module SocialHelper::TwitterMethods
|
module SocialHelper::TwitterMethods
|
||||||
include MarkdownHelper
|
include MarkdownHelper
|
||||||
|
|
||||||
def prepare_tweet(answer, post_tag = nil, omit_url = false)
|
def prepare_tweet(answer, post_tag = nil, omit_url = false)
|
||||||
question_content = twitter_markdown answer.question.content.gsub(/\@(\w+)/, '\1')
|
question_content = twitter_markdown answer.question.content.gsub(/@(\w+)/, '\1')
|
||||||
original_question_length = question_content.length
|
original_question_length = question_content.length
|
||||||
answer_content = twitter_markdown answer.content
|
answer_content = twitter_markdown answer.content
|
||||||
original_answer_length = answer_content.length
|
original_answer_length = answer_content.length
|
||||||
|
@ -18,7 +20,7 @@ module SocialHelper::TwitterMethods
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
parsed_tweet = { :valid => false }
|
parsed_tweet = { valid: false }
|
||||||
tweet_text = ""
|
tweet_text = ""
|
||||||
|
|
||||||
until parsed_tweet[:valid]
|
until parsed_tweet[:valid]
|
||||||
|
@ -26,14 +28,14 @@ module SocialHelper::TwitterMethods
|
||||||
shortened_answer = "#{answer_content[0..123]}#{'…' if original_answer_length > [124, answer_content.length].min}"
|
shortened_answer = "#{answer_content[0..123]}#{'…' if original_answer_length > [124, answer_content.length].min}"
|
||||||
components = [
|
components = [
|
||||||
shortened_question,
|
shortened_question,
|
||||||
'—',
|
"—",
|
||||||
shortened_answer,
|
shortened_answer,
|
||||||
post_tag,
|
post_tag,
|
||||||
answer_url
|
answer_url
|
||||||
]
|
]
|
||||||
tweet_text = components.compact.join(' ')
|
tweet_text = components.compact.join(" ")
|
||||||
|
|
||||||
parsed_tweet = Twitter::TwitterText::Validation::parse_tweet(tweet_text)
|
parsed_tweet = Twitter::TwitterText::Validation.parse_tweet(tweet_text)
|
||||||
|
|
||||||
question_content = question_content[0..-2]
|
question_content = question_content[0..-2]
|
||||||
answer_content = answer_content[0..-2]
|
answer_content = answer_content[0..-2]
|
||||||
|
|
|
@ -25,7 +25,7 @@ module ThemeHelper
|
||||||
"input_color" => "input-bg",
|
"input_color" => "input-bg",
|
||||||
"input_text" => "input-text",
|
"input_text" => "input-text",
|
||||||
"input_placeholder" => "input-placeholder",
|
"input_placeholder" => "input-placeholder",
|
||||||
"muted_text" => "muted-text"
|
"muted_text" => "muted-text",
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
def render_theme
|
def render_theme
|
||||||
|
|
|
@ -14,15 +14,16 @@ class Inbox < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
after_create do
|
after_create do
|
||||||
user.touch(:inbox_updated_at)
|
user.touch(:inbox_updated_at) # rubocop:disable Rails/SkipsModelValidations
|
||||||
end
|
end
|
||||||
|
|
||||||
after_update do
|
after_update do
|
||||||
user.touch(:inbox_updated_at)
|
user.touch(:inbox_updated_at) # rubocop:disable Rails/SkipsModelValidations
|
||||||
end
|
end
|
||||||
|
|
||||||
after_destroy do
|
after_destroy do
|
||||||
user.touch(:inbox_updated_at)
|
# user might not exist at this point (account deleted, records are cleaned up async)
|
||||||
|
user&.touch(:inbox_updated_at) # rubocop:disable Rails/SkipsModelValidations
|
||||||
end
|
end
|
||||||
|
|
||||||
def answer(answer_content, user)
|
def answer(answer_content, user)
|
||||||
|
@ -49,7 +50,7 @@ class Inbox < ApplicationRecord
|
||||||
user.profile.anon_display_name || APP_CONFIG["anonymous_name"]
|
user.profile.anon_display_name || APP_CONFIG["anonymous_name"]
|
||||||
else
|
else
|
||||||
question.user.profile.safe_name
|
question.user.profile.safe_name
|
||||||
end
|
end,
|
||||||
),
|
),
|
||||||
icon: notification_icon,
|
icon: notification_icon,
|
||||||
body: question.content.truncate(Question::SHORT_QUESTION_MAX_LENGTH),
|
body: question.content.truncate(Question::SHORT_QUESTION_MAX_LENGTH),
|
||||||
|
|
|
@ -5,15 +5,16 @@ class Notification < ApplicationRecord
|
||||||
belongs_to :target, polymorphic: true
|
belongs_to :target, polymorphic: true
|
||||||
|
|
||||||
after_create do
|
after_create do
|
||||||
recipient.touch(:notifications_updated_at)
|
recipient.touch(:notifications_updated_at) # rubocop:disable Rails/SkipsModelValidations
|
||||||
end
|
end
|
||||||
|
|
||||||
after_update do
|
after_update do
|
||||||
recipient.touch(:notifications_updated_at)
|
recipient.touch(:notifications_updated_at) # rubocop:disable Rails/SkipsModelValidations
|
||||||
end
|
end
|
||||||
|
|
||||||
after_destroy do
|
after_destroy do
|
||||||
recipient.touch(:notifications_updated_at)
|
# recipient might not exist at this point (account deleted, records are cleaned up async)
|
||||||
|
recipient&.touch(:notifications_updated_at) # rubocop:disable Rails/SkipsModelValidations
|
||||||
end
|
end
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
|
|
@ -75,6 +75,7 @@ Rails.application.configure do
|
||||||
# Use an evented file watcher to asynchronously detect changes in source code,
|
# Use an evented file watcher to asynchronously detect changes in source code,
|
||||||
# routes, locales, etc. This feature depends on the listen gem.
|
# routes, locales, etc. This feature depends on the listen gem.
|
||||||
# config.file_watcher = ActiveSupport::EventedFileUpdateChecker
|
# config.file_watcher = ActiveSupport::EventedFileUpdateChecker
|
||||||
|
config.hosts += ENV["EXTRA_HOSTS"].split(':') if ENV["EXTRA_HOSTS"].present?
|
||||||
end
|
end
|
||||||
|
|
||||||
# For better_errors to work inside Docker we need
|
# For better_errors to work inside Docker we need
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class IncludeTypeInRelationshipUniqueConstraint < ActiveRecord::Migration[6.1]
|
||||||
|
def change
|
||||||
|
change_table :relationships do |t|
|
||||||
|
t.remove_index(%i[source_id target_id])
|
||||||
|
t.index(%i[source_id target_id type], unique: true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 2023_05_26_181715) do
|
ActiveRecord::Schema.define(version: 2023_10_18_172518) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
@ -159,7 +159,7 @@ ActiveRecord::Schema.define(version: 2023_05_26_181715) do
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.string "type", null: false
|
t.string "type", null: false
|
||||||
t.index ["source_id", "target_id"], name: "index_relationships_on_source_id_and_target_id", unique: true
|
t.index ["source_id", "target_id", "type"], name: "index_relationships_on_source_id_and_target_id_and_type", unique: true
|
||||||
t.index ["source_id"], name: "index_relationships_on_source_id"
|
t.index ["source_id"], name: "index_relationships_on_source_id"
|
||||||
t.index ["target_id"], name: "index_relationships_on_target_id"
|
t.index ["target_id"], name: "index_relationships_on_target_id"
|
||||||
t.index ["type"], name: "index_relationships_on_type"
|
t.index ["type"], name: "index_relationships_on_type"
|
||||||
|
|
|
@ -17,9 +17,9 @@ module Retrospring
|
||||||
|
|
||||||
def month = 10
|
def month = 10
|
||||||
|
|
||||||
def day = 17
|
def day = 19
|
||||||
|
|
||||||
def patch = 1
|
def patch = 0
|
||||||
|
|
||||||
def suffix = ""
|
def suffix = ""
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,22 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
|
|
||||||
describe ApplicationHelper::GraphMethods, :type => :helper do
|
describe ApplicationHelper::GraphMethods, type: :helper do
|
||||||
describe "#user_opengraph" do
|
describe "#user_opengraph" do
|
||||||
context "sample user" do
|
context "sample user" do
|
||||||
let(:user) { FactoryBot.create(:user,
|
let(:user) do
|
||||||
profile: { display_name: 'Cunes',
|
FactoryBot.create(:user,
|
||||||
description: 'A bunch of raccoons in a trenchcoat.' },
|
profile: { display_name: "Cunes",
|
||||||
screen_name: 'raccoons') }
|
description: "A bunch of raccoons in a trenchcoat.", },
|
||||||
|
screen_name: "raccoons",)
|
||||||
|
end
|
||||||
|
|
||||||
subject { user_opengraph(user) }
|
subject { user_opengraph(user) }
|
||||||
|
|
||||||
it 'should generate a matching OpenGraph structure for a user' do
|
it "should generate a matching OpenGraph structure for a user" do
|
||||||
allow(APP_CONFIG).to receive(:[]).with('site_name').and_return('pineapplespring')
|
allow(APP_CONFIG).to receive(:[]).with("site_name").and_return("pineapplespring")
|
||||||
expect(subject).to eq(<<~EOS.chomp)
|
expect(subject).to eq(<<~META.chomp)
|
||||||
<meta property="og:title" content="Cunes">
|
<meta property="og:title" content="Cunes">
|
||||||
<meta property="og:type" content="profile">
|
<meta property="og:type" content="profile">
|
||||||
<meta property="og:image" content="http://test.host/images/large/no_avatar.png">
|
<meta property="og:image" content="http://test.host/images/large/no_avatar.png">
|
||||||
|
@ -22,54 +24,62 @@ describe ApplicationHelper::GraphMethods, :type => :helper do
|
||||||
<meta property="og:description" content="A bunch of raccoons in a trenchcoat.">
|
<meta property="og:description" content="A bunch of raccoons in a trenchcoat.">
|
||||||
<meta property="og:site_name" content="pineapplespring">
|
<meta property="og:site_name" content="pineapplespring">
|
||||||
<meta property="profile:username" content="raccoons">
|
<meta property="profile:username" content="raccoons">
|
||||||
EOS
|
META
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#user_twitter_card" do
|
describe "#user_twitter_card" do
|
||||||
context "sample user" do
|
context "sample user" do
|
||||||
let(:user) { FactoryBot.create(:user,
|
let(:user) do
|
||||||
profile: {
|
FactoryBot.create(:user,
|
||||||
display_name: '',
|
profile: {
|
||||||
description: 'A bunch of raccoons in a trenchcoat.'},
|
display_name: "",
|
||||||
screen_name: 'raccoons') }
|
description: "A bunch of raccoons in a trenchcoat.",
|
||||||
|
},
|
||||||
|
screen_name: "raccoons",)
|
||||||
|
end
|
||||||
|
|
||||||
subject { user_twitter_card(user) }
|
subject { user_twitter_card(user) }
|
||||||
it 'should generate a matching OpenGraph structure for a user' do
|
it "should generate a matching OpenGraph structure for a user" do
|
||||||
expect(subject).to eq(<<~EOS.chomp)
|
expect(subject).to eq(<<~META.chomp)
|
||||||
<meta name="twitter:card" content="summary">
|
<meta name="twitter:card" content="summary">
|
||||||
<meta name="twitter:site" content="@retrospring">
|
<meta name="twitter:site" content="@retrospring">
|
||||||
<meta name="twitter:title" content="Ask me anything!">
|
<meta name="twitter:title" content="Ask me anything!">
|
||||||
<meta name="twitter:description" content="Ask raccoons anything on Retrospring">
|
<meta name="twitter:description" content="Ask raccoons anything on Retrospring">
|
||||||
<meta name="twitter:image" content="http://test.host/images/large/no_avatar.png">
|
<meta name="twitter:image" content="http://test.host/images/large/no_avatar.png">
|
||||||
EOS
|
META
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#answer_opengraph" do
|
describe "#answer_opengraph" do
|
||||||
context "sample user and answer" do
|
context "sample user and answer" do
|
||||||
let!(:user) { FactoryBot.create(:user,
|
let!(:user) do
|
||||||
profile: {
|
FactoryBot.create(:user,
|
||||||
display_name: '',
|
profile: {
|
||||||
description: 'A bunch of raccoons in a trenchcoat.'},
|
display_name: "",
|
||||||
screen_name: 'raccoons') }
|
description: "A bunch of raccoons in a trenchcoat.",
|
||||||
let(:answer) { FactoryBot.create(:answer,
|
},
|
||||||
user_id: user.id,) }
|
screen_name: "raccoons",)
|
||||||
|
end
|
||||||
|
let(:answer) do
|
||||||
|
FactoryBot.create(:answer,
|
||||||
|
user_id: user.id,)
|
||||||
|
end
|
||||||
|
|
||||||
subject { answer_opengraph(answer) }
|
subject { answer_opengraph(answer) }
|
||||||
|
|
||||||
it 'should generate a matching OpenGraph structure for a user' do
|
it "should generate a matching OpenGraph structure for a user" do
|
||||||
allow(APP_CONFIG).to receive(:[]).with('site_name').and_return('pineapplespring')
|
allow(APP_CONFIG).to receive(:[]).with("site_name").and_return("pineapplespring")
|
||||||
expect(subject).to eq(<<~EOS.chomp)
|
expect(subject).to eq(<<~META.chomp)
|
||||||
<meta property="og:title" content="raccoons answered: #{answer.question.content}">
|
<meta property="og:title" content="raccoons answered: #{answer.question.content}">
|
||||||
<meta property="og:type" content="article">
|
<meta property="og:type" content="article">
|
||||||
<meta property="og:image" content="http://test.host/images/large/no_avatar.png">
|
<meta property="og:image" content="http://test.host/images/large/no_avatar.png">
|
||||||
<meta property="og:url" content="http://test.host/@raccoons/a/#{answer.id}">
|
<meta property="og:url" content="http://test.host/@raccoons/a/#{answer.id}">
|
||||||
<meta property="og:description" content="#{answer.content}">
|
<meta property="og:description" content="#{answer.content}">
|
||||||
<meta property="og:site_name" content="pineapplespring">
|
<meta property="og:site_name" content="pineapplespring">
|
||||||
EOS
|
META
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,8 +12,8 @@ describe ApplicationHelper::TitleMethods, type: :helper do
|
||||||
"anonymous_name" => "Anonymous",
|
"anonymous_name" => "Anonymous",
|
||||||
"https" => true,
|
"https" => true,
|
||||||
"items_per_page" => 5,
|
"items_per_page" => 5,
|
||||||
"sharing" => {}
|
"sharing" => {},
|
||||||
})
|
},)
|
||||||
|
|
||||||
user.profile.display_name = "Cool Man"
|
user.profile.display_name = "Cool Man"
|
||||||
user.profile.save!
|
user.profile.save!
|
||||||
|
@ -42,7 +42,7 @@ describe ApplicationHelper::TitleMethods, type: :helper do
|
||||||
|
|
||||||
context "user has custom anonymous display name" do
|
context "user has custom anonymous display name" do
|
||||||
before do
|
before do
|
||||||
FactoryBot.create(:answer, question: question, user: user)
|
FactoryBot.create(:answer, question:, user:)
|
||||||
user.profile.anon_display_name = "Amogus"
|
user.profile.anon_display_name = "Amogus"
|
||||||
user.profile.save!
|
user.profile.save!
|
||||||
end
|
end
|
||||||
|
@ -55,9 +55,9 @@ describe ApplicationHelper::TitleMethods, type: :helper do
|
||||||
|
|
||||||
describe "#answer_title" do
|
describe "#answer_title" do
|
||||||
let(:answer) do
|
let(:answer) do
|
||||||
FactoryBot.create(:answer, user: user,
|
FactoryBot.create(:answer, user:,
|
||||||
content: "a",
|
content: "a",
|
||||||
question_content: "q")
|
question_content: "q",)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should generate a proper title" do
|
it "should generate a proper title" do
|
||||||
|
|
|
@ -1,45 +1,47 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "rails_helper"
|
require "rails_helper"
|
||||||
|
|
||||||
describe BootstrapHelper, :type => :helper do
|
describe BootstrapHelper, type: :helper do
|
||||||
include ActiveSupport::Testing::TimeHelpers
|
include ActiveSupport::Testing::TimeHelpers
|
||||||
|
|
||||||
describe '#nav_entry' do
|
describe "#nav_entry" do
|
||||||
it 'should return a HTML navigation item which links to a given address' do
|
it "should return a HTML navigation item which links to a given address" do
|
||||||
allow(self).to receive(:current_page?).and_return(false)
|
allow(self).to receive(:current_page?).and_return(false)
|
||||||
expect(nav_entry('Example', '/example')).to(
|
expect(nav_entry("Example", "/example")).to(
|
||||||
eq('<li class="nav-item "><a class="nav-link" href="/example">Example</a></li>')
|
eq('<li class="nav-item "><a class="nav-link" href="/example">Example</a></li>'),
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return with an active attribute if the link matches the current URL' do
|
it "should return with an active attribute if the link matches the current URL" do
|
||||||
allow(self).to receive(:current_page?).and_return(true)
|
allow(self).to receive(:current_page?).and_return(true)
|
||||||
expect(nav_entry('Example', '/example')).to(
|
expect(nav_entry("Example", "/example")).to(
|
||||||
eq('<li class="nav-item active "><a class="nav-link" href="/example">Example</a></li>')
|
eq('<li class="nav-item active "><a class="nav-link" href="/example">Example</a></li>'),
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should include an icon if given' do
|
it "should include an icon if given" do
|
||||||
allow(self).to receive(:current_page?).and_return(false)
|
allow(self).to receive(:current_page?).and_return(false)
|
||||||
expect(nav_entry('Example', '/example', icon: 'beaker')).to(
|
expect(nav_entry("Example", "/example", icon: "beaker")).to(
|
||||||
eq('<li class="nav-item "><a class="nav-link" href="/example"><i class="fa fa-beaker"></i> Example</a></li>')
|
eq('<li class="nav-item "><a class="nav-link" href="/example"><i class="fa fa-beaker"></i> Example</a></li>'),
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should only include an icon if wanted' do
|
it "should only include an icon if wanted" do
|
||||||
allow(self).to receive(:current_page?).and_return(false)
|
allow(self).to receive(:current_page?).and_return(false)
|
||||||
expect(nav_entry('Example', '/example', icon: 'beaker', icon_only: true)).to(
|
expect(nav_entry("Example", "/example", icon: "beaker", icon_only: true)).to(
|
||||||
eq('<li class="nav-item "><a class="nav-link" href="/example"><i class="fa fa-beaker" title="Example"></i></a></li>')
|
eq('<li class="nav-item "><a class="nav-link" href="/example"><i class="fa fa-beaker" title="Example"></i></a></li>'),
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should include a badge if given' do
|
it "should include a badge if given" do
|
||||||
allow(self).to receive(:current_page?).and_return(false)
|
allow(self).to receive(:current_page?).and_return(false)
|
||||||
expect(nav_entry('Example', '/example', badge: 3)).to(
|
expect(nav_entry("Example", "/example", badge: 3)).to(
|
||||||
eq('<li class="nav-item "><a class="nav-link" href="/example">Example <span class="badge">3</span></a></li>')
|
eq('<li class="nav-item "><a class="nav-link" href="/example">Example <span class="badge">3</span></a></li>'),
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(nav_entry('Example', '/example', badge: 3, badge_color: 'primary', badge_pill: true)).to(
|
expect(nav_entry("Example", "/example", badge: 3, badge_color: "primary", badge_pill: true)).to(
|
||||||
eq('<li class="nav-item "><a class="nav-link" href="/example">Example <span class="badge badge-primary badge-pill">3</span></a></li>')
|
eq('<li class="nav-item "><a class="nav-link" href="/example">Example <span class="badge badge-primary badge-pill">3</span></a></li>'),
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -52,51 +54,51 @@ describe BootstrapHelper, :type => :helper do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#list_group_item" do
|
describe "#list_group_item" do
|
||||||
it 'should return a HTML navigation item which links to a given address' do
|
it "should return a HTML navigation item which links to a given address" do
|
||||||
allow(self).to receive(:current_page?).and_return(false)
|
allow(self).to receive(:current_page?).and_return(false)
|
||||||
expect(list_group_item('Example', '/example')).to(
|
expect(list_group_item("Example", "/example")).to(
|
||||||
eq('<a href="/example" class="list-group-item list-group-item-action ">Example</a>')
|
eq('<a href="/example" class="list-group-item list-group-item-action ">Example</a>'),
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return with an active attribute if the link matches the current URL' do
|
it "should return with an active attribute if the link matches the current URL" do
|
||||||
allow(self).to receive(:current_page?).and_return(true)
|
allow(self).to receive(:current_page?).and_return(true)
|
||||||
expect(list_group_item('Example', '/example')).to(
|
expect(list_group_item("Example", "/example")).to(
|
||||||
eq('<a href="/example" class="list-group-item list-group-item-action active ">Example</a>')
|
eq('<a href="/example" class="list-group-item list-group-item-action active ">Example</a>'),
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should include a badge if given' do
|
it "should include a badge if given" do
|
||||||
allow(self).to receive(:current_page?).and_return(false)
|
allow(self).to receive(:current_page?).and_return(false)
|
||||||
expect(list_group_item('Example', '/example', badge: 3)).to(
|
expect(list_group_item("Example", "/example", badge: 3)).to(
|
||||||
eq('<a href="/example" class="list-group-item list-group-item-action ">Example <span class="badge">3</span></a>')
|
eq('<a href="/example" class="list-group-item list-group-item-action ">Example <span class="badge">3</span></a>'),
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#bootstrap_color" do
|
describe "#bootstrap_color" do
|
||||||
it 'should map error and alert to danger' do
|
it "should map error and alert to danger" do
|
||||||
expect(bootstrap_color("error")).to eq("danger")
|
expect(bootstrap_color("error")).to eq("danger")
|
||||||
expect(bootstrap_color("alert")).to eq("danger")
|
expect(bootstrap_color("alert")).to eq("danger")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should map notice to info' do
|
it "should map notice to info" do
|
||||||
expect(bootstrap_color("notice")).to eq("info")
|
expect(bootstrap_color("notice")).to eq("info")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return any uncovered value' do
|
it "should return any uncovered value" do
|
||||||
expect(bootstrap_color("success")).to eq("success")
|
expect(bootstrap_color("success")).to eq("success")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#tooltip" do
|
describe "#tooltip" do
|
||||||
it 'should return the proper markup' do
|
it "should return the proper markup" do
|
||||||
expect(tooltip("Example Text", "This is in a tooltip")).to eq("<span title=\"This is in a tooltip\" data-bs-toggle=\"tooltip\" data-bs-placement=\"bottom\">Example Text</span>")
|
expect(tooltip("Example Text", "This is in a tooltip")).to eq("<span title=\"This is in a tooltip\" data-bs-toggle=\"tooltip\" data-bs-placement=\"bottom\">Example Text</span>")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#time_tooltip" do
|
describe "#time_tooltip" do
|
||||||
it 'should return a tooltip with proper time values' do
|
it "should return a tooltip with proper time values" do
|
||||||
travel_to(Time.utc(1984)) do
|
travel_to(Time.utc(1984)) do
|
||||||
@user = FactoryBot.create(:user)
|
@user = FactoryBot.create(:user)
|
||||||
travel 10.minutes
|
travel 10.minutes
|
||||||
|
@ -107,7 +109,7 @@ describe BootstrapHelper, :type => :helper do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#hidespan" do
|
describe "#hidespan" do
|
||||||
it 'should return the proper markup' do
|
it "should return the proper markup" do
|
||||||
expect(hidespan("Hidden Text", "d-none")).to eq("<span class=\"d-none\">Hidden Text</span>")
|
expect(hidespan("Hidden Text", "d-none")).to eq("<span class=\"d-none\">Hidden Text</span>")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,9 +11,9 @@ describe FeedbackHelper, type: :helper do
|
||||||
"canny" => {
|
"canny" => {
|
||||||
sso: "sso",
|
sso: "sso",
|
||||||
feature_board: "feature",
|
feature_board: "feature",
|
||||||
bug_board: "bug"
|
bug_board: "bug",
|
||||||
}
|
},
|
||||||
})
|
},)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#canny_token" do
|
describe "#canny_token" do
|
||||||
|
|
|
@ -10,8 +10,8 @@ describe MarkdownHelper, type: :helper do
|
||||||
"items_per_page" => 5,
|
"items_per_page" => 5,
|
||||||
"allowed_hosts" => [
|
"allowed_hosts" => [
|
||||||
"twitter.com"
|
"twitter.com"
|
||||||
]
|
],
|
||||||
})
|
},)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#markdown" do
|
describe "#markdown" do
|
||||||
|
|
|
@ -9,7 +9,7 @@ describe SocialHelper::TelegramMethods, type: :helper do
|
||||||
:answer,
|
:answer,
|
||||||
user:,
|
user:,
|
||||||
content: "this is an answer\nwith multiple lines\nand **FORMATTING**",
|
content: "this is an answer\nwith multiple lines\nand **FORMATTING**",
|
||||||
question_content: "this is a question .... or is it?"
|
question_content: "this is a question .... or is it?",
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ describe SocialHelper::TelegramMethods, type: :helper do
|
||||||
"hostname" => "example.com",
|
"hostname" => "example.com",
|
||||||
"https" => true,
|
"https" => true,
|
||||||
"items_per_page" => 5,
|
"items_per_page" => 5,
|
||||||
})
|
},)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#telegram_text" do
|
describe "#telegram_text" do
|
||||||
|
|
|
@ -1,33 +1,35 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
|
|
||||||
describe SocialHelper::TumblrMethods, :type => :helper do
|
describe SocialHelper::TumblrMethods, type: :helper do
|
||||||
let(:user) { FactoryBot.create(:user) }
|
let(:user) { FactoryBot.create(:user) }
|
||||||
let(:answer) { FactoryBot.create(:answer, user: user,
|
let(:answer) do
|
||||||
content: 'aaaa',
|
FactoryBot.create(:answer, user:,
|
||||||
question_content: 'q') }
|
content: "aaaa",
|
||||||
|
question_content: "q",)
|
||||||
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
stub_const("APP_CONFIG", {
|
stub_const("APP_CONFIG", {
|
||||||
'hostname' => 'example.com',
|
"hostname" => "example.com",
|
||||||
'anonymous_name' => 'Anonymous',
|
"anonymous_name" => "Anonymous",
|
||||||
'https' => true,
|
"https" => true,
|
||||||
'items_per_page' => 5,
|
"items_per_page" => 5,
|
||||||
'sharing' => {}
|
"sharing" => {},
|
||||||
})
|
},)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#tumblr_title' do
|
describe "#tumblr_title" do
|
||||||
context 'Asker is anonymous' do
|
context "Asker is anonymous" do
|
||||||
subject { tumblr_title(answer) }
|
subject { tumblr_title(answer) }
|
||||||
|
|
||||||
it 'should return a proper title' do
|
it "should return a proper title" do
|
||||||
expect(subject).to eq('Anonymous asked: q')
|
expect(subject).to eq("Anonymous asked: q")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'Asker is known' do
|
context "Asker is known" do
|
||||||
before do
|
before do
|
||||||
@user = FactoryBot.create(:user)
|
@user = FactoryBot.create(:user)
|
||||||
answer.question.user = @user
|
answer.question.user = @user
|
||||||
|
@ -36,24 +38,24 @@ describe SocialHelper::TumblrMethods, :type => :helper do
|
||||||
|
|
||||||
subject { tumblr_title(answer) }
|
subject { tumblr_title(answer) }
|
||||||
|
|
||||||
it 'should return a proper title' do
|
it "should return a proper title" do
|
||||||
expect(subject).to eq("#{answer.question.user.profile.display_name} asked: q")
|
expect(subject).to eq("#{answer.question.user.profile.display_name} asked: q")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#tumblr_body' do
|
describe "#tumblr_body" do
|
||||||
subject { tumblr_body(answer) }
|
subject { tumblr_body(answer) }
|
||||||
|
|
||||||
it 'should return a proper body' do
|
it "should return a proper body" do
|
||||||
expect(subject).to eq("aaaa\n\n[Smile or comment on the answer here](https://example.com/@#{answer.user.screen_name}/a/#{answer.id})")
|
expect(subject).to eq("aaaa\n\n[Smile or comment on the answer here](https://example.com/@#{answer.user.screen_name}/a/#{answer.id})")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#tumblr_share_url' do
|
describe "#tumblr_share_url" do
|
||||||
subject { tumblr_share_url(answer) }
|
subject { tumblr_share_url(answer) }
|
||||||
|
|
||||||
it 'should return a proper share link' do
|
it "should return a proper share link" do
|
||||||
expect(subject).to eq("https://www.tumblr.com/widgets/share/tool?shareSource=legacy&posttype=text&title=#{CGI.escape(tumblr_title(answer))}&url=#{CGI.escape("https://example.com/@#{answer.user.screen_name}/a/#{answer.id}")}&caption=&content=#{CGI.escape(tumblr_body(answer))}")
|
expect(subject).to eq("https://www.tumblr.com/widgets/share/tool?shareSource=legacy&posttype=text&title=#{CGI.escape(tumblr_title(answer))}&url=#{CGI.escape("https://example.com/@#{answer.user.screen_name}/a/#{answer.id}")}&caption=&content=#{CGI.escape(tumblr_body(answer))}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,39 +1,41 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
|
|
||||||
describe SocialHelper::TwitterMethods, :type => :helper do
|
describe SocialHelper::TwitterMethods, type: :helper do
|
||||||
let(:user) { FactoryBot.create(:user) }
|
let(:user) { FactoryBot.create(:user) }
|
||||||
let(:question_content) { 'q' * 255 }
|
let(:question_content) { "q" * 255 }
|
||||||
let(:answer_content) { 'a' * 255 }
|
let(:answer_content) { "a" * 255 }
|
||||||
let(:answer) { FactoryBot.create(:answer, user: user,
|
let(:answer) do
|
||||||
content: answer_content,
|
FactoryBot.create(:answer, user:,
|
||||||
question_content: question_content) }
|
content: answer_content,
|
||||||
|
question_content:,)
|
||||||
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
stub_const("APP_CONFIG", {
|
stub_const("APP_CONFIG", {
|
||||||
'hostname' => 'example.com',
|
"hostname" => "example.com",
|
||||||
'https' => true,
|
"https" => true,
|
||||||
'items_per_page' => 5
|
"items_per_page" => 5,
|
||||||
})
|
},)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#prepare_tweet' do
|
describe "#prepare_tweet" do
|
||||||
context 'when the question and answer need to be shortened' do
|
context "when the question and answer need to be shortened" do
|
||||||
subject { prepare_tweet(answer) }
|
subject { prepare_tweet(answer) }
|
||||||
|
|
||||||
it 'should return a properly formatted tweet' do
|
it "should return a properly formatted tweet" do
|
||||||
expect(subject).to eq("#{'q' * 123}… — #{'a' * 124}… https://example.com/@#{user.screen_name}/a/#{answer.id}")
|
expect(subject).to eq("#{'q' * 123}… — #{'a' * 124}… https://example.com/@#{user.screen_name}/a/#{answer.id}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when a suffix has been passed' do
|
context "when a suffix has been passed" do
|
||||||
let(:question_content) { 'question' }
|
let(:question_content) { "question" }
|
||||||
let(:answer_content) { 'answer' }
|
let(:answer_content) { "answer" }
|
||||||
|
|
||||||
subject { prepare_tweet(answer, '#askracc') }
|
subject { prepare_tweet(answer, "#askracc") }
|
||||||
|
|
||||||
it 'should include the suffix after the link' do
|
it "should include the suffix after the link" do
|
||||||
expect(subject).to eq("question — answer #askracc https://example.com/@#{user.screen_name}/a/#{answer.id}")
|
expect(subject).to eq("question — answer #askracc https://example.com/@#{user.screen_name}/a/#{answer.id}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -49,34 +51,34 @@ describe SocialHelper::TwitterMethods, :type => :helper do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when a suffix has been passed and the tweet needs to be shortened' do
|
context "when a suffix has been passed and the tweet needs to be shortened" do
|
||||||
subject { prepare_tweet(answer, '#askracc') }
|
subject { prepare_tweet(answer, "#askracc") }
|
||||||
|
|
||||||
it 'should shorten the tweet while keeping the suffix intact' do
|
it "should shorten the tweet while keeping the suffix intact" do
|
||||||
expect(subject).to eq("#{'q' * 120}… — #{'a' * 120}… #askracc https://example.com/@#{user.screen_name}/a/#{answer.id}")
|
expect(subject).to eq("#{'q' * 120}… — #{'a' * 120}… #askracc https://example.com/@#{user.screen_name}/a/#{answer.id}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the question and answer are short' do
|
context "when the question and answer are short" do
|
||||||
before do
|
before do
|
||||||
answer.question.content = 'Why are raccoons so good?'
|
answer.question.content = "Why are raccoons so good?"
|
||||||
answer.question.save!
|
answer.question.save!
|
||||||
answer.content = 'Because they are good cunes.'
|
answer.content = "Because they are good cunes."
|
||||||
answer.save!
|
answer.save!
|
||||||
end
|
end
|
||||||
|
|
||||||
subject { prepare_tweet(answer) }
|
subject { prepare_tweet(answer) }
|
||||||
|
|
||||||
it 'should return a properly formatted tweet' do
|
it "should return a properly formatted tweet" do
|
||||||
expect(subject).to eq("#{answer.question.content} — #{answer.content} https://example.com/@#{user.screen_name}/a/#{answer.id}")
|
expect(subject).to eq("#{answer.question.content} — #{answer.content} https://example.com/@#{user.screen_name}/a/#{answer.id}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#twitter_share_url' do
|
describe "#twitter_share_url" do
|
||||||
subject { twitter_share_url(answer) }
|
subject { twitter_share_url(answer) }
|
||||||
|
|
||||||
it 'should return a proper share link' do
|
it "should return a proper share link" do
|
||||||
expect(subject).to eq("https://twitter.com/intent/tweet?text=#{CGI.escape(prepare_tweet(answer))}")
|
expect(subject).to eq("https://twitter.com/intent/tweet?text=#{CGI.escape(prepare_tweet(answer))}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,8 +18,7 @@ describe SocialHelper, type: :helper do
|
||||||
"hostname" => "example.com",
|
"hostname" => "example.com",
|
||||||
"https" => true,
|
"https" => true,
|
||||||
"items_per_page" => 5,
|
"items_per_page" => 5,
|
||||||
},
|
},)
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#answer_share_url" do
|
describe "#answer_share_url" do
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
require "rails_helper"
|
require "rails_helper"
|
||||||
|
|
||||||
describe ThemeHelper, :type => :helper do
|
describe ThemeHelper, type: :helper do
|
||||||
describe "#render_theme" do
|
describe "#render_theme" do
|
||||||
context "when target page doesn't have a theme" do
|
context "when target page doesn't have a theme" do
|
||||||
it "returns no theme" do
|
it "returns no theme" do
|
||||||
|
@ -18,7 +18,7 @@ describe ThemeHelper, :type => :helper do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns a theme" do
|
it "returns a theme" do
|
||||||
expect(helper.render_theme).to include('<style>:root {')
|
expect(helper.render_theme).to include("<style>:root {")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "contains correct theme background colors" do
|
it "contains correct theme background colors" do
|
||||||
|
@ -193,12 +193,12 @@ describe ThemeHelper, :type => :helper do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#theme_color' do
|
describe "#theme_color" do
|
||||||
subject { helper.theme_color }
|
subject { helper.theme_color }
|
||||||
|
|
||||||
context 'when user is signed in' do
|
context "when user is signed in" do
|
||||||
let(:user) { FactoryBot.create(:user) }
|
let(:user) { FactoryBot.create(:user) }
|
||||||
let(:theme) { FactoryBot.create(:theme, user: user) }
|
let(:theme) { FactoryBot.create(:theme, user:) }
|
||||||
|
|
||||||
before(:each) do
|
before(:each) do
|
||||||
user.theme = theme
|
user.theme = theme
|
||||||
|
@ -206,24 +206,24 @@ describe ThemeHelper, :type => :helper do
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return the user theme\'s primary color' do
|
it "should return the user theme's primary color" do
|
||||||
expect(subject).to eq('#8e8cd8')
|
expect(subject).to eq("#8e8cd8")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'user is not signed in' do
|
context "user is not signed in" do
|
||||||
it 'should return the default primary color' do
|
it "should return the default primary color" do
|
||||||
expect(subject).to eq('#5e35b1')
|
expect(subject).to eq("#5e35b1")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#mobile_theme_color' do
|
describe "#mobile_theme_color" do
|
||||||
subject { helper.mobile_theme_color }
|
subject { helper.mobile_theme_color }
|
||||||
|
|
||||||
context 'when user is signed in' do
|
context "when user is signed in" do
|
||||||
let(:user) { FactoryBot.create(:user) }
|
let(:user) { FactoryBot.create(:user) }
|
||||||
let(:theme) { FactoryBot.create(:theme, user: user) }
|
let(:theme) { FactoryBot.create(:theme, user:) }
|
||||||
|
|
||||||
before(:each) do
|
before(:each) do
|
||||||
user.theme = theme
|
user.theme = theme
|
||||||
|
@ -231,14 +231,14 @@ describe ThemeHelper, :type => :helper do
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return the user theme\'s background color' do
|
it "should return the user theme's background color" do
|
||||||
expect(subject).to eq('#c6c5eb')
|
expect(subject).to eq("#c6c5eb")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'user is not signed in' do
|
context "user is not signed in" do
|
||||||
it 'should return the default background color' do
|
it "should return the default background color" do
|
||||||
expect(subject).to eq('#f0edf4')
|
expect(subject).to eq("#f0edf4")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,10 +6,10 @@ describe UserHelper, type: :helper do
|
||||||
describe "#user_screen_name" do
|
describe "#user_screen_name" do
|
||||||
subject do
|
subject do
|
||||||
helper.user_screen_name(user,
|
helper.user_screen_name(user,
|
||||||
context_user: context_user,
|
context_user:,
|
||||||
author_identifier: author_identifier,
|
author_identifier:,
|
||||||
url: url,
|
url:,
|
||||||
link_only: link_only)
|
link_only:,)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:user) { FactoryBot.create(:user) }
|
let(:user) { FactoryBot.create(:user) }
|
||||||
|
@ -20,8 +20,8 @@ describe UserHelper, type: :helper do
|
||||||
|
|
||||||
before do
|
before do
|
||||||
stub_const("APP_CONFIG", {
|
stub_const("APP_CONFIG", {
|
||||||
"anonymous_name" => "Anonymous"
|
"anonymous_name" => "Anonymous",
|
||||||
})
|
},)
|
||||||
end
|
end
|
||||||
|
|
||||||
context "moderation view enabled" do
|
context "moderation view enabled" do
|
||||||
|
@ -80,7 +80,7 @@ describe UserHelper, type: :helper do
|
||||||
context "author is anonymous" do
|
context "author is anonymous" do
|
||||||
let(:author_identifier) { "some_identifier" }
|
let(:author_identifier) { "some_identifier" }
|
||||||
|
|
||||||
let(:context_user) { FactoryBot.create(:user, profile: { anon_display_name: anon_display_name }) }
|
let(:context_user) { FactoryBot.create(:user, profile: { anon_display_name: }) }
|
||||||
context "context user has custom anonymous name" do
|
context "context user has custom anonymous name" do
|
||||||
let(:anon_display_name) { "Sneaky Raccoon" }
|
let(:anon_display_name) { "Sneaky Raccoon" }
|
||||||
|
|
||||||
|
|
26
spec/models/inbox_spec.rb
Normal file
26
spec/models/inbox_spec.rb
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "rails_helper"
|
||||||
|
|
||||||
|
describe Inbox, type: :model do
|
||||||
|
describe "associations" do
|
||||||
|
it { should belong_to(:user) }
|
||||||
|
it { should belong_to(:question) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "before_destroy" do
|
||||||
|
let(:user) { FactoryBot.create(:user) }
|
||||||
|
let(:question) { FactoryBot.create(:question, author_is_anonymous: true) }
|
||||||
|
|
||||||
|
it "does not fail if the user wants to delete their account" do
|
||||||
|
Inbox.create(user:, question:)
|
||||||
|
|
||||||
|
# this deletes the User record and enqueues the deletion of all
|
||||||
|
# associated records in sidekiq
|
||||||
|
user.destroy!
|
||||||
|
|
||||||
|
# so let's drain the queues
|
||||||
|
expect { Sidekiq::Worker.drain_all }.not_to raise_error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
26
spec/models/notification_spec.rb
Normal file
26
spec/models/notification_spec.rb
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "rails_helper"
|
||||||
|
|
||||||
|
describe Notification, type: :model do
|
||||||
|
describe "associations" do
|
||||||
|
it { should belong_to(:recipient) }
|
||||||
|
it { should belong_to(:target) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "before_destroy" do
|
||||||
|
let(:user) { FactoryBot.create(:user) }
|
||||||
|
let(:answer) { FactoryBot.create(:answer, user: FactoryBot.create(:user)) }
|
||||||
|
|
||||||
|
it "does not fail if the user wants to delete their account" do
|
||||||
|
Notification::QuestionAnswered.create(recipient: user, target: answer)
|
||||||
|
|
||||||
|
# this deletes the User record and enqueues the deletion of all
|
||||||
|
# associated records in sidekiq
|
||||||
|
user.destroy!
|
||||||
|
|
||||||
|
# so let's drain the queues
|
||||||
|
expect { Sidekiq::Worker.drain_all }.not_to raise_error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue