DEV: Absorb onebox gem into core (#12979)

* Move onebox gem in core library

* Update template file path

* Remove warning for onebox gem caching

* Remove onebox version file

* Remove onebox gem

* Add sanitize gem

* Require onebox library in lazy-yt plugin

* Remove onebox web specific code

This code was used in standalone onebox Sinatra application

* Merge Discourse specific AllowlistedGenericOnebox engine in core

* Fix onebox engine filenames to match class name casing

* Move onebox specs from gem into core

* DEV: Rename `response` helper to `onebox_response`

Fixes a naming collision.

* Require rails_helper

* Don't use `before/after(:all)`

* Whitespace

* Remove fakeweb

* Remove poor unit tests

* DEV: Re-add fakeweb, plugins are using it

* Move onebox helpers

* Stub Instagram API

* FIX: Follow additional redirect status codes (#476)

Don’t throw errors if we encounter 303, 307 or 308 HTTP status codes in responses

* Remove an empty file

* DEV: Update the license file

Using the copy from https://choosealicense.com/licenses/gpl-2.0/#

Hopefully this will enable GitHub to show the license UI?

* DEV: Update embedded copyrights

* DEV: Add Onebox copyright notice

* DEV: Add MIT license, convert COPYRIGHT.txt to md

* DEV: Remove an incorrect copyright claim

Co-authored-by: Jarek Radosz <jradosz@gmail.com>
Co-authored-by: jbrw <jamie@goatforce5.org>
This commit is contained in:
Arpit Jalan
2021-05-26 15:11:35 +05:30
committed by GitHub
parent d0779a87bb
commit 283b08d45f
211 changed files with 78330 additions and 74 deletions

View File

@ -0,0 +1,185 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::AllowlistedGenericOnebox do
describe ".===" do
it "matches any domain" do
expect(described_class === URI('http://foo.bar/resource')).to be(true)
end
it "doesn't match an IP address" do
expect(described_class === URI('http://1.2.3.4/resource')).to be(false)
expect(described_class === URI('http://1.2.3.4:1234/resource')).to be(false)
end
end
describe 'html_providers' do
class HTMLOnebox < Onebox::Engine::AllowlistedGenericOnebox
def data
{
html: 'cool html',
height: 123,
provider_name: 'CoolSite',
}
end
end
it "doesn't return the HTML when not in the `html_providers`" do
Onebox::Engine::AllowlistedGenericOnebox.html_providers = []
expect(HTMLOnebox.new("http://coolsite.com").to_html).to be_nil
end
it "returns the HMTL when in the `html_providers`" do
Onebox::Engine::AllowlistedGenericOnebox.html_providers = ['CoolSite']
expect(HTMLOnebox.new("http://coolsite.com").to_html).to eq "cool html"
end
end
describe 'rewrites' do
class DummyOnebox < Onebox::Engine::AllowlistedGenericOnebox
def generic_html
"<iframe src='http://youtube.com/asdf'></iframe>"
end
end
it "doesn't rewrite URLs that arent in the list" do
Onebox::Engine::AllowlistedGenericOnebox.rewrites = []
expect(DummyOnebox.new("http://youtube.com").to_html).to eq "<iframe src='http://youtube.com/asdf'></iframe>"
end
it "rewrites URLs when allowlisted" do
Onebox::Engine::AllowlistedGenericOnebox.rewrites = %w(youtube.com)
expect(DummyOnebox.new("http://youtube.com").to_html).to eq "<iframe src='https://youtube.com/asdf'></iframe>"
end
end
describe 'oembed_providers' do
let(:url) { "http://www.meetup.com/Toronto-Ember-JS-Meetup/events/219939537" }
before do
stub_request(:get, url).to_return(status: 200, body: onebox_response('meetup'))
stub_request(:get, "http://api.meetup.com/oembed?url=#{url}").to_return(status: 200, body: onebox_response('meetup_oembed'))
end
it 'uses the endpoint for the url' do
onebox = described_class.new("http://www.meetup.com/Toronto-Ember-JS-Meetup/events/219939537")
expect(onebox.raw).not_to be_nil
expect(onebox.raw[:title]).to eq "February EmberTO Meet-up"
end
end
describe "cookie support" do
let(:url) { "http://www.dailymail.co.uk/news/article-479146/Brutality-justice-The-truth-tarred-feathered-drug-dealer.html" }
it "sends the cookie with the request" do
stub_request(:get, url)
.with(headers: { cookie: 'evil=trout' })
.to_return(status: 200, body: onebox_response('dailymail'))
onebox = described_class.new(url)
onebox.options = { cookie: "evil=trout" }
expect(onebox.to_html).not_to be_empty
end
it "fetches site_name and article_published_time tags" do
stub_request(:get, url).to_return(status: 200, body: onebox_response('dailymail'))
onebox = described_class.new(url)
expect(onebox.to_html).to include("Mail Online &ndash; 8 Aug 14")
end
end
describe 'canonical link' do
context 'uses canonical link if available' do
let(:mobile_url) { "https://m.etsy.com/in-en/listing/87673424/personalized-word-pillow-case-letter" }
let(:canonical_url) { "https://www.etsy.com/in-en/listing/87673424/personalized-word-pillow-case-letter" }
before do
stub_request(:get, mobile_url).to_return(status: 200, body: onebox_response('etsy_mobile'))
stub_request(:get, canonical_url).to_return(status: 200, body: onebox_response('etsy'))
end
it 'fetches opengraph data and price from canonical link' do
onebox = described_class.new(mobile_url)
expect(onebox.to_html).not_to be_nil
expect(onebox.to_html).to include("images/favicon.ico")
expect(onebox.to_html).to include("Etsy")
expect(onebox.to_html).to include("Personalized Word Pillow Case")
expect(onebox.to_html).to include("Allow your personality to shine through your decor; this contemporary and modern accent will help you do just that.")
expect(onebox.to_html).to include("https://i.etsystatic.com/6088772/r/il/719b4b/1631899982/il_570xN.1631899982_2iay.jpg")
expect(onebox.to_html).to include("CAD 52.00")
end
end
context 'does not use canonical link for Discourse topics' do
let(:discourse_topic_url) { "https://meta.discourse.org/t/congratulations-most-stars-in-2013-github-octoverse/12483" }
let(:discourse_topic_reply_url) { "https://meta.discourse.org/t/congratulations-most-stars-in-2013-github-octoverse/12483/2" }
before do
stub_request(:get, discourse_topic_url).to_return(status: 200, body: onebox_response('discourse_topic'))
stub_request(:get, discourse_topic_reply_url).to_return(status: 200, body: onebox_response('discourse_topic_reply'))
end
it 'fetches opengraph data from original link' do
onebox = described_class.new(discourse_topic_reply_url)
expect(onebox.to_html).not_to be_nil
expect(onebox.to_html).to include("Congratulations, most stars in 2013 GitHub Octoverse!")
expect(onebox.to_html).to include("Thanks for that link and thank you – and everyone else who is contributing to the project!")
expect(onebox.to_html).to include("https://d11a6trkgmumsb.cloudfront.net/optimized/2X/d/d063b3b0807377d98695ee08042a9ba0a8c593bd_2_690x362.png")
end
end
end
describe 'to_html' do
let(:original_link) { "http://www.dailymail.co.uk/pages/live/articles/news/news.html?in_article_id=479146&in_page_id=1770" }
let(:redirect_link) { 'http://www.dailymail.co.uk/news/article-479146/Brutality-justice-The-truth-tarred-feathered-drug-dealer.html' }
before do
stub_request(:get, original_link).to_return(
status: 301,
headers: {
location: redirect_link,
}
)
stub_request(:get, redirect_link).to_return(status: 200, body: onebox_response('dailymail'))
end
around do |example|
previous_options = Onebox.options.to_h
example.run
Onebox.options = previous_options
end
it "follows redirects and includes the summary" do
Onebox.options = { redirect_limit: 2 }
onebox = described_class.new(original_link)
expect(onebox.to_html).to include("It was the most chilling image of the week")
end
it "recives an error with too many redirects" do
Onebox.options = { redirect_limit: 1 }
onebox = described_class.new(original_link)
expect(onebox.to_html).to be_nil
end
end
describe 'missing description' do
context 'works without description if image is present' do
before do
stub_request(:get, "https://edition.cnn.com/2020/05/15/health/gallery/coronavirus-people-adopting-pets-photos/index.html")
.to_return(status: 200, body: onebox_response('cnn'))
stub_request(:get, "https://www.cnn.com/2020/05/15/health/gallery/coronavirus-people-adopting-pets-photos/index.html")
.to_return(status: 200, body: onebox_response('cnn'))
end
it 'shows basic onebox' do
onebox = described_class.new("https://edition.cnn.com/2020/05/15/health/gallery/coronavirus-people-adopting-pets-photos/index.html")
expect(onebox.to_html).not_to be_nil
expect(onebox.to_html).to include("https://edition.cnn.com/2020/05/15/health/gallery/coronavirus-people-adopting-pets-photos/index.html")
expect(onebox.to_html).to include("https://cdn.cnn.com/cnnnext/dam/assets/200427093451-10-coronavirus-people-adopting-pets-super-tease.jpg")
expect(onebox.to_html).to include("People are fostering and adopting pets during the pandemic")
end
end
end
end

View File

@ -0,0 +1,196 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::AmazonOnebox do
context "regular amazon page" do
before do
@link = "https://www.amazon.com/Knit-Noro-Accessories-Colorful-Little/dp/193609620X"
@uri = "https://www.amazon.com/dp/193609620X"
stub_request(:get, "https://www.amazon.com/Seven-Languages-Weeks-Programming-Programmers/dp/193435659X")
.to_return(status: 200, body: onebox_response("amazon"))
end
include_context "engines"
it_behaves_like "an engine"
describe "works with international domains" do
def check_link(tdl, link)
onebox_cls = Onebox::Matcher.new(link).oneboxed
expect(onebox_cls).to_not be(nil)
expect(onebox_cls.new(link).url).to include("https://www.amazon.#{tdl}")
end
it "matches canadian domains" do
check_link("ca", "https://www.amazon.ca/Too-Much-Happiness-Alice-Munro-ebook/dp/B0031TZ98K/")
end
it "matches german domains" do
check_link("de", "https://www.amazon.de/Buddenbrooks-Verfall-einer-Familie-Roman/dp/3596294312/")
end
it "matches uk domains" do
check_link("co.uk", "https://www.amazon.co.uk/Pygmalion-George-Bernard-Shaw/dp/1420925237/")
end
it "matches japanese domains" do
check_link("co.jp", "https://www.amazon.co.jp/%E9%9B%AA%E5%9B%BD-%E6%96%B0%E6%BD%AE%E6%96%87%E5%BA%AB-%E3%81%8B-1-1-%E5%B7%9D%E7%AB%AF-%E5%BA%B7%E6%88%90/dp/4101001014/")
end
it "matches chinese domains" do
check_link("cn", "https://www.amazon.cn/%E5%AD%99%E5%AD%90%E5%85%B5%E6%B3%95-%E5%AD%99%E8%86%91%E5%85%B5%E6%B3%95-%E5%AD%99%E6%AD%A6/dp/B0011C40FC/")
end
it "matches french domains" do
check_link("fr", "https://www.amazon.fr/Les-Mots-autres-%C3%A9crits-autobiographiques/dp/2070114147/")
end
it "matches italian domains" do
check_link("it", "https://www.amazon.it/Tutte-poesie-Salvatore-Quasimodo/dp/8804520477/")
end
it "matches spanish domains" do
check_link("es", "https://www.amazon.es/familia-Pascual-Duarte-Camilo-Jos%C3%A9-ebook/dp/B00EJRTKTW/")
end
it "matches brasilian domains" do
check_link("com.br", "https://www.amazon.com.br/A-p%C3%A1tria-chuteiras-Nelson-Rodrigues-ebook/dp/B00J2B414Y/")
end
it "matches indian domains" do
check_link("in", "https://www.amazon.in/Fireflies-Rabindranath-Tagore/dp/9381523169/")
end
it "matches mexican domains" do
check_link("com.mx", "https://www.amazon.com.mx/Legend-Zelda-Links-Awakening-Nintendo/dp/B07SG15148/")
end
end
describe "#url" do
let(:long_url) { "https://www.amazon.ca/gp/product/B087Z3N428?pf_rd_r=SXABADD0ZZ3NF9Q5F8TW&pf_rd_p=05378fd5-c43e-4948-99b1-a65b129fdd73&pd_rd_r=0237fb28-7f47-49f4-986a-be0c78e52863&pd_rd_w=FfIoI&pd_rd_wg=Hw4qq&ref_=pd_gw_unk" }
it "maintains the same http/https scheme as the requested URL" do
expect(described_class.new("https://www.amazon.fr/gp/product/B01BYD0TZM").url)
.to eq("https://www.amazon.fr/dp/B01BYD0TZM")
expect(described_class.new("http://www.amazon.fr/gp/product/B01BYD0TZM").url)
.to eq("https://www.amazon.fr/dp/B01BYD0TZM")
end
it "removes parameters from the URL" do
expect(described_class.new(long_url).url)
.not_to include("?pf_rd_r")
end
end
describe "#to_html" do
it "includes image" do
expect(html).to include("https://images-na.ssl-images-amazon.com/images/I/51opYcR6kVL._SY400_.jpg")
end
it "includes description" do
expect(html).to include("You should learn a programming language every year, as recommended by The Pragmatic Programmer.")
end
it "includes price" do
expect(html).to include("$21.11")
end
it "includes title" do
expect(html).to include("Seven Languages in Seven Weeks: A Pragmatic Guide to Learning Programming Languages (Pragmatic Programmers)")
end
end
end
context "amazon with opengraph" do
let(:link) { "https://www.amazon.com/dp/B01MFXN4Y2" }
let(:html) { described_class.new(link).to_html }
before do
stub_request(:get, "https://www.amazon.com/dp/B01MFXN4Y2")
.to_return(status: 200, body: onebox_response("amazon-og"))
stub_request(:get, "https://www.amazon.com/Christine-Rebecca-Hall/dp/B01MFXN4Y2")
.to_return(status: 200, body: onebox_response("amazon-og"))
end
describe "#to_html" do
it "includes image" do
expect(html).to include("https://images-na.ssl-images-amazon.com/images/I/51nOF2iBa6L._SX940_.jpg")
end
it "includes description" do
expect(html).to include("CHRISTINE is the story of an aspiring newswoman caught in the midst of a personal and professional life crisis. Between unrequited love, frustration at work, a tumultuous home, and self-doubt; she begins to spiral down a dark path.")
end
it "includes title" do
expect(html).to include("Watch Christine online - Amazon Video")
end
end
end
context "amazon book page" do
let(:link) { "https://www.amazon.com/dp/B00AYQNR46" }
let(:html) { described_class.new(link).to_html }
before do
stub_request(:get, "https://www.amazon.com/dp/B00AYQNR46")
.to_return(status: 200, body: onebox_response("amazon"))
stub_request(:get, "https://www.amazon.com/Seven-Languages-Weeks-Programming-Programmers/dp/193435659X")
.to_return(status: 200, body: onebox_response("amazon"))
end
describe "#to_html" do
it "includes title and author" do
expect(html).to include("Seven Languages in Seven Weeks: A Pragmatic Guide to Learning Programming Languages (Pragmatic Programmers)")
expect(html).to include("Bruce Tate")
end
it "includes ISBN" do
expect(html).to include("978-1934356593")
end
it "includes publisher" do
expect(html).to include("Pragmatic Bookshelf")
end
end
end
context "amazon ebook page" do
let(:link) { "https://www.amazon.com/dp/193435659X" }
let(:html) { described_class.new(link).to_html }
before do
stub_request(:get, "https://www.amazon.com/dp/193435659X")
.to_return(status: 200, body: onebox_response("amazon-ebook"))
stub_request(:get, "https://www.amazon.com/Seven-Languages-Weeks-Programming-Programmers-ebook/dp/B00AYQNR46")
.to_return(status: 200, body: onebox_response("amazon-ebook"))
end
describe "#to_html" do
it "includes title and author" do
expect(html).to include("Seven Languages in Seven Weeks: A Pragmatic Guide to Learning Programming Languages (Pragmatic Programmers)")
expect(html).to include("Bruce Tate")
end
it "includes image" do
expect(html).to include("https://images-na.ssl-images-amazon.com/images/I/51LZT%2BtSrTL._SX133_.jpg")
end
it "includes ASIN" do
expect(html).to include("B00AYQNR46")
end
it "includes rating" do
expect(html).to include("4.2 out of 5 stars")
end
it "includes publisher" do
expect(html).to include("Pragmatic Bookshelf")
end
end
end
end

View File

@ -0,0 +1,37 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::AudioOnebox do
it "supports ogg" do
expect(Onebox.preview('http://upload.wikimedia.org/wikipedia/commons/c/c8/Example.ogg').to_s).to match(/<audio/)
end
it "supports mp3" do
expect(Onebox.preview('http://kolber.github.io/audiojs/demos/mp3/juicy.MP3').to_s).to match(/<audio/)
end
it "supports wav" do
expect(Onebox.preview('http://download.wavetlan.com/SVV/Media/HTTP/sample14.wav').to_s).to match(/<audio/)
end
it "supports m4a" do
expect(Onebox.preview('http://techslides.com/demos/samples/sample.m4a').to_s).to match(/<audio/)
end
it "supports URLs with query parameters" do
expect(Onebox.preview('https://upload.wikimedia.org/wikipedia/commons/c/c8/Example.ogg?foo=bar').to_s).to match(/<audio/)
end
it "supports protocol relative URLs" do
expect(Onebox.preview('//upload.wikimedia.org/wikipedia/commons/c/c8/Example.ogg').to_s).to match(/<audio/)
end
it "includes a fallback direct link to the audio" do
expect(Onebox.preview('http://kolber.github.io/audiojs/demos/mp3/juicy.mp3').to_s).to match(/<a.*mp3/)
end
it "respects the disable_media_downloads option" do
expect(Onebox.preview('http://kolber.github.io/audiojs/demos/mp3/juicy.MP3', disable_media_download_controls: true).to_s).to include("controlslist=\"nodownload\"")
end
end

View File

@ -0,0 +1,28 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::CloudAppOnebox do
before do
stub_request(:get, "https://cl.ly/0m2a2u2k440O").to_return(status: 200, body: onebox_response("cloudapp-gif"))
stub_request(:get, "https://cl.ly/0T0c2J3S373X").to_return(status: 200, body: onebox_response("cloudapp-mp4"))
stub_request(:get, "https://cl.ly/2C0E1V451J0C").to_return(status: 200, body: onebox_response("cloudapp-jpg"))
stub_request(:get, "https://cl.ly/1x1f2g253l03").to_return(status: 200, body: onebox_response("cloudapp-others"))
end
it "supports gif" do
expect(Onebox.preview('https://cl.ly/0m2a2u2k440O').to_s).to match(/<img/)
end
it "supports mp4" do
expect(Onebox.preview('https://cl.ly/0T0c2J3S373X').to_s).to match(/<video/)
end
it "supports jpg" do
expect(Onebox.preview('https://cl.ly/2C0E1V451J0C').to_s).to match(/<img/)
end
it "links to other formats" do
expect(Onebox.preview('https://cl.ly/1x1f2g253l03').to_s).to match(/<a/)
end
end

View File

@ -0,0 +1,39 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::GfycatOnebox do
let(:link) { "https://gfycat.com/shrillnegativearrowana" }
let(:html) { described_class.new(link).to_html }
let(:placeholder_html) { described_class.new(link).placeholder_html }
before do
stub_request(:get, link).to_return(status: 200, body: onebox_response("gfycat"))
end
it "has the title" do
expect(html).to include("shrillnegativearrowana")
expect(placeholder_html).to include("shrillnegativearrowana")
end
it "has the link" do
expect(html).to include(link)
expect(placeholder_html).to include(link)
end
it "has the poster" do
expect(html).to include("https://thumbs.gfycat.com/ShrillNegativeArrowana-poster.jpg")
end
it "has the webm video" do
expect(html).to include("https://giant.gfycat.com/ShrillNegativeArrowana.webm")
end
it "has the mp4 video" do
expect(html).to include("https://giant.gfycat.com/ShrillNegativeArrowana.mp4")
end
it "has keywords" do
expect(html).to include("<a href='https://gfycat.com/gifs/search/lego'>#lego</a>")
end
end

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::GithubBlobOnebox do
before do
@link = "https://github.com/discourse/onebox/blob/master/lib/onebox/engine/github_blob_onebox.rb"
stub_request(:get, "https://raw.githubusercontent.com/discourse/onebox/master/lib/onebox/engine/github_blob_onebox.rb")
.to_return(status: 200, body: onebox_response(described_class.onebox_name))
end
include_context "engines"
it_behaves_like "an engine"
describe "#to_html" do
it "includes file name" do
expect(html).to include("github_blob_onebox.rb")
end
it "includes blob contents" do
expect(html).to include("module Oneboxer")
end
end
end

View File

@ -0,0 +1,115 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::GithubCommitOnebox do
describe "regular commit url" do
before do
@link = "https://github.com/discourse/discourse/commit/803d023e2307309f8b776ab3b8b7e38ba91c0919"
@uri = "https://api.github.com/repos/discourse/discourse/commits/803d023e2307309f8b776ab3b8b7e38ba91c0919"
stub_request(:get, @uri).to_return(status: 200, body: onebox_response("githubcommit"))
end
include_context "engines"
it_behaves_like "an engine"
describe "#to_html" do
it "includes owner" do
expect(html).to include("discourse")
end
it "includes repository name" do
expect(html).to include("discourse")
end
it "includes commit sha" do
expect(html).to include("803d023e2307309f8b776ab3b8b7e38ba91c0919")
end
it "includes commit author gravatar" do
expect(html).to include("2F7d3010c11d08cf990b7614d2c2ca9098.png")
end
it "includes commit message" do
expect(html).to include("Fixed GitHub auth")
end
it "includes commit author" do
expect(html).to include("SamSaffron")
end
it "includes commit time and date" do
expect(html).to include("02:03AM - 02 Aug 13")
end
it "includes number of files changed" do
expect(html).to include("1 file")
end
it "includes number of additions" do
expect(html).to include("18 additions")
end
it "includes number of deletions" do
expect(html).to include("2 deletions")
end
end
end
describe "PR with commit URL" do
before do
@link = "https://github.com/discourse/discourse/pull/4662/commit/803d023e2307309f8b776ab3b8b7e38ba91c0919"
@uri = "https://api.github.com/repos/discourse/discourse/commit/803d023e2307309f8b776ab3b8b7e38ba91c0919"
stub_request(:get, "https://api.github.com/repos/discourse/discourse/commits/803d023e2307309f8b776ab3b8b7e38ba91c0919")
.to_return(status: 200, body: onebox_response("githubcommit"))
end
include_context "engines"
# TODO: fix test to make sure it's not failing when matching object
# it_behaves_like "an engine"
describe "#to_html" do
it "includes owner" do
expect(html).to include("discourse")
end
it "includes repository name" do
expect(html).to include("discourse")
end
it "includes commit sha" do
expect(html).to include("803d023e2307309f8b776ab3b8b7e38ba91c0919")
end
it "includes commit author gravatar" do
expect(html).to include("2F7d3010c11d08cf990b7614d2c2ca9098.png")
end
it "includes commit message" do
expect(html).to include("Fixed GitHub auth")
end
it "includes commit author" do
expect(html).to include("SamSaffron")
end
it "includes commit time and date" do
expect(html).to include("02:03AM - 02 Aug 13")
end
it "includes number of files changed" do
expect(html).to include("1 file")
end
it "includes number of additions" do
expect(html).to include("18 additions")
end
it "includes number of deletions" do
expect(html).to include("2 deletions")
end
end
end
end

View File

@ -0,0 +1,44 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::GithubFolderOnebox do
context 'without fragments' do
before do
@link = "https://github.com/discourse/discourse/tree/master/spec/fixtures"
@uri = "https://github.com/discourse/discourse/tree/master/spec/fixtures"
stub_request(:get, @uri).to_return(status: 200, body: onebox_response(described_class.onebox_name))
end
include_context "engines"
it_behaves_like "an engine"
describe "#to_html" do
it "includes link to folder with truncated display path" do
expect(html).to include("<a href=\"https://github.com/discourse/discourse/tree/master/spec/fixtures\" target=\"_blank\" rel=\"noopener\">master/spec/fixtures</a>")
end
it "includes repository name" do
expect(html).to include("discourse/discourse")
end
it "includes logo" do
expect(html).to include("")
end
end
end
context 'with fragments' do
before do
@link = "https://github.com/discourse/discourse#setting-up-discourse"
@uri = "https://github.com/discourse/discourse"
stub_request(:get, @uri).to_return(status: 200, body: onebox_response("githubfolder-discourse-root"))
@onebox = described_class.new(@link)
end
it "extracts subtitles when linking to docs" do
expect(@onebox.to_html).to include("<a href=\"https://github.com/discourse/discourse#setting-up-discourse\" target=\"_blank\" rel=\"noopener\">discourse/discourse - Setting up Discourse</a>")
end
end
end

View File

@ -0,0 +1,72 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::GithubGistOnebox do
before do
@link = "https://gist.github.com/karreiro/208fdd59fc4b4c39283b"
stub_request(:get, "https://api.github.com/gists/208fdd59fc4b4c39283b")
.to_return(status: 200, body: onebox_response(described_class.onebox_name))
end
include_context "engines"
it_behaves_like "an engine"
describe "#data" do
let(:gist_files) { data[:gist_files] }
it 'includes contents with 10 lines at most' do
gist_files.each do |gist_file|
truncated_lines = gist_file.content.split("\n").size
expect(truncated_lines).to be < 10
end
end
end
describe "#to_html" do
describe 'when Gist API responds correctly' do
it "includes the link to original page" do
expect(html).to include("https://gist.github.com/karreiro/208fdd59fc4b4c39283b")
end
it "includes three files" do
expect(html).to include("0.rb")
expect(html).to include("1.js")
expect(html).to include("2.md")
end
it "does not include truncated files" do
expect(html).not_to include("3.java")
end
it "includes gist contents" do
expect(html).to include("3.times { puts &quot;Gist API test.&quot; }")
expect(html).to include("console.log(&quot;Hey! ;)&quot;)")
expect(html).to include("#### Hey, this is a test!")
end
it "does not include gist contents from truncated files" do
expect(html).not_to include("System.out.println(&quot;Wow! This is a test!&quot;);")
end
end
describe 'when the rate limit has been reached' do
before do
stub_request(:get, "https://api.github.com/gists/208fdd59fc4b4c39283b")
.to_return(status: 403)
end
it "includes the link to original page" do
expect(html).to include("https://gist.github.com/karreiro/208fdd59fc4b4c39283b")
end
it "does not include any file" do
expect(html).not_to include("0.rb")
expect(html).not_to include("1.js")
expect(html).not_to include("2.md")
expect(html).not_to include("3.java")
end
end
end
end

View File

@ -0,0 +1,53 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::GithubPullRequestOnebox do
before do
@link = "https://github.com/discourse/discourse/pull/1253/"
@uri = "https://api.github.com/repos/discourse/discourse/pulls/1253"
stub_request(:get, @uri).to_return(status: 200, body: onebox_response(described_class.onebox_name))
end
include_context "engines"
it_behaves_like "an engine"
describe "#to_html" do
it "includes pull request author" do
expect(html).to include("jamesaanderson")
end
it "includes repository name" do
expect(html).to include("discourse")
end
it "includes commit author gravatar" do
expect(html).to include("b3e9977094ce189bbb493cf7f9adea21")
end
it "includes commit time and date" do
expect(html).to include("02:05AM - 26 Jul 13")
end
it "includes number of commits" do
expect(html).to include("1")
end
it "includes number of files changed" do
expect(html).to include("4")
end
it "includes number of additions" do
expect(html).to include("19")
end
it "includes number of deletions" do
expect(html).to include("1")
end
it "includes the body" do
expect(html).to include("http://meta.discourse.org/t/audio-html5-tag/8168")
end
end
end

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::GitlabBlobOnebox do
before do
@link = "https://gitlab.com/discourse/onebox/blob/master/lib/onebox/engine/gitlab_blob_onebox.rb"
stub_request(:get, "https://gitlab.com/discourse/onebox/raw/master/lib/onebox/engine/gitlab_blob_onebox.rb")
.to_return(status: 200, body: onebox_response(described_class.onebox_name))
end
include_context "engines"
it_behaves_like "an engine"
describe "#to_html" do
it "includes file name" do
expect(html).to include("gitlab_blob_onebox.rb")
end
it "includes blob contents" do
expect(html).to include("module Onebox")
end
end
end

View File

@ -0,0 +1,28 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::GoogleDocsOnebox do
before do
@link = "https://docs.google.com/document/d/DOC_KEY/pub"
stub_request(:get, @link).to_return(status: 200, body: onebox_response("googledocs"))
end
include_context "engines"
it_behaves_like "an engine"
describe "#to_html" do
it "has title" do
expect(html).to include("Lorem Ipsum!")
end
it "has description" do
expect(html).to include("Lorem Ipsum Lorem ipsum dolor sit amet, consectetur adipiscing elit")
end
it "has icon" do
expect(html).to include("googledocs-onebox-logo g-docs-logo")
end
end
end

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::GoogleDriveOnebox do
let(:link) { "https://drive.google.com/file/d/1FgMt06wENEUfC6_-1tImXaNCH7vM9QsA/view" }
let(:html) { described_class.new(link).to_html }
before do
stub_request(:get, link).to_return(status: 200, body: onebox_response("googledrive"))
end
it "includes title" do
expect(html).to include('<a href="https://drive.google.com/file/d/1FgMt06wENEUfC6_-1tImXaNCH7vM9QsA/view" target="_blank" rel="noopener">test.txt</a>')
end
it "includes image" do
expect(html).to include("https://lh5.googleusercontent.com/wcDbcSFKB3agLf0963iFPqwy96OE2s7of1pAEbEOpg-38yS_m7u8VHKezWQ=w1200-h630-p")
end
it "includes description" do
expect(html).to include("Awesome description here! 😎")
end
end

View File

@ -0,0 +1,74 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::GoogleMapsOnebox do
URLS = {
short: {
test: "https://goo.gl/maps/rEG3D",
redirect: [302, :long],
expect: "https://maps.google.de/maps?sll=40.689249,-74.0445&sspn=0.0062479,0.0109864&cid=4667599994556318251&q=Statue+of+Liberty+National+Monument&output=embed&dg=ntvb&ll=40.689249,-74.0445&spn=0.0062479,0.0109864",
},
long: {
test: "https://www.google.de/maps/place/Statue+of+Liberty+National+Monument/@40.689249,-74.0445,17z/data=!3m1!4b1!4m2!3m1!1s0x89c25090129c363d:0x40c6a5770d25022b",
redirect: [301, :canonical],
expect: "https://maps.google.de/maps?sll=40.689249,-74.0445&sspn=0.0062479,0.0109864&cid=4667599994556318251&q=Statue+of+Liberty+National+Monument&output=embed&dg=ntvb&ll=40.689249,-74.0445&spn=0.0062479,0.0109864",
},
canonical: {
test: "https://maps.google.de/maps?sll=40.689249,-74.0445&sspn=0.0062479,0.0109864&cid=4667599994556318251&q=Statue+of+Liberty+National+Monument&output=classic&dg=ntvb",
expect: "https://maps.google.de/maps?sll=40.689249,-74.0445&sspn=0.0062479,0.0109864&cid=4667599994556318251&q=Statue+of+Liberty+National+Monument&output=embed&dg=ntvb&ll=40.689249,-74.0445&spn=0.0062479,0.0109864",
},
custom: {
test: "https://www.google.com/maps/d/edit?mid=zPYyZFrHi1MU.kX85W_Y2y2_E",
expect: "https://www.google.com/maps/d/embed?mid=zPYyZFrHi1MU.kX85W_Y2y2_E",
},
streetview: {
test: "https://www.google.com/maps/@46.414384,10.013947,3a,75y,232.83h,99.08t/data=!3m5!1e1!3m3!1s9WgYUb5quXDjqqFd3DWI6A!2e0!3e5?hl=de",
expect: "https://www.google.com/maps/embed?pb=!3m2!2sen!4v0!6m8!1m7!1s9WgYUb5quXDjqqFd3DWI6A!2m2!1d46.414384!2d10.013947!3f232.83!4f9.908!5f0.75",
streetview: true
},
unresolveable: {
test: "https://www.google.com/maps/place/Den+Abattoir/@51.2285173,4.4336702,17z/data=!4m7!1m4!3m3!1s0x47c3f7a5ac48e237:0x63d716018f584a33!2zUGnDqXRyYWlu!3b1!3m1!1s0x0000000000000000:0xfbfac0c41c32471a",
redirect: [302, "https://www.google.com/maps/place/Den+Abattoir/@51.2285173,4.4336702,17z/data=!4m7!1m4!3m3!1s0x47c3f7a5ac48e237:0x63d716018f584a33!2zUGnDqXRyYWlu!3b1!3m1!1s0x0000000000000000:0xfbfac0c41c32471a?dg=dbrw&newdg=1"],
expect: "https://maps.google.com/maps?ll=51.2285173,4.4336702&z=17&output=embed&dg=ntvb&q=Den+Abattoir&cid=18157036796216755994"
},
satellite: {
test: "https://www.google.de/maps/@40.6894264,-74.0449146,758m/data=!3m1!1e3",
redirect: [302, "https://www.google.de/maps/@40.6894264,-74.0449146,758m/data=!3m1!1e3?dg=dbrw&newdg=1"],
expect: "https://maps.google.com/maps?ll=40.6894264,-74.0449146&z=16&output=embed&dg=ntvb"
}
}
# Register URL redirects
before do
URLS.values.each do |t|
status, location = *t[:redirect]
location = URLS[location][:test] if location.is_a? Symbol
stub_request(:head, t[:test])
.to_return(status: status, headers: { location: location })
end
end
# Prevent sleep from wasting our time when we test with strange redirects
subject do
described_class.send(:allocate).tap do |obj|
obj.stubs(:sleep)
obj.send(:initialize, link)
end
end
let(:data) { Onebox::Helpers.symbolize_keys(subject.send(:data)) }
let(:link) { |example| URLS[example.metadata[:urltype] || :short][:test] }
include_context "an engine", urltype: :short
URLS.each do |kind, t|
it "processes #{kind.to_s} url correctly", urltype: kind do
expect(subject.url).to eq t[:expect]
expect(subject.streetview?).to t[:streetview] ? be_truthy : be_falsey
expect(subject.to_html).to include("<iframe")
expect(subject.placeholder_html).to include("placeholder-icon map")
end
end
end

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::GooglePhotosOnebox do
let(:link) { "https://photos.app.goo.gl/pXA7T8zBX4WZWVMT7" }
let(:html) { described_class.new(link).to_html }
before do
stub_request(:get, link).to_return(status: 200, body: onebox_response("googlephotos"))
stub_request(:get, "https://photos.google.com/share/AF1QipOV3gcu_edA8lyjJEpS9sC1g3AeCUtaZox11ylYZId7wJ7cthZ8M1kZXeAp5vhEPg?key=QktmUFNvdWpNVktERU5zWmVRZlZubzRRc0ttWWN3")
.to_return(status: 200, body: onebox_response("googlephotos"))
end
it "includes album title" do
expect(html).to include("[3 new photos · Album by Arpit Jalan] Mesmerizing Singapore")
end
it "includes album poster image" do
expect(html).to include("https://lh3.googleusercontent.com/ZlYoleNnrVo8qdx0qEjKi_-_VXY7pqqCqIW-B88EMqJ0etibFw1kEu4bzo-T4jyOQ9Ey2ekADim_L3re4lT3aBmYJUwhjkEUb5Yk59YaCSy2R8AoME5Rx4wviDRgICllF8g6lsZnS8c=w600-h315-p-k")
end
end

View File

@ -0,0 +1,33 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::GooglePlayAppOnebox do
before do
@link = "https://play.google.com/store/apps/details?id=com.hulu.plus&hl=en"
stub_request(:get, "https://play.google.com/store/apps/details?id=com.hulu.plus&hl=en")
.to_return(status: 200, body: onebox_response("googleplayapp"))
end
include_context "engines"
it_behaves_like "an engine"
describe "#to_html" do
it "has title" do
expect(html).to include("Hulu: Stream TV, Movies &amp; more")
end
it "has image" do
expect(html).to include("4iScc4heC5Cog-i-es2hIYe0RuewYTkGiJfHAaXv0Kb2Q5b2qpbYWxWiooAPuUEhpg")
end
it "has description" do
expect(html).to include("Enjoy all your TV in one place with a new Hulu experience")
end
it "has price" do
expect(html).to include("Free")
end
end
end

View File

@ -0,0 +1,30 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::HTML do
before do
@link = "http://amazon.com"
stub_request(:get, @link).to_return(status: 200, body: onebox_response("amazon"))
stub_request(:get, "https://www.amazon.com/Seven-Languages-Weeks-Programming-Programmers/dp/193435659X")
.to_return(status: 200, body: onebox_response("amazon"))
end
describe "#raw" do
class OneboxEngineHTML
include Onebox::Engine
include Onebox::Engine::HTML
def initialize(link)
@url = link
@options = {}
end
end
it "returns a Nokogiri object that has a css method" do
object = OneboxEngineHTML.new("http://amazon.com").send(:raw)
expect(object).to respond_to(:css)
end
end
end

View File

@ -0,0 +1,41 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::ImageOnebox do
it "supports png" do
expect(Onebox.preview('http://www.discourse.org/images/logo.png').to_s).to match(/<img/)
end
it "supports jpg" do
expect(Onebox.preview('http://upload.wikimedia.org/wikipedia/en/a/a9/Example.jpg').to_s).to match(/<img/)
end
it "supports jpeg" do
expect(Onebox.preview('http://upload.wikimedia.org/wikipedia/en/b/bb/Poster.jpeg').to_s).to match(/<img/)
end
it "supports gif" do
expect(Onebox.preview('http://upload.wikimedia.org/wikipedia/commons/5/55/Tesseract.gif').to_s).to match(/<img/)
end
it "supports tif" do
expect(Onebox.preview('http://www.fileformat.info/format/tiff/sample/1f37bbd5603048178487ec88b1a6425b/MARBLES.tif').to_s).to match(/<img/)
end
it "supports bmp" do
expect(Onebox.preview('http://www.fileformat.info/format/bmp/sample/d4202a5fc22a48c388d9e1c636792cc6/LAND.BMP').to_s).to match(/<img/)
end
it "supports image URLs with query parameters" do
expect(Onebox.preview('https://www.google.com/logos/doodles/2014/percy-julians-115th-birthday-born-1899-5688801926053888-hp.jpg?foo=bar').to_s).to match(/<img/)
end
it "supports protocol relative image URLs" do
expect(Onebox.preview('//www.google.com/logos/doodles/2014/percy-julians-115th-birthday-born-1899-5688801926053888-hp.jpg').to_s).to match(/<img/)
end
it "includes a direct link to the image" do
expect(Onebox.preview('http://www.discourse.org/images/logo.png').to_s).to match(/<a.*png/)
end
end

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::ImgurOnebox do
let(:link) { "https://imgur.com/gallery/Sdc0Klc" }
let(:imgur) { described_class.new(link) }
let(:html) { imgur.to_html }
before do
stub_request(:get, link).to_return(status: 200, body: onebox_response("imgur"))
end
it "excludes html tags in title" do
imgur.stubs(:is_album?).returns(true)
expect(html).to include("<span class='album-title'>[Album] Did you miss me?</span>")
end
end

View File

@ -0,0 +1,75 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::InstagramOnebox do
let(:access_token) { 'abc123' }
let(:link) { "https://www.instagram.com/p/CARbvuYDm3Q" }
it 'oneboxes links that include the username' do
link_with_profile = 'https://www.instagram.com/bennyblood24/p/CARbvuYDm3Q/'
onebox_klass = Onebox::Matcher.new(link_with_profile).oneboxed
expect(onebox_klass.name).to eq(described_class.name)
end
it 'oneboxes photo links' do
photo_link = 'https://www.instagram.com/p/CARbvuYDm3Q/'
onebox_klass = Onebox::Matcher.new(photo_link).oneboxed
expect(onebox_klass.name).to eq(described_class.name)
end
it 'oneboxes tv links' do
tv_link = "https://www.instagram.com/tv/CIlM7UzMgXO/?hl=en"
onebox_klass = Onebox::Matcher.new(tv_link).oneboxed
expect(onebox_klass.name).to eq(described_class.name)
end
context 'with access token' do
let(:api_link) { "https://graph.facebook.com/v9.0/instagram_oembed?url=#{link}&access_token=#{access_token}" }
before do
stub_request(:get, api_link).to_return(status: 200, body: onebox_response("instagram"))
stub_request(:get, "https://api.instagram.com/oembed/?url=https://www.instagram.com/p/CARbvuYDm3Q")
.to_return(status: 200, body: onebox_response("instagram"))
end
around do |example|
previous_options = Onebox.options.to_h
example.run
Onebox.options = previous_options
end
it "includes title" do
Onebox.options = { facebook_app_access_token: access_token }
onebox = described_class.new(link)
html = onebox.to_html
expect(html).to include('<a href="https://www.instagram.com/p/CARbvuYDm3Q" target="_blank" rel="noopener">@natgeo</a>')
end
it "includes image" do
Onebox.options = { facebook_app_access_token: access_token }
onebox = described_class.new(link)
html = onebox.to_html
expect(html).to include("https://scontent.cdninstagram.com/v/t51.2885-15/sh0.08/e35/s640x640/97565241_163250548553285_9172168193050746487_n.jpg")
end
end
context 'without access token' do
let(:api_link) { "https://api.instagram.com/oembed/?url=#{link}" }
let(:html) { described_class.new(link).to_html }
before do
stub_request(:get, api_link).to_return(status: 200, body: onebox_response("instagram_old_onebox"))
end
it "includes title" do
expect(html).to include('<a href="https://www.instagram.com/p/CARbvuYDm3Q" target="_blank" rel="noopener">@natgeo</a>')
end
it "includes image" do
expect(html).to include("https://scontent-yyz1-1.cdninstagram.com/v/t51.2885-15/sh0.08/e35/s640x640/97565241_163250548553285_9172168193050746487_n.jpg")
end
end
end

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::JSON do
before do
@link = "http://stackoverflow.com"
stub_request(:get, @link).to_return(status: 200, body: onebox_response("stackexchange-question"))
end
describe "#raw" do
class OneboxEngineJSON
include Onebox::Engine
include Onebox::Engine::JSON
def initialize(link)
@url = link
@options = {}
end
end
it "returns a hash" do
object = OneboxEngineJSON.new(@link).send(:raw)
expect(object).to be_a(Hash)
end
end
end

View File

@ -0,0 +1,58 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::KalturaOnebox do
let(:link) { 'https://videos.kaltura.com/id/0_e2ea6ygt' }
before do
stub_request(:get, link).to_return(status: 200, body: onebox_response('kaltura'))
end
shared_examples 'Pulling the width and height from OpenGraph' do
it 'Pulls the width from the OG video width' do
og_video_width = '534'
actual_width = inspect_html_fragment(html, tag_name, 'width')
expect(actual_width).to eq og_video_width
end
it 'Pulls the height from the OG video height' do
og_video_height = '300'
actual_height = inspect_html_fragment(html, tag_name, 'height')
expect(actual_height).to eq og_video_height
end
end
describe '#to_html' do
let(:html) { described_class.new(link).to_html }
let(:tag_name) { 'iframe' }
it_behaves_like 'Pulling the width and height from OpenGraph'
it 'Pulls the src from the OG view secure url' do
og_video_secure_url = 'https://cdnapisec.kaltura.com//p/811441/sp/81144100/embedIframeJs/uiconf_id/40430081/' \
'partner_id/811441?iframeembed=true&playerId=kaltura_playe' \
'r&entry_id=0_e2ea6ygt&widget_id=1_c6hc9mf5'
actual_src = inspect_html_fragment(html, tag_name, 'src')
expect(actual_src).to eq og_video_secure_url
end
end
describe '#placeholder_html' do
let(:html) { described_class.new(link).preview_html }
let(:tag_name) { 'img' }
it_behaves_like 'Pulling the width and height from OpenGraph'
it 'Pulls the thumbnail from the OG video thumbnail' do
og_video_thumbnail = 'https://cdnapisec.kaltura.com/p/811441/sp/81144100/thumbnail/entry_id/0_e2ea6ygt/width/534'
actual_thumbnail = inspect_html_fragment(html, tag_name, 'src')
expect(actual_thumbnail).to eq og_video_thumbnail
end
end
end

View File

@ -0,0 +1,30 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::PdfOnebox do
let(:link) { "https://acrobatusers.com/assets/uploads/public_downloads/2217/adobe-acrobat-xi-merge-pdf-files-tutorial-ue.pdf" }
let(:html) { described_class.new(link).to_html }
let(:no_content_length_link) { "https://dspace.lboro.ac.uk/dspace-jspui/bitstream/2134/14294/3/greiffenhagen-ca_and_consumption.pdf" }
let(:no_filesize_html) { described_class.new(no_content_length_link).to_html }
before do
stub_request(:head, link).to_return(status: 200, headers: { "Content-Length" => "335562" })
stub_request(:head, no_content_length_link).to_return(status: 200)
end
describe "#to_html" do
it "includes filename" do
expect(html).to include("adobe-acrobat-xi-merge-pdf-files-tutorial-ue.pdf")
end
it "includes filesize" do
expect(html).to include("327.70 KB")
end
it "doesn’t include filesize when unknown" do
expect(no_filesize_html).to_not include("<p class='filesize'>")
end
end
end

View File

@ -0,0 +1,78 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::PubmedOnebox do
let(:link) { "http://www.ncbi.nlm.nih.gov/pubmed/7288891" }
let(:xml_link) { "http://www.ncbi.nlm.nih.gov/pubmed/7288891?report=xml&format=text" }
let(:html) { described_class.new(link).to_html }
before do
stub_request(:get, link).to_return(status: 200, body: onebox_response("pubmed"))
stub_request(:get, xml_link).to_return(status: 200, body: onebox_response("pubmed-xml"))
end
it "has the paper's title" do
expect(html).to include("Evolutionary trees from DNA sequences: a maximum likelihood approach.")
end
it "has the paper's author" do
expect(html).to include("Felsenstein")
end
it "has the paper's abstract" do
expect(html).to include("The application of maximum likelihood techniques to the estimation of evolutionary trees from nucleic acid sequence data is discussed.") end
it "has the paper's date" do
expect(html).to include("1981")
end
it "has the URL to the resource" do
expect(html).to include(link)
end
context "Pubmed electronic print" do
let(:link) { "http://www.ncbi.nlm.nih.gov/pubmed/24737116" }
let(:xml_link) { "http://www.ncbi.nlm.nih.gov/pubmed/24737116?report=xml&format=text" }
let(:html) { described_class.new(link).to_html }
before do
stub_request(:get, link).to_return(status: 200, body: onebox_response("pubmed-electronic"))
stub_request(:get, xml_link).to_return(status: 200, body: onebox_response("pubmed-electronic-xml"))
end
it "has the paper's title" do
expect(html).to include("Cushingoid facies on (18)F-FDG PET/CT.")
end
it "has the paper's author" do
expect(html).to include("van Rheenen")
end
it "has the paper's date" do
expect(html).to include("Jul 2014")
end
it "has the URL to the resource" do
expect(html).to include(link)
end
end
context "regex URI match" do
it "matches on specific articles" do
expect(match("http://www.ncbi.nlm.nih.gov/pubmed/7288891")).to eq true
end
it "does not match on search" do
expect(match("http://www.ncbi.nlm.nih.gov/pubmed/?term=rheenen+r")).to eq false
end
it "does not match on the root" do
expect(match("http://www.ncbi.nlm.nih.gov/pubmed/")).to eq false
end
def match(url)
Onebox::Engine::PubmedOnebox === URI(url)
end
end
end

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::RedditMediaOnebox do
let(:link) { "https://www.reddit.com/r/colors/comments/b4d5xm/literally_nothing_black_edition" }
let(:html) { described_class.new(link).to_html }
before do
stub_request(:get, link).to_return(status: 200, body: onebox_response("reddit_image"))
stub_request(:get, "#{link}/").to_return(status: 200, body: onebox_response("reddit_image"))
end
it "includes title" do
expect(html).to include('<a href="https://www.reddit.com/r/colors/comments/b4d5xm/literally_nothing_black_edition/" target="_blank" rel="nofollow ugc noopener">reddit</a>')
end
it "includes image" do
expect(html).to include("https://preview.redd.it/vsg59iw0srn21.jpg")
end
it "includes description" do
expect(html).to include("Literally nothing black edition")
end
end

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::SlidesOnebox do
let(:link) { "http://slides.com/drksephy/ecmascript-2015" }
let(:html) { described_class.new(link).to_html }
before do
stub_request(:get, link).to_return(status: 200, body: onebox_response("slides"))
end
describe "#placeholder_html" do
it "returns an image as the placeholder" do
expect(Onebox.preview(link).placeholder_html).to include("//s3.amazonaws.com/media-p.slid.es/thumbnails/secure/cff7c3/decks.jpg")
end
end
describe "#to_html" do
it "returns iframe embed" do
expect(html).to include(URI(link).path)
expect(html).to include("iframe")
end
end
end

View File

@ -0,0 +1,103 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::StackExchangeOnebox do
describe 'domains' do
[
'stackoverflow.com', 'meta.stackoverflow.com',
'superuser.com', 'meta.superuser.com',
'serverfault.com', 'meta.serverfault.com',
'askubuntu.com', 'meta.askubuntu.com',
'mathoverflow.net', 'meta.mathoverflow.net',
'money.stackexchange.com', 'meta.money.stackexchange.com',
'stackapps.com'
].each do |domain|
it "matches question with short URL on #{domain}" do
expect(described_class === URI("http://#{domain}/q/55495")).to eq(true)
end
it "matches question with long URL on #{domain}" do
expect(described_class === URI("http://#{domain}/questions/55495/title-of-question")).to eq(true)
end
it "matches answer with short URL on #{domain}" do
expect(described_class === URI("http://#{domain}/a/55503")).to eq(true)
end
it "matches question with long URL on #{domain}" do
expect(described_class === URI("http://#{domain}/questions/55495/title-of-question/55503#55503")).to eq(true)
end
end
it "doesn't match question on example.com" do
expect(described_class === URI('http://example.com/q/4711')).to eq(false)
end
it "doesn't match answer on example.com" do
expect(described_class === URI('http://example.com/a/4711')).to eq(false)
end
end
{
'long URL' => 'http://stackoverflow.com/questions/17992553/concept-behind-these-four-lines-of-tricky-c-code',
'short URL' => 'http://stackoverflow.com/q/17992553'
}.each do |name, url|
describe "question with #{name}" do
before do
@link = url
stub_request(:get, 'https://api.stackexchange.com/2.2/questions/17992553?site=stackoverflow.com&filter=!5-duuxrJa-iw9oVvOA(JNimB5VIisYwZgwcfNI')
.to_return(status: 200, body: onebox_response('stackexchange-question'))
end
include_context 'engines'
it_behaves_like 'an engine'
describe '#to_html' do
it 'includes question title' do
expect(html).to include('Concept behind these four lines of tricky C code')
end
it "includes 'asked by'" do
expect(html).to include('asked by')
end
it "doesn't include 'answered by'" do
expect(html).not_to include('answered by')
end
end
end
end
{
'long URL' => 'http://stackoverflow.com/questions/17992553/concept-behind-these-four-lines-of-tricky-c-code/17992906#17992906',
'short URL' => 'http://stackoverflow.com/a/17992906'
}.each do |name, url|
describe "answer with #{name}" do
before do
@link = url
stub_request(:get, 'https://api.stackexchange.com/2.2/answers/17992906?site=stackoverflow.com&filter=!.FjueITQdx6-Rq3Ue9PWG.QZ2WNdW')
.to_return(status: 200, body: onebox_response('stackexchange-answer'))
end
include_context 'engines'
it_behaves_like 'an engine'
describe '#to_html' do
it 'includes question title' do
expect(html).to include('Concept behind these four lines of tricky C code')
end
it "includes 'answered by'" do
expect(html).to include('answered by')
end
it "doesn't include 'asked by'" do
expect(html).not_to include('asked by')
end
end
end
end
end

View File

@ -0,0 +1,31 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::TrelloOnebox do
context "Boards" do
it "should onebox with SEF url corrrectly" do
expect(Onebox.preview('https://trello.com/b/nC8QJJoZ/trello-development-roadmap').to_s).to match('iframe src="https://trello.com/b/nC8QJJoZ.html"')
end
it "should onebox without SEF url corrrectly" do
expect(Onebox.preview('https://trello.com/b/nC8QJJoZ/').to_s).to match('iframe src="https://trello.com/b/nC8QJJoZ.html"')
# Without trailing slash
expect(Onebox.preview('https://trello.com/b/nC8QJJoZ').to_s).to match('iframe src="https://trello.com/b/nC8QJJoZ.html"')
end
end
context "Cards" do
it "should onebox with SEF url corrrectly" do
expect(Onebox.preview('https://trello.com/c/NIRpzVDM/1211-what-can-you-expect-from-this-board').to_s).to match('iframe src="https://trello.com/c/NIRpzVDM.html"')
end
it "should onebox without SEF url corrrectly" do
expect(Onebox.preview('https://trello.com/c/NIRpzVDM/').to_s).to match('iframe src="https://trello.com/c/NIRpzVDM.html"')
# Without trailing slash
expect(Onebox.preview('https://trello.com/c/NIRpzVDM').to_s).to match('iframe src="https://trello.com/c/NIRpzVDM.html"')
end
end
end

View File

@ -0,0 +1,12 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::TwitchClipsOnebox do
let(:hostname) { 'www.example.com' }
let(:options) { { hostname: hostname } }
it "has the iframe with the correct channel" do
expect(Onebox.preview('https://clips.twitch.tv/FunVastGalagoKlappa', options).to_s).to match(/<iframe src="https:\/\/clips\.twitch\.tv\/embed\?clip=FunVastGalagoKlappa&amp;parent=#{hostname}/)
end
end

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::TwitchStreamOnebox do
let(:hostname) { 'www.example.com' }
let(:options) { { hostname: hostname } }
it "has the iframe with the correct channel" do
expect(Onebox.preview('https://www.twitch.tv/theduckie908', options).to_s).to match(/<iframe src="https:\/\/player\.twitch\.tv\/\?channel=theduckie908&amp;parent=#{hostname}/)
end
it "works in the twitch new interface/url" do
expect(Onebox.preview('https://go.twitch.tv/admiralbulldog', options).to_s).to match(/<iframe src="https:\/\/player\.twitch\.tv\/\?channel=admiralbulldog&amp;parent=#{hostname}/)
end
end

View File

@ -0,0 +1,12 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::TwitchVideoOnebox do
let(:hostname) { 'www.example.com' }
let(:options) { { hostname: hostname } }
it "has the iframe with the correct channel" do
expect(Onebox.preview('https://www.twitch.tv/videos/140675974', options).to_s).to match(/<iframe src="https:\/\/player\.twitch\.tv\/\?video=v140675974&amp;parent=#{hostname}/)
end
end

View File

@ -0,0 +1,658 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::TwitterStatusOnebox do
shared_examples_for "#to_html" do
it "includes tweet" do
expect(html).to include(tweet_content)
end
# TODO: handle t.co links
# it "includes link" do
# expect(html).to include("http://www.peers.org/action/peers-pledgea")
# end
it "gets the correct timestamp" do
expect(html).to include(timestamp)
end
it "includes name" do
expect(html).to include(full_name)
end
it "includes username" do
expect(html).to include(screen_name)
end
it "includes user avatar" do
expect(html).to include(avatar)
end
it "includes twitter link" do
expect(html).to include(link)
end
it "includes twitter likes" do
expect(html).to include(favorite_count)
end
it "includes twitter retweets" do
expect(html).to include(retweets_count)
end
end
shared_context "standard tweet info" do
before do
@link = "https://twitter.com/vyki_e/status/363116819147538433"
@onebox_fixture = "twitterstatus"
end
let(:full_name) { "Vyki Englert" }
let(:screen_name) { "vyki_e" }
let(:avatar) { "732349210264133632/RTNgZLrm_400x400.jpg" }
let(:timestamp) { "6:59 PM - 1 Aug 2013" }
let(:link) { @link }
let(:favorite_count) { "0" }
let(:retweets_count) { "0" }
end
shared_context "quoted tweet info" do
before do
@link = "https://twitter.com/Metallica/status/1128068672289890305"
@onebox_fixture = "twitterstatus_quoted"
stub_request(:get, @link.downcase).to_return(status: 200, body: onebox_response(@onebox_fixture))
end
let(:full_name) { "Metallica" }
let(:screen_name) { "Metallica" }
let(:avatar) { "profile_images/766360293953802240/kt0hiSmv_400x400.jpg" }
let(:timestamp) { "10:45 PM - 13 May 2019" }
let(:link) { @link }
let(:favorite_count) { "1.7K" }
let(:retweets_count) { "201" }
end
shared_examples "includes quoted tweet data" do
it 'includes quoted tweet' do
expect(html).to include("If you bought a ticket for tonight’s @Metallica show at Stade de France, you have helped")
end
it "includes quoted tweet name" do
expect(html).to include("All Within My Hands Foundation")
end
it "includes quoted username" do
expect(html).to include("AWMHFoundation")
end
it "includes quoted tweet link" do
expect(html).to include("https://twitter.com/AWMHFoundation/status/1127646016931487744")
end
end
context "with html" do
context "with a standard tweet" do
let(:tweet_content) { "I'm a sucker for pledges." }
include_context "standard tweet info"
include_context "engines"
it_behaves_like "an engine"
it_behaves_like "#to_html"
end
context "with a quoted tweet" do
let(:tweet_content) do
"Thank you to everyone who came out for #MetInParis last night for helping us support @EMMAUSolidarite &amp;"
end
include_context "quoted tweet info"
include_context "engines"
it_behaves_like "an engine"
it_behaves_like '#to_html'
it_behaves_like "includes quoted tweet data"
end
end
context "with twitter client" do
before do
@twitter_client = stub("TwitterClient",
status: api_response,
prettify_tweet: tweet_content,
twitter_credentials_missing?: false,
prettify_number: favorite_count
)
@previous_options = Onebox.options.to_h
Onebox.options = { twitter_client: @twitter_client }
end
after do
Onebox.options = @previous_options
end
context "with a standard tweet" do
let(:tweet_content) do
"I'm a sucker for pledges. <a href='https://twitter.com/Peers' target='_blank'>@Peers</a> Pledge <a href='https://twitter.com/search?q=%23sharingeconomy' target='_blank'>#sharingeconomy</a> <a target='_blank' href='http://www.peers.org/action/peers-pledgea/'>peers.org/action/peers-p…</a>"
end
let(:api_response) do
{
created_at: "Fri Aug 02 01:59:30 +0000 2013",
id: 363116819147538400,
id_str: "363116819147538433",
text: "I'm a sucker for pledges. @Peers Pledge #sharingeconomy http://t.co/T4Sc47KAzh",
truncated: false,
entities: {
hashtags: [
{
text: "sharingeconomy",
indices: [
41,
56
]
}
],
symbols: [],
user_mentions: [
{
screen_name: "peers",
name: "Peers",
id: 1428357889,
id_str: "1428357889",
indices: [
27,
33
]
}
],
urls: [
{
url: "http://t.co/T4Sc47KAzh",
expanded_url: "http://www.peers.org/action/peers-pledgea/",
display_url: "peers.org/action/peers-p…",
indices: [
57,
79
]
}
]
},
source: "<a href=\"https://dev.twitter.com/docs/tfw\" rel=\"nofollow\">Twitter for Websites</a>",
in_reply_to_status_id: nil,
in_reply_to_status_id_str: nil,
in_reply_to_user_id: nil,
in_reply_to_user_id_str: nil,
in_reply_to_screen_name: nil,
user: {
id: 1087064150,
id_str: "1087064150",
name: "Vyki Englert",
screen_name: "vyki_e",
location: "Los Angeles, CA",
description: "Rides bikes, writes code, likes maps. @CompilerLA / @CityGrows / Brigade Captain @HackforLA",
url: "http://t.co/YCAP3asRG1",
entities: {
url: {
urls: [
{
url: "http://t.co/YCAP3asRG1",
expanded_url: "http://www.compiler.la",
display_url: "compiler.la",
indices: [
0,
22
]
}
]
},
description: {
urls: []
}
},
protected: false,
followers_count: 1128,
friends_count: 2244,
listed_count: 83,
created_at: "Sun Jan 13 19:53:00 +0000 2013",
favourites_count: 2928,
utc_offset: -25200,
time_zone: "Pacific Time (US & Canada)",
geo_enabled: true,
verified: false,
statuses_count: 3295,
lang: "en",
contributors_enabled: false,
is_translator: false,
is_translation_enabled: false,
profile_background_color: "ACDED6",
profile_background_image_url: "http://abs.twimg.com/images/themes/theme18/bg.gif",
profile_background_image_url_https: "https://abs.twimg.com/images/themes/theme18/bg.gif",
profile_background_tile: false,
profile_image_url: "http://pbs.twimg.com/profile_images/732349210264133632/RTNgZLrm_normal.jpg",
profile_image_url_https: "https://pbs.twimg.com/profile_images/732349210264133632/RTNgZLrm_normal.jpg",
profile_banner_url: "https://pbs.twimg.com/profile_banners/1087064150/1424315468",
profile_link_color: "4E99D1",
profile_sidebar_border_color: "EEEEEE",
profile_sidebar_fill_color: "F6F6F6",
profile_text_color: "333333",
profile_use_background_image: true,
has_extended_profile: false,
default_profile: false,
default_profile_image: false,
following: false,
follow_request_sent: false,
notifications: false
},
geo: nil,
coordinates: nil,
place: nil,
contributors: nil,
is_quote_status: false,
retweet_count: 0,
favorite_count: 0,
favorited: false,
retweeted: false,
possibly_sensitive: false,
possibly_sensitive_appealable: false,
lang: "en"
}
end
include_context "standard tweet info"
include_context "engines"
it_behaves_like "an engine"
it_behaves_like "#to_html"
end
context "with quoted tweet" do
let(:tweet_content) do
"Thank you to everyone who came out for <a href='https://twitter.com/search?q=%23MetInParis' target='_blank'>#MetInParis</a> last night for helping us support <a href='https://twitter.com/EMMAUSolidarite' target='_blank'>@EMMAUSolidarite</a> &amp; <a href='https://twitter.com/PompiersParis' target='_blank'>@PompiersParis</a>. <a href='https://twitter.com/search?q=%23AWMH' target='_blank'>#AWMH</a> <a href='https://twitter.com/search?q=%23MetalicaGivesBack' target='_blank'>#MetalicaGivesBack</a> <a href=\"https://t.co/gLtZSdDFmN\" target=\"_blank\">https://t.co/gLtZSdDFmN</a>"
end
let(:api_response) do
{
created_at: "Mon May 13 22:45:04 +0000 2019",
id: 1128068672289890305,
id_str: "1128068672289890305",
full_text: "Thank you to everyone who came out for #MetInParis last night for helping us support @EMMAUSolidarite &amp; @PompiersParis. #AWMH #MetalicaGivesBack https://t.co/gLtZSdDFmN",
truncated: false,
display_text_range: [0, 148],
entities: {
hashtags: [
{
text: "MetInParis",
indices: [39, 50]
},
{
text: "AWMH",
indices: [124, 129]
},
{
text: "MetalicaGivesBack",
indices: [130, 148]
}
],
symbols: [],
user_mentions: [
{
screen_name: "EMMAUSolidarite",
name: "EMMAÜS Solidarité",
id: 2912493406,
id_str: "2912493406",
indices: [85, 101]
},
{
screen_name: "PompiersParis",
name: "Pompiers de Paris",
id: 1342191438,
id_str: "1342191438",
indices: [108, 122]
}
],
urls: [
{
url: "https://t.co/gLtZSdDFmN",
expanded_url: "https://twitter.com/AWMHFoundation/status/1127646016931487744",
display_url: "twitter.com/AWMHFoundation…",
indices: [149, 172]
}
]
},
source: "<a href=\"http://twitter.com\" rel=\"nofollow\">Twitter Web Client</a>",
in_reply_to_status_id: nil,
in_reply_to_status_id_str: nil,
in_reply_to_user_id: nil,
in_reply_to_user_id_str: nil,
in_reply_to_screen_name: nil,
user: {
id: 238475531,
id_str: "238475531",
name: "Metallica",
screen_name: "Metallica",
location: "San Francisco, CA",
description: "http://t.co/EAkqroM0OA | http://t.co/BEu6OVRhKG",
url: "http://t.co/kVxaQpmqSI",
entities: {
url: {
urls: [
{
url: "http://t.co/kVxaQpmqSI",
expanded_url: "http://www.metallica.com",
display_url: "metallica.com",
indices: [0, 22]
}
]
},
description: {
urls: [
{
url: "http://t.co/EAkqroM0OA",
expanded_url: "http://metallica.com",
display_url: "metallica.com",
indices: [0, 22]
},
{
url: "http://t.co/BEu6OVRhKG",
expanded_url: "http://livemetallica.com",
display_url: "livemetallica.com",
indices: [25, 47]
}
]
}
},
protected: false,
followers_count: 5760661,
friends_count: 31,
listed_count: 12062,
created_at: "Sat Jan 15 07:34:59 +0000 2011",
favourites_count: 567,
utc_offset: nil,
time_zone: nil,
geo_enabled: true,
verified: true,
statuses_count: 3764,
lang: nil,
contributors_enabled: false,
is_translator: false,
is_translation_enabled: false,
profile_background_color: "000000",
profile_background_image_url: "http://abs.twimg.com/images/themes/theme9/bg.gif",
profile_background_image_url_https: "https://abs.twimg.com/images/themes/theme9/bg.gif",
profile_background_tile: false,
profile_image_url: "http://pbs.twimg.com/profile_images/766360293953802240/kt0hiSmv_normal.jpg",
profile_image_url_https: "https://pbs.twimg.com/profile_images/766360293953802240/kt0hiSmv_normal.jpg",
profile_banner_url: "https://pbs.twimg.com/profile_banners/238475531/1479538295",
profile_link_color: "2FC2EF",
profile_sidebar_border_color: "000000",
profile_sidebar_fill_color: "252429",
profile_text_color: "666666",
profile_use_background_image: false,
has_extended_profile: false,
default_profile: false,
default_profile_image: false,
following: false,
follow_request_sent: false,
notifications: false,
translator_type: "regular"
},
geo: nil,
coordinates: nil,
place: nil,
contributors: nil,
is_quote_status: true,
quoted_status_id: 1127646016931487744,
quoted_status_id_str: "1127646016931487744",
quoted_status_permalink: {
url: "https://t.co/gLtZSdDFmN",
expanded: "https://twitter.com/AWMHFoundation/status/1127646016931487744",
display: "twitter.com/AWMHFoundation…"
},
quoted_status: {
created_at: "Sun May 12 18:45:35 +0000 2019",
id: 1127646016931487744,
id_str: "1127646016931487744",
full_text: "If you bought a ticket for tonight’s @Metallica show at Stade de France, you have helped contribute to @EMMAUSolidarite &amp; @PompiersParis. #MetallicaGivesBack #AWMH #MetInParis https://t.co/wlUtDQbQEK",
truncated: false,
display_text_range: [0, 179],
entities: {
hashtags: [
{
text: "MetallicaGivesBack",
indices: [142, 161]
}, {
text: "AWMH",
indices: [162, 167]
}, {
text: "MetInParis",
indices: [168, 179]
}
],
symbols: [],
user_mentions: [
{
screen_name: "Metallica",
name: "Metallica",
id: 238475531,
id_str: "238475531",
indices: [37, 47]
}, {
screen_name: "EMMAUSolidarite",
name: "EMMAÜS Solidarité",
id: 2912493406,
id_str: "2912493406",
indices: [103, 119]
}, {
screen_name: "PompiersParis",
name: "Pompiers de Paris",
id: 1342191438,
id_str: "1342191438",
indices: [126, 140]
}
],
urls: [],
media: [
{
id: 1127645176183250944,
id_str: "1127645176183250944",
indices: [180, 203],
media_url: "http://pbs.twimg.com/media/D6YzUC8V4AApDdF.jpg",
media_url_https: "https://pbs.twimg.com/media/D6YzUC8V4AApDdF.jpg",
url: "https://t.co/wlUtDQbQEK",
display_url: "pic.twitter.com/wlUtDQbQEK",
expanded_url: "https://twitter.com/AWMHFoundation/status/1127646016931487744/photo/1",
type: "photo",
sizes: {
large: {
w: 2048,
h: 1498,
resize: "fit"
},
thumb: {
w: 150,
h: 150,
resize: "crop"
},
medium: {
w: 1200,
h: 877,
resize: "fit"
},
small: {
w: 680,
h: 497,
resize: "fit"
}
}
}
]
},
extended_entities: {
media: [
{
id: 1127645176183250944,
id_str: "1127645176183250944",
indices: [180, 203],
media_url: "http://pbs.twimg.com/media/D6YzUC8V4AApDdF.jpg",
media_url_https: "https://pbs.twimg.com/media/D6YzUC8V4AApDdF.jpg",
url: "https://t.co/wlUtDQbQEK",
display_url: "pic.twitter.com/wlUtDQbQEK",
expanded_url: "https://twitter.com/AWMHFoundation/status/1127646016931487744/photo/1",
type: "photo",
sizes: {
large: {
w: 2048,
h: 1498,
resize: "fit"
},
thumb: {
w: 150,
h: 150,
resize: "crop"
},
medium: {
w: 1200,
h: 877,
resize: "fit"
},
small: {
w: 680,
h: 497,
resize: "fit"
}
}
}, {
id: 1127645195384774657,
id_str: "1127645195384774657",
indices: [180, 203],
media_url: "http://pbs.twimg.com/media/D6YzVKeV4AEPpSQ.jpg",
media_url_https: "https://pbs.twimg.com/media/D6YzVKeV4AEPpSQ.jpg",
url: "https://t.co/wlUtDQbQEK",
display_url: "pic.twitter.com/wlUtDQbQEK",
expanded_url: "https://twitter.com/AWMHFoundation/status/1127646016931487744/photo/1",
type: "photo",
sizes: {
thumb: {
w: 150,
h: 150,
resize: "crop"
},
medium: {
w: 1200,
h: 922,
resize: "fit"
},
small: {
w: 680,
h: 522,
resize: "fit"
},
large: {
w: 2048,
h: 1574,
resize: "fit"
}
}
}
]
},
source: "<a href=\"http://twitter.com\" rel=\"nofollow\">Twitter Web Client</a>",
in_reply_to_status_id: nil,
in_reply_to_status_id_str: nil,
in_reply_to_user_id: nil,
in_reply_to_user_id_str: nil,
in_reply_to_screen_name: nil,
user: {
id: 886959980254871552,
id_str: "886959980254871552",
name: "All Within My Hands Foundation",
screen_name: "AWMHFoundation",
location: "",
description: "",
url: "https://t.co/KgwIPrVVhg",
entities: {
url: {
urls: [
{
url: "https://t.co/KgwIPrVVhg",
expanded_url: "http://allwithinmyhands.org",
display_url: "allwithinmyhands.org",
indices: [0, 23]
}
]
},
description: {
urls: []
}
},
protected: false,
followers_count: 5962,
friends_count: 6,
listed_count: 15,
created_at: "Mon Jul 17 14:45:13 +0000 2017",
favourites_count: 30,
utc_offset: nil,
time_zone: nil,
geo_enabled: true,
verified: false,
statuses_count: 241,
lang: nil,
contributors_enabled: false,
is_translator: false,
is_translation_enabled: false,
profile_background_color: "000000",
profile_background_image_url: "http://abs.twimg.com/images/themes/theme1/bg.png",
profile_background_image_url_https: "https://abs.twimg.com/images/themes/theme1/bg.png",
profile_background_tile: false,
profile_image_url: "http://pbs.twimg.com/profile_images/935181032185241600/D8FoOIRJ_normal.jpg",
profile_image_url_https: "https://pbs.twimg.com/profile_images/935181032185241600/D8FoOIRJ_normal.jpg",
profile_banner_url: "https://pbs.twimg.com/profile_banners/886959980254871552/1511799663",
profile_link_color: "000000",
profile_sidebar_border_color: "000000",
profile_sidebar_fill_color: "000000",
profile_text_color: "000000",
profile_use_background_image: false,
has_extended_profile: false,
default_profile: false,
default_profile_image: false,
following: false,
follow_request_sent: false,
notifications: false,
translator_type: "none"
},
geo: nil,
coordinates: nil,
place: nil,
contributors: nil,
is_quote_status: false,
retweet_count: 46,
favorite_count: 275,
favorited: false,
retweeted: false,
possibly_sensitive: false,
possibly_sensitive_appealable: false,
lang: "en"
},
retweet_count: 201,
favorite_count: 1664,
favorited: false,
retweeted: false,
possibly_sensitive: false,
possibly_sensitive_appealable: false,
lang: "en"
}
end
include_context "quoted tweet info"
include_context "engines"
it_behaves_like "an engine"
it_behaves_like '#to_html'
it_behaves_like "includes quoted tweet data"
end
end
end

View File

@ -0,0 +1,45 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::TypeformOnebox do
it 'Appends the embed widget param when is missing' do
raw_preview = Onebox.preview('https://basvanleeuwen1.typeform.com/to/NzdRpx').to_s
query_params = get_query_params(raw_preview)
expect_to_have_embed_widget(query_params)
end
it 'Uses the URL as it is when the embed widget param is present' do
raw_preview = Onebox.preview('https://basvanleeuwen1.typeform.com/to/NzdRpx?typeform-embed=embed-widget').to_s
query_params = get_query_params(raw_preview)
expect_to_have_embed_widget(query_params)
end
it 'Does not adds an ? when it is already present' do
raw_preview = Onebox.preview('https://basvanleeuwen1.typeform.com/to/NzdRpx?').to_s
query_params = get_query_params(raw_preview)
expect_to_have_embed_widget(query_params)
end
it 'Appends it to the end when there are other params present' do
raw_preview = Onebox.preview('https://basvanleeuwen1.typeform.com/to/NzdRpx?param1=value1').to_s
query_params = get_query_params(raw_preview)
expect_to_have_embed_widget(query_params)
end
def expect_to_have_embed_widget(query_params)
expected_widget_type = ['embed-widget']
current_widget_type = query_params.fetch('typeform-embed', [])
expect(current_widget_type).to eq expected_widget_type
end
def get_query_params(raw_preview)
form_url = inspect_html_fragment(raw_preview, 'iframe', 'src')
CGI::parse(URI::parse(form_url).query || '')
end
end

View File

@ -0,0 +1,37 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::VideoOnebox do
it "supports ogv" do
expect(Onebox.preview('http://upload.wikimedia.org/wikipedia/commons/3/37/STS-134_launch_2.ogv').to_s).to match(/<video/)
end
it "supports mp4" do
expect(Onebox.preview('http://download.wavetlan.com/svv/dev/test.mp4').to_s).to match(/<video/)
end
it "supports mov" do
expect(Onebox.preview('http://download.wavetlan.com/SVV/Media/HTTP/BlackBerry.MOV').to_s).to match(/<video/)
end
it "supports webm" do
expect(Onebox.preview('http://video.webmfiles.org/big-buck-bunny_trailer.webm').to_s).to match(/<video/)
end
it "supports URLs with query parameters" do
expect(Onebox.preview('http://video.webmfiles.org/big-buck-bunny_trailer.webm?foo=bar').to_s).to match(/<video/)
end
it "supports protocol relative URLs" do
expect(Onebox.preview('//video.webmfiles.org/big-buck-bunny_trailer.webm').to_s).to match(/<video/)
end
it "includes a fallback direct link to the video" do
expect(Onebox.preview('http://download.wavetlan.com/svv/dev/test.mp4').to_s).to match(/<a.*mp4/)
end
it "respects the disable_media_download_controls option" do
expect(Onebox.preview('http://download.wavetlan.com/svv/dev/test.mp4', disable_media_download_controls: true).to_s).to include("controlslist=\"nodownload\"")
end
end

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::WikimediaOnebox do
let(:link) { "https://commons.wikimedia.org/wiki/File:Stones_members_montage2.jpg" }
let(:api_link) { "https://en.wikipedia.org/w/api.php?action=query&titles=File:Stones_members_montage2.jpg&prop=imageinfo&iilimit=50&iiprop=timestamp|user|url&iiurlwidth=500&format=json" }
let(:html) { described_class.new(link).to_html }
before do
stub_request(:get, api_link).to_return(status: 200, body: onebox_response("wikimedia"))
end
it "has the title" do
expect(html).to include("File:Stones members montage2.jpg")
end
it "has the link" do
expect(html).to include(link)
end
it "has the image" do
expect(html).to include("https://upload.wikimedia.org/wikipedia/commons/a/af/Stones_members_montage2.jpg")
end
end

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::WikipediaOnebox do
before do
@link = "http://en.wikipedia.org/wiki/Billy_Jack"
stub_request(:get, "https://en.wikipedia.org/wiki/Billy_Jack")
.to_return(status: 200, body: onebox_response(described_class.onebox_name))
end
include_context "engines"
it_behaves_like "an engine"
describe "#to_html" do
it "includes article image" do
expect(html).to include("Billy_Jack_poster.jpg")
end
it "includes summary" do
expect(html).to include("Billy Jack is a 1971 action/drama")
end
end
end

View File

@ -0,0 +1,29 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::XkcdOnebox do
let(:link) { "https://xkcd.com/327/" }
let(:api_link) { "https://xkcd.com/327/info.0.json" }
let(:html) { described_class.new(link).to_html }
before do
stub_request(:get, api_link).to_return(status: 200, body: onebox_response("xkcd"))
end
it "has the comic's description" do
expect(html).to include("Her daughter is named Help")
end
it "has the comic's title" do
expect(html).to include("Exploits of a Mom")
end
it "has the permalink to the comic" do
expect(html).to include(link)
end
it "has the comic image" do
expect(html).to include("http://imgs.xkcd.com/comics/exploits_of_a_mom.png")
end
end

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::YoukuOnebox do
before do
stub_request(:get, 'http://v.youku.com/v_show/id_XNjM3MzAxNzc2.html')
.to_return(status: 200, body: onebox_response('youku'), headers: { content_type: 'text/html' })
stub_request(:get, 'http://v.youku.com/player/getPlayList/VideoIDS/XNjM3MzAxNzc2')
.to_return(status: 200, body: onebox_response('youku-meta'), headers: { content_type: 'text/html' })
end
it 'returns embed as the placeholder' do
html = Onebox.preview('http://v.youku.com/v_show/id_XNjM3MzAxNzc2.html').placeholder_html
expect(html).to match(/embed/)
end
end

View File

@ -0,0 +1,97 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine::YoutubeOnebox do
before do
stub_request(:get, "https://www.youtube.com/watch?feature=player_embedded&v=21Lk4YiASMo").to_return(status: 200, body: onebox_response("youtube"))
stub_request(:get, "https://youtu.be/21Lk4YiASMo").to_return(status: 200, body: onebox_response("youtube"))
stub_request(:get, "https://www.youtube.com/embed/21Lk4YiASMo").to_return(status: 200, body: onebox_response("youtube"))
stub_request(:get, "http://www.youtube.com/watch?v=21Lk4YiASMo").to_return(status: 200, body: onebox_response("youtube"))
stub_request(:get, "https://www.youtube.com/watch?v=21Lk4YiASMo").to_return(status: 200, body: onebox_response("youtube"))
stub_request(:get, "https://www.youtube.com/channel/UCL8ZULXASCc1I_oaOT0NaOQ").to_return(status: 200, body: onebox_response("youtube-channel"))
stub_request(:get, "http://www.youtube.com/user/googlechrome").to_return(status: 200, body: onebox_response("youtube-channel"))
stub_request(:get, "https://www.youtube.com/playlist?list=PL5308B2E5749D1696").to_return(status: 200, body: onebox_response("youtube-playlist"))
stub_request(:get, "https://www.youtube.com/embed/KCyIfcevExE").to_return(status: 200, body: onebox_response("youtube-embed"))
end
it "adds wmode=opaque" do
expect(Onebox.preview('https://www.youtube.com/watch?v=21Lk4YiASMo').to_s).to match(/wmode=opaque/)
end
it "rewrites URLs for videos to be HTTPS" do
# match: plain HTTP and protocol agnostic
regex = /(http:|["']\/\/)/
expect(Onebox.preview('https://www.youtube.com/watch?v=21Lk4YiASMo').to_s).not_to match(regex)
expect(Onebox.preview('https://www.youtube.com/watch?v=21Lk4YiASMo').placeholder_html).not_to match(regex)
expect(Onebox.preview('https://www.youtube.com/channel/UCL8ZULXASCc1I_oaOT0NaOQ').to_s).not_to match(regex)
end
it "can onebox a channel page" do
expect(Onebox.preview('https://www.youtube.com/channel/UCL8ZULXASCc1I_oaOT0NaOQ').to_s).to match(/Google Chrome/)
end
it "can onebox a playlist" do
expect(Onebox.preview('https://www.youtube.com/playlist?list=PL5308B2E5749D1696').to_s).to match(/iframe/)
placeholder_html = Onebox.preview('https://www.youtube.com/playlist?list=PL5308B2E5749D1696').placeholder_html
expect(placeholder_html).to match(/<img/)
expect(placeholder_html).to include("The web is what you make of it")
end
it "does not make HTTP requests unless necessary" do
# We haven't defined any fixture for requests associated with this ID, so if
# any HTTP requests are made webmock will complain and the test will fail.
Onebox.preview('http://www.youtube.com/watch?v=q39Ce3zDScI').to_s
end
it "does not fail if we cannot get the video ID from the URL" do
expect(Onebox.preview('http://www.youtube.com/watch?feature=player_embedded&v=21Lk4YiASMo').to_s).to match(/embed/)
end
it "returns an image as the placeholder" do
expect(Onebox.preview('https://www.youtube.com/watch?v=21Lk4YiASMo').placeholder_html).to match(/<img/)
expect(Onebox.preview('https://youtu.be/21Lk4YiASMo').placeholder_html).to match(/<img/)
end
it "passes the playlist ID through" do
expect(Onebox.preview('https://www.youtube.com/watch?v=21Lk4YiASMo&list=UUQau-O2C0kGJpR3_CHBTGbw&index=1').to_s).to match(/UUQau-O2C0kGJpR3_CHBTGbw/)
end
it "filters out nonsense parameters" do
expect(Onebox.preview('https://www.youtube.com/watch?v=21Lk4YiASMo&potential[]=exploit&potential[]=fun').to_s).not_to match(/potential|exploit|fun/)
end
it "converts time strings into a &start= parameter" do
expect(Onebox.preview('https://www.youtube.com/watch?v=21Lk4YiASMo&start=3782').to_s).to match(/start=3782/)
expect(Onebox.preview('https://www.youtube.com/watch?start=1h3m2s&v=21Lk4YiASMo').to_s).to match(/start=3782/)
expect(Onebox.preview('https://www.youtube.com/watch?v=21Lk4YiASMo&t=1h3m2s').to_s).to match(/start=3782/)
expect(Onebox.preview('https://www.youtube.com/watch?v=21Lk4YiASMo&start=1h3m2s').to_s).to match(/start=3782/)
expect(Onebox.preview('https://www.youtube.com/watch?v=21Lk4YiASMo#t=1h3m2s').to_s).to match(/start=3782/)
end
it "allows both start and end" do
preview = expect(Onebox.preview('https://www.youtube.com/watch?v=21Lk4YiASMo&start=2m&end=3m').to_s)
preview.to match(/start=120/)
preview.to match(/end=180/)
end
it "permits looping videos" do
preview = expect(Onebox.preview('https://www.youtube.com/watch?v=21Lk4YiASMo&loop').to_s)
preview.to match(/loop=1/)
preview.to match(/playlist=21Lk4YiASMo/)
end
it "includes title in preview" do
expect(Onebox.preview("https://youtu.be/21Lk4YiASMo").placeholder_html).to include("96neko - orange")
end
it "can parse youtube embed results" do
preview = expect(Onebox.preview('https://www.youtube.com/watch?v=KCyIfcevExE').placeholder_html)
preview.to match(/Delvon/)
preview.to match(/hqdefault/)
end
end

View File

@ -0,0 +1,85 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Engine do
class OneboxEngineExample
include Onebox::Engine
def to_html
"Hello #{link}"
end
def data
{ foo: raw[:key], url: @url }
end
def raw
{ key: "value" }
end
end
describe "#link" do
before do
Onebox::View.stubs(:template).returns(%|this should be a template|)
end
it "escapes `link`" do
html = OneboxEngineExample.new(%|http://foo.com/bar?a='&b=2|).to_html
expect(html).not_to match(/'/)
end
end
describe '.placeholder_html' do
let(:onebox) { OneboxEngineExample.new('http://eviltrout.com') }
it "returns `to_html` by default" do
expect(onebox.to_html).to eq(onebox.placeholder_html)
end
end
describe ".===" do
class OneboxEngineTripleEqual
include Onebox::Engine
@@matcher = /example/
end
it "returns true if argument matches the matcher" do
result = OneboxEngineTripleEqual === URI("http://www.example.com/product/5?var=foo&bar=5")
expect(result).to eq(true)
end
end
class AlwaysHttpsEngineExample < OneboxEngineExample
always_https
end
describe "always_https" do
it "never returns a plain http url" do
url = 'http://play.google.com/store/apps/details?id=com.google.android.inputmethod.latin'
onebox = AlwaysHttpsEngineExample.new(url)
result = onebox.to_html
expect(result).to_not match(/http(?!s)/)
expect(result).to_not match(/['"]\/\//)
expect(result).to match(/https/)
end
end
end
describe ".onebox_name" do
module ScopeForTemplateName
class TemplateNameOnebox
include Onebox::Engine
end
end
let(:onebox_name) { ScopeForTemplateName::TemplateNameOnebox.onebox_name }
it "should not include the scope" do
expect(onebox_name).not_to include("ScopeForTemplateName", "scopefortemplatename")
end
it "should not include the word Onebox" do
expect(onebox_name).not_to include("onebox", "Onebox")
end
end

View File

@ -0,0 +1,176 @@
# frozen_string_literal: true
require "rails_helper"
RSpec.describe Onebox::Helpers do
describe '.blank?' do
it { expect(described_class.blank?("")).to be(true) }
it { expect(described_class.blank?(" ")).to be(true) }
it { expect(described_class.blank?("test")).to be(false) }
it { expect(described_class.blank?(["test", "testing"])).to be(false) }
it { expect(described_class.blank?([])).to be(true) }
it { expect(described_class.blank?({})).to be(true) }
it { expect(described_class.blank?(nil)).to be(true) }
it { expect(described_class.blank?(true)).to be(false) }
it { expect(described_class.blank?(false)).to be(true) }
it { expect(described_class.blank?(a: 'test')).to be(false) }
end
describe ".truncate" do
let(:test_string) { "Chops off on spaces" }
it { expect(described_class.truncate(test_string)).to eq(test_string) }
it { expect(described_class.truncate(test_string, 5)).to eq("Chops...") }
it { expect(described_class.truncate(test_string, 7)).to eq("Chops...") }
it { expect(described_class.truncate(test_string, 9)).to eq("Chops off...") }
it { expect(described_class.truncate(test_string, 10)).to eq("Chops off...") }
it { expect(described_class.truncate(test_string, 100)).to eq("Chops off on spaces") }
it { expect(described_class.truncate(" #{test_string} ", 6)).to eq(" Chops...") }
end
describe "fetch_response" do
around do |example|
previous_options = Onebox.options.to_h
Onebox.options = { max_download_kb: 1 }
stub_request(:get, "http://example.com/large-file").to_return(status: 200, body: onebox_response("slides"))
example.run
Onebox.options = previous_options
end
it "raises an exception when responses are larger than our limit" do
expect {
described_class.fetch_response('http://example.com/large-file')
}.to raise_error(Onebox::Helpers::DownloadTooLarge)
end
end
describe "fetch_html_doc" do
it "can handle unicode URIs" do
uri = 'https://www.reddit.com/r/UFOs/comments/k18ukd/𝗨𝗙𝗢_𝗱𝗿𝗼𝗽𝘀_𝗰𝗼𝘄_𝘁𝗵𝗿𝗼𝘂𝗴𝗵_𝗯𝗮𝗿𝗻_𝗿𝗼𝗼𝗳/'
stub_request(:get, uri).to_return(status: 200, body: "<!DOCTYPE html><p>success</p>")
expect(described_class.fetch_html_doc(uri).to_s).to match("success")
end
end
describe "redirects" do
describe "redirect limit" do
before do
codes = [301, 302, 303, 307, 308]
(1..6).each do |i|
code = codes.pop || 302
stub_request(:get, "https://httpbin.org/redirect/#{i}")
.to_return(status: code, body: "", headers: { location: "https://httpbin.org/redirect/#{i - 1}" })
end
stub_request(:get, "https://httpbin.org/redirect/0").to_return(status: 200, body: "<!DOCTYPE html><p>success</p>")
end
it "can follow redirects" do
expect(described_class.fetch_response("https://httpbin.org/redirect/2")).to match("success")
end
it "errors on long redirect chains" do
expect {
described_class.fetch_response("https://httpbin.org/redirect/6")
}.to raise_error(Net::HTTPError, /redirect too deep/)
end
end
describe "cookie handling" do
it "naively forwards cookies to the next request" do
stub_request(:get, "https://httpbin.org/cookies/set/a/b").to_return(
status: 302,
headers: {
location: "/cookies",
"set-cookie": "a=b; Path=/",
}
)
stub_request(:get, "https://httpbin.org/cookies")
.with(headers: { cookie: "a=b; Path=/" })
.to_return(status: 200, body: "success, cookie readback not implemented")
expect(described_class.fetch_response('https://httpbin.org/cookies/set/a/b')).to match("success")
end
it "does not send cookies to the wrong domain" do
skip("unimplemented")
stub_request(:get, "https://httpbin.org/cookies/set/a/b").to_return(
status: 302,
headers: {
location: "https://evil.com/show_cookies",
"set-cookie": "a=b; Path=/",
}
)
stub_request(:get, "https://evil.com/show_cookies")
.with(headers: { cookie: nil })
.to_return(status: 200, body: "success, cookie readback not implemented")
described_class.fetch_response('https://httpbin.org/cookies/set/a/b')
end
end
end
describe "user_agent" do
context "default" do
it "has the default Discourse user agent" do
stub_request(:get, "http://example.com/some-resource")
.with(headers: { "user-agent" => /Discourse Forum Onebox/ })
.to_return(status: 200, body: "test")
described_class.fetch_response('http://example.com/some-resource')
end
end
context "Custom option" do
around do |example|
previous_options = Onebox.options.to_h
Onebox.options = { user_agent: "EvilTroutBot v0.1" }
example.run
Onebox.options = previous_options
end
it "has the custom user agent" do
stub_request(:get, "http://example.com/some-resource")
.with(headers: { "user-agent" => "EvilTroutBot v0.1" })
.to_return(status: 200, body: "test")
described_class.fetch_response('http://example.com/some-resource')
end
end
end
describe '.normalize_url_for_output' do
it { expect(described_class.normalize_url_for_output('http://example.com/fo o')).to eq("http://example.com/fo%20o") }
it { expect(described_class.normalize_url_for_output("http://example.com/fo'o")).to eq("http://example.com/fo&apos;o") }
it { expect(described_class.normalize_url_for_output('http://example.com/fo"o')).to eq("http://example.com/fo&quot;o") }
it { expect(described_class.normalize_url_for_output('http://example.com/fo<o>')).to eq("http://example.com/foo") }
it { expect(described_class.normalize_url_for_output('http://example.com/d’écran-à')).to eq("http://example.com/d’écran-à") }
it { expect(described_class.normalize_url_for_output('//example.com/hello')).to eq("//example.com/hello") }
it { expect(described_class.normalize_url_for_output('example.com/hello')).to eq("") }
it { expect(described_class.normalize_url_for_output('linear-gradient(310.77deg, #29AA9F 0%, #098EA6 100%)')).to eq("") }
end
describe '.uri_encode' do
it { expect(described_class.uri_encode('http://example.com/f"o&o?[b"ar]')).to eq("http://example.com/f%22o&o?%5Bb%22ar%5D") }
it { expect(described_class.uri_encode("http://example.com/f.o~o;?<ba'r>")).to eq("http://example.com/f.o~o;?%3Cba%27r%3E") }
it { expect(described_class.uri_encode("http://example.com/<pa'th>(foo)?b+a+r")).to eq("http://example.com/%3Cpa'th%3E(foo)?b%2Ba%2Br") }
it { expect(described_class.uri_encode("http://example.com/p,a:t!h-f$o@o*?b!a#r@")).to eq("http://example.com/p,a:t!h-f$o@o*?b%21a#r%40") }
it { expect(described_class.uri_encode("http://example.com/path&foo?b'a<r>&qu(er)y=1")).to eq("http://example.com/path&foo?b%27a%3Cr%3E&qu%28er%29y=1") }
it { expect(described_class.uri_encode("http://example.com/index&<script>alert('XSS');</script>")).to eq("http://example.com/index&%3Cscript%3Ealert('XSS');%3C/script%3E") }
it { expect(described_class.uri_encode("http://example.com/index.html?message=<script>alert('XSS');</script>")).to eq("http://example.com/index.html?message=%3Cscript%3Ealert%28%27XSS%27%29%3B%3C%2Fscript%3E") }
it { expect(described_class.uri_encode("http://example.com/index.php/<IFRAME SRC=source.com onload='alert(document.cookie)'></IFRAME>")).to eq("http://example.com/index.php/%3CIFRAME%20SRC=source.com%20onload='alert(document.cookie)'%3E%3C/IFRAME%3E") }
it { expect(described_class.uri_encode("https://en.wiktionary.org/wiki/greengrocer%27s_apostrophe")).to eq("https://en.wiktionary.org/wiki/greengrocer%27s_apostrophe") }
it { expect(described_class.uri_encode("https://example.com/random%2Bpath?q=random%2Bquery")).to eq("https://example.com/random%2Bpath?q=random%2Bquery") }
it { expect(described_class.uri_encode("https://glitch.com/edit/#!/equinox-watch")).to eq("https://glitch.com/edit/#!/equinox-watch") }
it { expect(described_class.uri_encode("https://gitpod.io/#https://github.com/eclipse-theia/theia")).to eq("https://gitpod.io/#https://github.com/eclipse-theia/theia") }
end
end

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Layout do
let(:record) { {} }
let(:layout) { described_class.new("amazon", record) }
let(:html) { layout.to_html }
describe "#to_html" do
it "contains layout template" do
expect(html).to include(%|class="onebox|)
end
it "contains the view" do
record = { link: "foo" }
html = described_class.new("amazon", record).to_html
expect(html).to include(%|"foo"|)
end
it "rewrites relative image path" do
record = { image: "/image.png", link: "https://discourse.org" }
klass = described_class.new("allowlistedgeneric", record)
expect(klass.view.record[:image]).to include("https://discourse.org")
end
end
end

View File

@ -0,0 +1,105 @@
# frozen_string_literal: true
require "rails_helper"
class TestEngine
def self.===(uri)
true
end
def self.iframe_origins
["https://example.com", "https://example2.com"]
end
end
describe Onebox::Matcher do
let(:opts) { { allowed_iframe_regexes: [/.*/] } }
describe "oneboxed" do
describe "with a path" do
let(:url) { "http://party.time.made.up-url.com/beep/boop" }
let(:matcher) { Onebox::Matcher.new(url, opts) }
it "finds an engine" do
matcher.stubs(:ordered_engines).returns([TestEngine])
expect(matcher.oneboxed).not_to be_nil
end
end
describe "without a path" do
let(:url) { "http://party.time.made.up-url.com/" }
let(:matcher) { Onebox::Matcher.new(url, opts) }
it "doesn't find an engine" do
matcher.stubs(:ordered_engines).returns([TestEngine])
expect(matcher.oneboxed).not_to be_nil
end
end
describe "without a path but has a query string" do
let(:url) { "http://party.time.made.up-url.com/?article_id=1234" }
let(:matcher) { Onebox::Matcher.new(url, opts) }
it "finds an engine" do
matcher.stubs(:ordered_engines).returns([TestEngine])
expect(matcher.oneboxed).not_to be_nil
end
end
describe "without a path but has a fragment string" do
let(:url) { "http://party.time.made.up-url.com/#article_id=1234" }
let(:matcher) { Onebox::Matcher.new(url, opts) }
it "finds an engine" do
matcher.stubs(:ordered_engines).returns([TestEngine])
expect(matcher.oneboxed).not_to be_nil
end
end
describe "with a allowlisted port/scheme" do
%w{http://example.com https://example.com http://example.com:80 //example.com}.each do |url|
it "finds an engine for '#{url}'" do
matcher = Onebox::Matcher.new(url, opts)
matcher.stubs(:ordered_engines).returns([TestEngine])
expect(matcher.oneboxed).not_to be_nil
end
end
end
describe "without a allowlisted port/scheme" do
%w{http://example.com:21 ftp://example.com}.each do |url|
it "doesn't find an engine for '#{url}'" do
matcher = Onebox::Matcher.new(url, opts)
matcher.stubs(:ordered_engines).returns([TestEngine])
expect(matcher.oneboxed).to be_nil
end
end
end
describe "with restricted iframe domains" do
it "finds an engine when wildcard allowed" do
matcher = Onebox::Matcher.new("https://example.com", allowed_iframe_regexes: [/.*/])
matcher.stubs(:ordered_engines).returns([TestEngine])
expect(matcher.oneboxed).not_to be_nil
end
it "doesn't find an engine when nothing allowed" do
matcher = Onebox::Matcher.new("https://example.com", allowed_iframe_regexes: [])
matcher.stubs(:ordered_engines).returns([TestEngine])
expect(matcher.oneboxed).to be_nil
end
it "doesn't find an engine when only some subdomains are allowed" do
matcher = Onebox::Matcher.new("https://example.com", allowed_iframe_regexes: Onebox::Engine.origins_to_regexes(["https://example.com"]))
matcher.stubs(:ordered_engines).returns([TestEngine])
expect(matcher.oneboxed).to be_nil
end
it "finds an engine when all required domains are allowed" do
matcher = Onebox::Matcher.new("https://example.com", allowed_iframe_regexes: Onebox::Engine.origins_to_regexes(["https://example.com", "https://example2.com"]))
matcher.stubs(:ordered_engines).returns([TestEngine])
expect(matcher.oneboxed).not_to be_nil
end
end
end
end

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
require "rails_helper"
require "onebox/oembed"
describe Onebox::Oembed do
it "excludes html tags" do
json = '{"text": "<iframe src=\'https://ifram.es/foo/bar\'></iframe>"}'
oembed = described_class.new(json)
expect(oembed.text).to be_nil
end
it "includes html tags" do
json = '{"html": "<iframe src=\'https://ifram.es/foo/bar\'></iframe>"}'
oembed = described_class.new(json)
expect(oembed.html).to eq("<iframe src='https://ifram.es/foo/bar'></iframe>")
end
end

View File

@ -0,0 +1,20 @@
# frozen_string_literal: true
require "rails_helper"
require "onebox/open_graph"
describe Onebox::OpenGraph do
it "excludes html tags in title" do
doc = Nokogiri::HTML('<html><title>Did&#8217; you &lt;b&gt;miss me&lt;/b&gt;? - Album on Imgur</title><meta name="og:description" content="Post with 7 votes and 151 views. Shared by vinothkannans. Did you &lt;b&gt;miss me&lt;/b&gt;?" /><meta property="og:image" content="https://i.imgur.com/j1CNCZY.gif?noredirect" /></html>')
og = described_class.new(doc)
expect(og.title).to eq("Did’ you miss me? - Album on Imgur")
expect(og.description).to eq("Post with 7 votes and 151 views. Shared by vinothkannans. Did you miss me?")
expect(og.image).to eq("https://i.imgur.com/j1CNCZY.gif?noredirect")
end
it "correctly normalizes the url properties" do
doc = Nokogiri::HTML("<html><meta property=\"og:image\" content=\"http://test.com/test'ing.mp3\" /></html>")
og = described_class.new(doc)
expect(og.image).to eq("http://test.com/test&apos;ing.mp3")
end
end

View File

@ -0,0 +1,108 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::Preview do
before do
stub_request(:get, "https://www.amazon.com/product")
.to_return(status: 200, body: onebox_response("amazon"))
end
let(:preview_url) { "http://www.amazon.com/product" }
let(:preview) { described_class.new(preview_url) }
describe "#to_s" do
before do
stub_request(:get, "https://www.amazon.com/Seven-Languages-Weeks-Programming-Programmers/dp/193435659X")
.to_return(status: 200, body: onebox_response("amazon"))
end
it "returns some html if given a valid url" do
title = "Seven Languages in Seven Weeks: A Pragmatic Guide to Learning Programming Languages (Pragmatic Programmers)"
expect(preview.to_s).to include(title)
end
it "returns an empty string if the url is not valid" do
expect(described_class.new('not a url').to_s).to eq("")
end
end
describe "max_width" do
let(:iframe_html) { '<iframe src="//player.vimeo.com/video/96017582" width="1280" height="720" frameborder="0" title="GO BIG OR GO HOME" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>' }
it "doesn't change dimensions without an option" do
iframe = described_class.new(preview_url)
iframe.stubs(:engine_html).returns(iframe_html)
result = iframe.to_s
expect(result).to include("width=\"1280\"")
expect(result).to include("height=\"720\"")
end
it "doesn't change dimensions if it is smaller than `max_width`" do
iframe = described_class.new(preview_url, max_width: 2000)
iframe.stubs(:engine_html).returns(iframe_html)
result = iframe.to_s
expect(result).to include("width=\"1280\"")
expect(result).to include("height=\"720\"")
end
it "changes dimensions if larger than `max_width`" do
iframe = described_class.new(preview_url, max_width: 900)
iframe.stubs(:engine_html).returns(iframe_html)
result = iframe.to_s
expect(result).to include("width=\"900\"")
expect(result).to include("height=\"506\"")
end
end
describe "#engine" do
it "returns an engine" do
expect(preview.send(:engine)).to be_an(Onebox::Engine)
end
end
describe "xss" do
let(:xss) { "wat' onerror='alert(/XSS/)" }
let(:img_html) { "<img src='#{xss}'>" }
it "prevents XSS" do
preview = described_class.new(preview_url)
preview.stubs(:engine_html).returns(img_html)
result = preview.to_s
expect(result).not_to match(/onerror/)
end
end
describe "iframe sanitizer" do
let(:iframe_html) { "<iframe src='https://thirdparty.example.com'>" }
it "sanitizes iframes from unknown origins" do
preview = described_class.new(preview_url)
preview.stubs(:engine_html).returns(iframe_html)
result = preview.to_s
expect(result).not_to include(' src="https://thirdparty.example.com"')
expect(result).to include(' data-unsanitized-src="https://thirdparty.example.com"')
end
it "allows allowed origins" do
preview = described_class.new(preview_url, allowed_iframe_origins: ["https://thirdparty.example.com"])
preview.stubs(:engine_html).returns(iframe_html)
result = preview.to_s
expect(result).to include ' src="https://thirdparty.example.com"'
end
it "allows wildcard allowed origins" do
preview = described_class.new(preview_url, allowed_iframe_origins: ["https://*.example.com"])
preview.stubs(:engine_html).returns(iframe_html)
result = preview.to_s
expect(result).to include ' src="https://thirdparty.example.com"'
end
end
end

View File

@ -0,0 +1,70 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox::StatusCheck do
before do
stub_request(:get, "http://www.amazon.com/200-url").to_return(status: 200)
stub_request(:get, "http://www.amazon.com/201-url").to_return(status: 201)
stub_request(:get, "http://www.amazon.com/401-url").to_return(status: 401)
stub_request(:get, "http://www.amazon.com/403-url").to_return(status: 403)
stub_request(:get, "http://www.amazon.com/404-url").to_return(status: 404)
stub_request(:get, "http://www.amazon.com/500-url").to_return(status: 500)
stub_request(:get, "http://www.amazon.com/503-url").to_return(status: 503)
stub_request(:get, "http://www.amazon.com/timeout-url").to_raise(Timeout::Error)
stub_request(:get, "http://www.amazon.com/http-error").to_raise(Net::HTTPError.new("error", nil))
stub_request(:get, "http://www.amazon.com/error-connecting").to_raise(Errno::ECONNREFUSED)
end
describe '#human_status' do
it 'returns :success on HTTP status code 200' do
expect(described_class.new("http://www.amazon.com/200-url").human_status).to eq(:success)
end
it 'returns :success on HTTP status code 201' do
expect(described_class.new("http://www.amazon.com/201-url").human_status).to eq(:success)
end
it 'returns :client_error on HTTP status code 401' do
expect(described_class.new("http://www.amazon.com/401-url").human_status).to eq(:client_error)
end
it 'returns :client_error on HTTP status code 403' do
expect(described_class.new("http://www.amazon.com/403-url").human_status).to eq(:client_error)
end
it 'returns :client_error on HTTP status code 404' do
expect(described_class.new("http://www.amazon.com/404-url").human_status).to eq(:client_error)
end
it 'returns :server_error on HTTP status code 500' do
expect(described_class.new("http://www.amazon.com/500-url").human_status).to eq(:server_error)
end
it 'returns :server_error on HTTP status code 503' do
expect(described_class.new("http://www.amazon.com/503-url").human_status).to eq(:server_error)
end
it 'returns :connection_error if there is a connection refused error' do
expect(described_class.new("http://www.amazon.com/error-connecting").human_status).to eq(:connection_error)
end
it 'returns :connection_error if there is a timeout error' do
expect(described_class.new("http://www.amazon.com/timeout-url").human_status).to eq(:connection_error)
end
it 'returns :connection_error if there is a general HTTP error' do
expect(described_class.new("http://www.amazon.com/http-error").human_status).to eq(:connection_error)
end
end
describe '#ok?' do
it 'returns true for HTTP status codes 200-299' do
expect(described_class.new("http://www.amazon.com/200-url").ok?).to be true
end
it 'returns false for any status codes other than 200-299' do
expect(described_class.new("http://www.amazon.com/404-url").ok?).to be false
end
end
end

31
spec/lib/onebox_spec.rb Normal file
View File

@ -0,0 +1,31 @@
# frozen_string_literal: true
require "rails_helper"
describe Onebox do
before do
stub_request(:get, "https://www.amazon.com/product")
.to_return(status: 200, body: onebox_response("amazon"))
end
describe "templates" do
let(:ignored) { ["templates/_layout.mustache"] }
let(:templates) { Dir["templates/*.mustache"] - ignored }
def expect_templates_to_not_match(text)
templates.each do |template|
expect(File.read(template)).not_to match(text)
end
end
it "should not contain any script tags" do
expect_templates_to_not_match(/<script/)
end
end
describe 'has_matcher?' do
it "has a matcher for a real site" do
expect(Onebox.has_matcher?("http://www.youtube.com/watch?v=azaIE6QSMUs")).to be true
end
end
end