require "rails_helper" RSpec.describe "ArticlesShow" do let(:user) { create(:user) } let(:admin_user) { create(:user, :admin) } let(:article) { create(:article, user: user, published: true, organization: organization) } let(:organization) { create(:organization) } let(:doc) { Nokogiri::HTML(response.body) } let(:text) { doc.at('script[type="application/ld+json"]').text } let(:response_json) { JSON.parse(text) } describe "GET /:slug (articles)" do before do allow(Settings::General).to receive(:logo_png).and_return("logo.png") get article.path end it "returns a 200 status when navigating to the article's page" do expect(response).to have_http_status(:ok) end it "renders the proper JSON-LD for an article" do expect(response_json).to include( "@context" => "http://schema.org", "@type" => "Article", "mainEntityOfPage" => { "@type" => "WebPage", "@id" => URL.article(article) }, "url" => URL.article(article), "image" => [ ApplicationController.helpers.article_social_image_url(article, width: 1080, height: 1080), ApplicationController.helpers.article_social_image_url(article, width: 1280, height: 720), ApplicationController.helpers.article_social_image_url(article, width: 1600, height: 900), ], "publisher" => { "@context" => "http://schema.org", "@type" => "Organization", "name" => Settings::Community.community_name.to_s, "logo" => { "@context" => "http://schema.org", "@type" => "ImageObject", "url" => ApplicationController.helpers.optimized_image_url(Settings::General.logo_png, width: 192, fetch_format: "png"), "width" => "192", "height" => "192" } }, "headline" => article.title, "author" => { "@context" => "http://schema.org", "@type" => "Person", "url" => URL.user(user), "name" => user.name }, "datePublished" => article.published_timestamp, "dateModified" => article.published_timestamp, ) end it "renders 'posted on' information" do get article.path expect(response.body).to include("Posted on") expect(response.body).not_to include("Scheduled for") end end describe "GET /:username/:slug (scheduled)" do let(:scheduled_article) { create(:article, published: true, published_at: Date.tomorrow) } let(:query_params) { "?preview=#{scheduled_article.password}" } let(:scheduled_article_path) { scheduled_article.path + query_params } it "renders a scheduled article with the article password" do get scheduled_article_path expect(response).to have_http_status(:ok) expect(response.body).to include(scheduled_article.title) end it "renders 'scheduled for' information" do get scheduled_article_path expect(response.body).to include("Scheduled for") expect(response.body).not_to include("Posted on") end it "doesn't show edit link when user is not signed in" do get scheduled_article_path expect(response.body).not_to include("Click to edit") end it "renders 404 for a scheduled article w/o article password" do expect { get scheduled_article.path }.to raise_error(ActiveRecord::RecordNotFound) end end it "renders the proper organization for an article when one is present" do get organization.path expect(response_json).to include( { "@context" => "http://schema.org", "@type" => "Organization", "mainEntityOfPage" => { "@type" => "WebPage", "@id" => URL.organization(organization) }, "url" => URL.organization(organization), "image" => organization.profile_image_url_for(length: 320), "name" => organization.name, "description" => organization.summary }, ) end context "when keywords are set" do it "shows keywords" do allow(Settings::General).to receive(:meta_keywords).and_return({ article: "hello, world" }) article.update_column(:cached_tag_list, "super sheep") get article.path expect(response.body).to include('') end end context "when keywords are not" do it "does not show keywords" do allow(Settings::General).to receive(:meta_keywords).and_return({ article: "" }) article.update_column(:cached_tag_list, "super sheep") get article.path expect(response.body).not_to include( '', ) end end context "when author has spam role" do before do article.user.add_role(:spam) end it "renders 404" do expect do get article.path end.to raise_error(ActiveRecord::RecordNotFound) end it "renders 404 for authorized user" do sign_in user expect do get article.path end.to raise_error(ActiveRecord::RecordNotFound) end it "renders successfully for admins", :aggregate_failures do sign_in admin_user get article.path expect(response).to be_successful expect(response.body).to include("Spam") end end context "when user signed in" do before do sign_in user get article.path end describe "GET /:slug (user)" do it "does not render json ld" do expect(response.body).not_to include "application/ld+json" end it "renders comment sort button" do expect(response.body).to include "toggle-comments-sort-dropdown" end end end context "when user not signed in" do before do get article.path end describe "GET /:slug (user)" do it "does not render json ld" do expect(response.body).to include "application/ld+json" end it "does not render comment sort button" do expect(response.body).not_to include "toggle-comments-sort-dropdown" end end end context "with comments" do let!(:spam_comment) { create(:comment, score: -450, commentable: article, body_markdown: "Spam comment") } before do create(:comment, score: 10, commentable: article, body_markdown: "Good comment") create(:comment, score: -99, commentable: article, body_markdown: "Bad comment") create(:comment, score: -10, commentable: article, body_markdown: "Mediocre comment") end context "when user signed in" do before do sign_in user end it "shows positive comments" do get article.path expect(response.body).to include("Good comment") end it "shows comments with score from -400 to -75" do get article.path expect(response.body).to include("Bad comment") end it "hides comments with score < -400 and no comment deleted message" do get article.path expect(response.body).not_to include("Spam comment") expect(response.body).not_to include("Comment deleted") end it "displays children of a low-quality comment and comment deleted message" do create(:comment, score: 0, commentable: article, parent: spam_comment, body_markdown: "Child comment") get article.path expect(response.body).to include("Child comment") expect(response.body).to include("Comment deleted") # instead of the low quality one end it "displays comments count w/o including super low-quality ones" do get article.path expect(response.body).to include("(3)") end it "displays includes spam comments in comments count if they have children" do create(:comment, score: 0, commentable: article, parent: spam_comment, body_markdown: "Child comment") get article.path expect(response.body).to include("(5)") end end context "when user not signed in" do it "shows positive comments" do get article.path expect(response.body).to include("Good comment") end it "hides all negative comments", :aggregate_failures do get article.path expect(response.body).not_to include("Bad comment") expect(response.body).not_to include("Spam comment") expect(response.body).not_to include("Mediocre comment") end it "doesn't show children of a low-quality comment" do create(:comment, score: 0, commentable: article, parent: spam_comment, body_markdown: "Child comment") get article.path expect(response.body).not_to include("Child comment") end end context "when below post billboard exists" do it "does not render when it is below long article threshold" do article.update_column(:body_markdown, "a" * 888) get article.path expect(response.body).not_to include("body-billboard-container") end it "renders when it is above long article threshold" do article.update_column(:body_markdown, "a" * 901) get article.path expect(response.body).to include("body-billboard-container") end end context "when a mid-comments billboard exists" do it "does not render the billboard if there are fewer than 6 root comments" do create_list(:comment, 6, commentable: article) get article.path expect(response.body).not_to include("mid-comments-billboard-container") end it "renders the billboard if there are 6 or more root comments" do create_list(:comment, 7, commentable: article) get article.path expect(response.body).to include("mid-comments-billboard-container") end it "does not render the billboard when the first comment has too few children" do create(:comment, commentable: article, score: 100) create_list(:comment, 4, commentable: article, parent: Comment.last) create(:comment, commentable: article, score: 2) get article.path expect(response.body).not_to include("mid-comments-billboard-container") end it "renders the billboard when the first comment has enough children" do create(:comment, commentable: article, score: 100) create_list(:comment, 5, commentable: article, parent: Comment.last) create(:comment, commentable: article, score: 2) get article.path expect(response.body).to include("mid-comments-billboard-container") end end end context "when user not signed in but internal nav triggered" do before do get "#{article.path}?i=i" end describe "GET /:slug (user)" do it "does not render json ld" do expect(response.body).not_to include "application/ld+json" end end end end