mirror of
https://github.com/discourse/discourse.git
synced 2025-06-04 23:14:44 +08:00
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:
185
spec/lib/onebox/engine/allowlisted_generic_onebox_spec.rb
Normal file
185
spec/lib/onebox/engine/allowlisted_generic_onebox_spec.rb
Normal 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 – 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
|
196
spec/lib/onebox/engine/amazon_onebox_spec.rb
Normal file
196
spec/lib/onebox/engine/amazon_onebox_spec.rb
Normal 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
|
37
spec/lib/onebox/engine/audio_onebox_spec.rb
Normal file
37
spec/lib/onebox/engine/audio_onebox_spec.rb
Normal 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
|
28
spec/lib/onebox/engine/cloudapp_onebox_spec.rb
Normal file
28
spec/lib/onebox/engine/cloudapp_onebox_spec.rb
Normal 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
|
39
spec/lib/onebox/engine/gfycat_onebox_spec.rb
Normal file
39
spec/lib/onebox/engine/gfycat_onebox_spec.rb
Normal 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
|
24
spec/lib/onebox/engine/github_blob_onebox_spec.rb
Normal file
24
spec/lib/onebox/engine/github_blob_onebox_spec.rb
Normal 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
|
115
spec/lib/onebox/engine/github_commit_onebox_spec.rb
Normal file
115
spec/lib/onebox/engine/github_commit_onebox_spec.rb
Normal 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
|
44
spec/lib/onebox/engine/github_folder_onebox_spec.rb
Normal file
44
spec/lib/onebox/engine/github_folder_onebox_spec.rb
Normal 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
|
72
spec/lib/onebox/engine/github_gist_onebox_spec.rb
Normal file
72
spec/lib/onebox/engine/github_gist_onebox_spec.rb
Normal 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 "Gist API test." }")
|
||||
expect(html).to include("console.log("Hey! ;)")")
|
||||
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("Wow! This is a test!");")
|
||||
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
|
53
spec/lib/onebox/engine/github_pullrequest_onebox_spec.rb
Normal file
53
spec/lib/onebox/engine/github_pullrequest_onebox_spec.rb
Normal 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
|
25
spec/lib/onebox/engine/gitlab_blob_onebox_spec.rb
Normal file
25
spec/lib/onebox/engine/gitlab_blob_onebox_spec.rb
Normal 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
|
28
spec/lib/onebox/engine/google_docs_onebox_spec.rb
Normal file
28
spec/lib/onebox/engine/google_docs_onebox_spec.rb
Normal 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
|
24
spec/lib/onebox/engine/google_drive_onebox_spec.rb
Normal file
24
spec/lib/onebox/engine/google_drive_onebox_spec.rb
Normal 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
|
74
spec/lib/onebox/engine/google_maps_onebox_spec.rb
Normal file
74
spec/lib/onebox/engine/google_maps_onebox_spec.rb
Normal 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
|
22
spec/lib/onebox/engine/google_photos_onebox_spec.rb
Normal file
22
spec/lib/onebox/engine/google_photos_onebox_spec.rb
Normal 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
|
33
spec/lib/onebox/engine/google_play_app_onebox_spec.rb
Normal file
33
spec/lib/onebox/engine/google_play_app_onebox_spec.rb
Normal 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 & 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
|
30
spec/lib/onebox/engine/html_spec.rb
Normal file
30
spec/lib/onebox/engine/html_spec.rb
Normal 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
|
41
spec/lib/onebox/engine/image_onebox_spec.rb
Normal file
41
spec/lib/onebox/engine/image_onebox_spec.rb
Normal 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
|
18
spec/lib/onebox/engine/imgur_onebox_spec.rb
Normal file
18
spec/lib/onebox/engine/imgur_onebox_spec.rb
Normal 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
|
75
spec/lib/onebox/engine/instagram_onebox_spec.rb
Normal file
75
spec/lib/onebox/engine/instagram_onebox_spec.rb
Normal 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
|
27
spec/lib/onebox/engine/json_spec.rb
Normal file
27
spec/lib/onebox/engine/json_spec.rb
Normal 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
|
58
spec/lib/onebox/engine/kaltura_onebox_spec.rb
Normal file
58
spec/lib/onebox/engine/kaltura_onebox_spec.rb
Normal 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
|
30
spec/lib/onebox/engine/pdf_onebox_spec.rb
Normal file
30
spec/lib/onebox/engine/pdf_onebox_spec.rb
Normal 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
|
78
spec/lib/onebox/engine/pubmed_onebox_spec.rb
Normal file
78
spec/lib/onebox/engine/pubmed_onebox_spec.rb
Normal 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
|
25
spec/lib/onebox/engine/reddit_media_onebox.rb
Normal file
25
spec/lib/onebox/engine/reddit_media_onebox.rb
Normal 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
|
25
spec/lib/onebox/engine/slides_onebox_spec.rb
Normal file
25
spec/lib/onebox/engine/slides_onebox_spec.rb
Normal 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
|
103
spec/lib/onebox/engine/stack_exchange_onebox_spec.rb
Normal file
103
spec/lib/onebox/engine/stack_exchange_onebox_spec.rb
Normal 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
|
31
spec/lib/onebox/engine/trello_onebox_spec.rb
Normal file
31
spec/lib/onebox/engine/trello_onebox_spec.rb
Normal 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
|
12
spec/lib/onebox/engine/twitch_clips_onebox_spec.rb
Normal file
12
spec/lib/onebox/engine/twitch_clips_onebox_spec.rb
Normal 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&parent=#{hostname}/)
|
||||
end
|
||||
end
|
16
spec/lib/onebox/engine/twitch_stream_onebox_spec.rb
Normal file
16
spec/lib/onebox/engine/twitch_stream_onebox_spec.rb
Normal 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&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&parent=#{hostname}/)
|
||||
end
|
||||
end
|
12
spec/lib/onebox/engine/twitch_video_onebox_spec.rb
Normal file
12
spec/lib/onebox/engine/twitch_video_onebox_spec.rb
Normal 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&parent=#{hostname}/)
|
||||
end
|
||||
end
|
658
spec/lib/onebox/engine/twitter_status_onebox_spec.rb
Normal file
658
spec/lib/onebox/engine/twitter_status_onebox_spec.rb
Normal 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 &"
|
||||
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> & <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 & @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 & @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
|
45
spec/lib/onebox/engine/typeform_onebox_spec.rb
Normal file
45
spec/lib/onebox/engine/typeform_onebox_spec.rb
Normal 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
|
37
spec/lib/onebox/engine/video_onebox_spec.rb
Normal file
37
spec/lib/onebox/engine/video_onebox_spec.rb
Normal 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
|
25
spec/lib/onebox/engine/wikimedia_onebox_spec.rb
Normal file
25
spec/lib/onebox/engine/wikimedia_onebox_spec.rb
Normal 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
|
25
spec/lib/onebox/engine/wikipedia_onebox_spec.rb
Normal file
25
spec/lib/onebox/engine/wikipedia_onebox_spec.rb
Normal 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
|
29
spec/lib/onebox/engine/xkcd_spec.rb
Normal file
29
spec/lib/onebox/engine/xkcd_spec.rb
Normal 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
|
18
spec/lib/onebox/engine/youku_onebox_spec.rb
Normal file
18
spec/lib/onebox/engine/youku_onebox_spec.rb
Normal 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
|
97
spec/lib/onebox/engine/youtube_onebox_spec.rb
Normal file
97
spec/lib/onebox/engine/youtube_onebox_spec.rb
Normal 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
|
85
spec/lib/onebox/engine_spec.rb
Normal file
85
spec/lib/onebox/engine_spec.rb
Normal 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
|
176
spec/lib/onebox/helpers_spec.rb
Normal file
176
spec/lib/onebox/helpers_spec.rb
Normal 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'o") }
|
||||
it { expect(described_class.normalize_url_for_output('http://example.com/fo"o')).to eq("http://example.com/fo"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
|
27
spec/lib/onebox/layout_spec.rb
Normal file
27
spec/lib/onebox/layout_spec.rb
Normal 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
|
105
spec/lib/onebox/matcher_spec.rb
Normal file
105
spec/lib/onebox/matcher_spec.rb
Normal 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
|
18
spec/lib/onebox/oembed_spec.rb
Normal file
18
spec/lib/onebox/oembed_spec.rb
Normal 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
|
20
spec/lib/onebox/open_graph_spec.rb
Normal file
20
spec/lib/onebox/open_graph_spec.rb
Normal 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’ you <b>miss me</b>? - Album on Imgur</title><meta name="og:description" content="Post with 7 votes and 151 views. Shared by vinothkannans. Did you <b>miss me</b>?" /><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'ing.mp3")
|
||||
end
|
||||
end
|
108
spec/lib/onebox/preview_spec.rb
Normal file
108
spec/lib/onebox/preview_spec.rb
Normal 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
|
70
spec/lib/onebox/status_check_spec.rb
Normal file
70
spec/lib/onebox/status_check_spec.rb
Normal 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
31
spec/lib/onebox_spec.rb
Normal 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
|
Reference in New Issue
Block a user