Merge pull request #6256 from TeamDeltaQuadrant/5813-show-geolocation-on-osm

5813 show geolocation on osm
This commit is contained in:
Steffen van Bergerem 2015-09-23 01:41:31 +02:00
commit b40d5362cf
29 changed files with 655 additions and 334 deletions

View file

@ -71,6 +71,7 @@ With the port to Bootstrap 3, app/views/terms/default.haml has a new structure.
* Add support for relay based public post federation [#6207](https://github.com/diaspora/diaspora/pull/6207)
* Bigger mobile publisher [#6261](https://github.com/diaspora/diaspora/pull/6261)
* Backend information panel & health checks for known pods [#6290](https://github.com/diaspora/diaspora/pull/6290)
* Allow users to view a posts locations on an OpenStreetMap [#6256](https://github.com/diaspora/diaspora/pull/6256)
# 0.5.4.0

View file

@ -128,6 +128,9 @@ gem "rails-i18n", "4.0.5"
gem "markerb", "1.1.0"
gem "messagebus_ruby_api", "1.0.3"
# Map
gem "leaflet-rails", "0.7.4"
# Parsing
gem "nokogiri", "1.6.6.2"

View file

@ -418,6 +418,7 @@ GEM
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
kgio (2.10.0)
leaflet-rails (0.7.4)
listen (3.0.3)
rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9)
@ -841,6 +842,7 @@ DEPENDENCIES
jshintrb (= 0.3.0)
json (= 1.8.3)
json-schema (= 2.5.1)
leaflet-rails (= 0.7.4)
logging-rails (= 0.5.0)
markerb (= 1.1.0)
messagebus_ruby_api (= 1.0.3)

View file

@ -29,7 +29,6 @@ app.views.Content = app.views.Base.extend({
return photos;
},
expandPost: function(evt) {
var el = $(this.el).find('.collapsible');
el.removeClass('collapsed').addClass('opened');
@ -40,8 +39,8 @@ app.views.Content = app.views.Base.extend({
},
location: function(){
var address = this.model.get('address')? this.model.get('address') : '';
return address;
var location = this.model.get("location")? this.model.get("location") : "";
return location;
},
collapseOversized : function() {
@ -155,4 +154,5 @@ app.views.SPVOpenGraph = app.views.OpenGraph.extend({
// override with nothing
}
});
// @license-end

View file

@ -1,7 +1,54 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.LocationStream = app.views.Content.extend({
templateName: "status-message-location"
events: {
"click .near-from": "toggleMap"
},
templateName: "status-message-location",
toggleMap: function () {
var mapContainer = this.$el.find(".mapContainer");
if (mapContainer.hasClass("empty")) {
var location = this.model.get("location");
mapContainer.css("height", "150px");
if (location.lat) {
// If map function is enabled the maptiles from the Heidelberg University are used by default.
var map = L.map(mapContainer[0]).setView([location.lat, location.lng], 14);
var tiles = L.tileLayer("http://korona.geog.uni-heidelberg.de/tiles/roads/x={x}&y={y}&z={z}", {
attribution: "Map data &copy; <a href='http://openstreetmap.org'>OpenStreetMap</a> contributors, " +
"rendering <a href='http://giscience.uni-hd.de/'>" +
"GIScience Research Group @ Heidelberg University</a>",
maxZoom: 18,
});
// If the mapbox option is enabled in the diaspora.yml, the mapbox tiles with the podmin's credentials are used.
if (gon.appConfig.map.mapbox.enabled) {
tiles = L.tileLayer("https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}", {
id: gon.appConfig.map.mapbox.id,
/* jshint camelcase: false */
accessToken: gon.appConfig.map.mapbox.access_token,
/* jshint camelcase: true */
attribution: "Map data &copy; <a href='http://openstreetmap.org'>OpenStreetMap</a> contributors, " +
"<a href='http://creativecommons.org/licenses/by-sa/2.0/''>CC-BY-SA</a>, " +
"Imagery © <a href='https://www.mapbox.com'>Mapbox</a>",
maxZoom: 18,
});
}
tiles.addTo(map);
L.marker(location).addTo(map);
mapContainer.removeClass("empty");
return map;
}
} else {
mapContainer.toggle();
}
}
});
// @license-end

View file

@ -1,6 +1,10 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.SinglePostContent = app.views.Base.extend({
events: {
"click .near-from": "toggleMap"
},
templateName: "single-post-viewer/single-post-content",
tooltipSelector: "time, .post_scope",
@ -10,8 +14,7 @@ app.views.SinglePostContent = app.views.Base.extend({
"#real-post-content" : "postContentView",
".oembed" : "oEmbedView",
".opengraph" : "openGraphView",
".status-message-location" : "postLocationStreamView",
".poll": "pollView"
".poll": "pollView",
},
initialize : function() {
@ -23,8 +26,59 @@ app.views.SinglePostContent = app.views.Base.extend({
this.pollView = new app.views.Poll({ model: this.model });
},
postLocationStreamView : function(){
return new app.views.LocationStream({ model : this.model});
map : function(){
if (this.$el.find(".mapContainer")){
// find and set height of mapContainer to max size of the container
// which is necessary to have all necessary tiles prerendered
var mapContainer = this.$el.find(".mapContainer");
mapContainer.css("height", "200px");
// get location data and render map
var location = this.model.get("location");
// If map function is enabled the maptiles from the Heidelberg University are used by default.
var map = L.map(mapContainer[0]).setView([location.lat, location.lng], 14);
var tiles = L.tileLayer("http://korona.geog.uni-heidelberg.de/tiles/roads/x={x}&y={y}&z={z}", {
attribution: "Map data &copy; <a href='http://openstreetmap.org'>OpenStreetMap</a> contributors, " +
"rendering <a href='http://giscience.uni-hd.de/'>" +
"GIScience Research Group @ Heidelberg University</a>",
maxZoom: 18,
});
// If the mapbox option is enabled in the diaspora.yml, the mapbox tiles with the podmin's credentials are used.
if (gon.appConfig.map.mapbox.enabled) {
tiles = L.tileLayer("https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}", {
id: gon.appConfig.map.mapbox.id,
/* jshint camelcase: false */
accessToken: gon.appConfig.map.mapbox.access_token,
/* jshint camelcase: true */
attribution: "Map data &copy; <a href='http://openstreetmap.org'>OpenStreetMap</a> contributors, " +
"<a href='http://creativecommons.org/licenses/by-sa/2.0/''>CC-BY-SA</a>, " +
"Imagery © <a href='https://www.mapbox.com'>Mapbox</a>",
maxZoom: 18,
});
}
tiles.addTo(map);
// set mapContainer size to a smaller preview size
mapContainer.css("height", "75px");
map.invalidateSize();
// put marker on map
L.marker(location).addTo(map);
return map;
}
},
toggleMap: function () {
$(".mapContainer").height($(".small-map")[0] ? 200 : 50);
$(".leaflet-control-zoom").css("display", $(".small-map")[0] ? "block" : "none");
$(".mapContainer").toggleClass("small-map");
},
presenter : function() {
@ -37,6 +91,10 @@ app.views.SinglePostContent = app.views.Base.extend({
showPost : function() {
return (app.currentUser.get("showNsfw")) || !this.model.get("nsfw");
},
postRenderTemplate : function(){
_.defer(_.bind(this.map, this));
}
});
// @license-end

View file

@ -45,3 +45,4 @@
//= require osmlocator
//= require bootstrap-switch
//= require blueimp-gallery
//= require leaflet

View file

@ -42,6 +42,10 @@
@import 'single-post-view';
@import 'new_styles/poll';
/* map*/
@import 'leaflet';
@import 'map';
/* conversations */
@import 'conversations';

View file

@ -0,0 +1,21 @@
.mapContainer {
position: relative;
overflow: hidden;
}
.near-from:hover {
cursor: pointer;
text-decoration: underline;
}
.leaflet-control-zoom {
display: none;
}
.leaflet-bottom .leaflet-control {
margin-bottom: 0;
}
.leaflet-right .leaflet-control {
margin-right: 0;
}

View file

@ -23,6 +23,14 @@
padding-left: 10px;
}
}
.near-from {
color: $text-grey;
font-size: 12px;
margin: 10px 20px 0px 15px;
}
.mapContainer {
margin: 10px 20px 0px 15px;
}
.row.reshare {
border-top: 1px solid lighten($border-grey,5%);
padding-top: 10px;

View file

@ -83,10 +83,13 @@
float: left;
margin-top: 6px;
}
.status-message-location .near-from {
.status-message-location {
font-size: $font-size-small;
color: $text-grey;
}
.leaflet-control-zoom {
display: block;
}
.grey { color: $text-grey; }
.post-content p:last-of-type { margin-bottom: 0; }
.nsfw-shield {

View file

@ -57,7 +57,6 @@
{{t "stream.via" provider=provider_display_name}}
{{/if}}
{{/if}}
<div class="status-message-location" />
</div>
{{#unless root}}
<div id="single-post-moderation" />
@ -68,6 +67,14 @@
<div id="single-post-actions" class="col-md-4" />
{{/unless}}
</div>
{{#if location.lat}}
<div class="row">
<div class='near-from'>
{{t "publisher.near_from" location=location.address}}
</div>
<div class="mapContainer small-map"></div>
</div>
{{/if}}
{{#if root}}
<div class="row reshare">
<div class="col-md-8" id="reshare-info">

View file

@ -1,5 +1,8 @@
{{#if location}}
{{#if location.address}}
<div class='near-from'>
{{ t "publisher.near_from" location=location}}
{{t "publisher.near_from" location=location.address}}
</div>
<div>
<div class="mapContainer empty"></div>
</div>
{{/if}}

View file

@ -145,7 +145,8 @@ class ApplicationController < ActionController::Base
def gon_set_appconfig
gon.push(appConfig: {
chat: {enabled: AppConfig.chat.enabled?},
settings: {podname: AppConfig.settings.pod_name}
settings: {podname: AppConfig.settings.pod_name},
map: {mapbox: AppConfig.map.mapbox}
})
end

View file

@ -3,7 +3,6 @@
# the COPYRIGHT file.
class Reshare < Post
belongs_to :root, :class_name => 'Post', :foreign_key => :root_guid, :primary_key => :guid
validate :root_must_be_public
validates_presence_of :root, :on => :create
@ -45,8 +44,12 @@ class Reshare < Post
absolute_root.try(:photos) || super
end
def address
absolute_root.try(:location).try(:address)
def post_location
{
address: absolute_root.try(:location).try(:address),
lat: absolute_root.try(:location).try(:lat),
lng: absolute_root.try(:location).try(:lng)
}
end
def poll

View file

@ -158,8 +158,12 @@ class StatusMessage < Post
self.open_graph_url = self.message.urls[0]
end
def address
location.try(:address)
def post_location
{
address: location.try(:address),
lat: location.try(:lat),
lng: location.try(:lng)
}
end
protected

View file

@ -31,7 +31,7 @@ class PostPresenter < BasePresenter
photos: build_photos_json,
root: root,
title: title,
address: @post.address,
location: @post.post_location,
poll: @post.poll,
already_participated_in_poll: already_participated_in_poll,
participation: participate?,

View file

@ -46,6 +46,7 @@
"HandlebarsTemplates",
"ImagePaths",
"jsxc",
"L",
"MBP",
"Routes",
"OSM",

View file

@ -76,6 +76,11 @@ defaults:
log:
file: 'log/vines.log'
level: 'info'
map:
mapbox:
enabled: false
id:
access_token:
privacy:
jquery_cdn: false
google_analytics_key:

View file

@ -327,6 +327,20 @@ configuration: ## Section
## The debug level logs all XML sent and received by the server.
#level: 'info'
## Displays the location of a post in a map. Per default we are using the map
## tiles of the Heidelberg University (http://giscience.uni-hd.de).
## You also have the possibility to use the map tiles of https://www.mapbox.com
## which is probably more reliable. There you have to create an account to get
## an ID and an access token which is limited. If you want to get an unlimited
## account you can write an email to team@diasporafoundation.org.
## Please enable mapbox and fill out your id and access_token.
map: ##Section
mapbox:
# enabled: false
# id: 'your.id'
# access_token: 'youraccesstoken'
## Settings potentially affecting the privacy of your users.
privacy: ## Section

View file

@ -140,9 +140,9 @@ FactoryGirl.define do
end
factory(:location) do
address "unicorn city"
lat 1
lng 2
address "Fernsehturm Berlin, Berlin, Germany"
lat 52.520645
lng 13.409779
end
factory(:poll) do

View file

@ -24,5 +24,10 @@ describe("app.views.Content", function(){
this.post.set({post_type : "Reshare"});
expect(this.view.presenter().isReshare).toBeTruthy();
});
it("provides location", function(){
this.post.set({location : factory.location()});
expect(this.view.presenter().location).toEqual(factory.location());
});
});
});

View file

@ -0,0 +1,45 @@
describe("app.views.LocationStream", function() {
beforeEach(function(){
this.post = factory.post();
this.view = new app.views.LocationStream({model : this.post});
/* jshint camelcase: false */
gon.appConfig = {map: { mapbox: {enabled: true, id: "yourID", access_token: "yourAccessToken" }}};
/* jshint camelcase: true */
});
describe("toggleMap", function() {
context("with location provided", function() {
beforeEach(function(){
this.post.set({location : factory.location()}); // set location
spec.content().html(this.view.render().el); // loads html element to the page
});
it("should contain a map container", function() {
expect(spec.content()).toContainElement(".mapContainer");
});
it("should initialize map", function() {
expect($(".mapContainer")).toHaveClass("empty");
this.view.toggleMap();
expect($(".mapContainer")).not.toHaveClass("empty");
});
it("should change display status on every click", function() {
this.view.toggleMap();
expect($(".mapContainer")).toHaveCss({display: "block"});
this.view.toggleMap();
expect($(".mapContainer")).toHaveCss({display: "none"});
});
});
context("without location provided", function() {
beforeEach(function(){
spec.content().html(this.view.render().el);
});
it("should not initialize the map", function() {
expect(spec.content()).not.toContainElement(".mapContainer");
});
});
});
});

View file

@ -1,6 +1,5 @@
describe("app.views.Location", function(){
beforeEach(function(){
OSM = {};
OSM.Locator = function(){return { getAddress:function(){}}};
this.view = new app.views.Location();

View file

@ -0,0 +1,50 @@
describe("app.views.SinglePostContent", function() {
beforeEach(function(){
this.post = factory.post();
this.view = new app.views.SinglePostContent({model : this.post});
gon.appConfig = { map: {mapbox: {enabled: true, id: "yourID", accessToken: "yourAccessToken" }}};
});
describe("toggleMap", function() {
context("with location provided", function() {
beforeEach(function(){
this.post.set({location : factory.location()}); // set location
spec.content().html(this.view.render().el); // loads html element to the page
});
it("should contain a map container", function() {
expect(spec.content()).toContainElement(".mapContainer");
});
it("should provide a small map", function() {
expect($(".mapContainer")).toHaveClass("small-map");
expect($(".mapContainer").height() < 100).toBeTruthy();
expect($(".mapContainer")).toBeVisible();
});
it("should toggle class small-map on every click", function(){
this.view.toggleMap();
expect($(".mapContainer")).not.toHaveClass("small-map");
this.view.toggleMap();
expect($(".mapContainer")).toHaveClass("small-map");
});
it("should change height on every click", function() {
this.view.toggleMap();
expect($(".mapContainer").height() > 100).toBeTruthy();
this.view.toggleMap();
expect($(".mapContainer").height() < 100).toBeTruthy();
});
});
context("without location provided", function() {
beforeEach(function(){
spec.content().html(this.view.render().el);
});
it("should not initialize the map", function() {
expect(spec.content()).not.toContainElement(".mapContainer");
});
});
});
});

View file

@ -148,6 +148,14 @@ var factory = {
}, overrides);
},
location : function() {
return {
address: "Starco Mart, Mission Street, San Francisco, Kalifornien, 94103, Vereinigte Staaten von Amerika",
lat: 37.78,
lng: -122.41
};
},
post : function(overrides) {
var defaultAttrs = _.extend(factory.postAttrs(), {"author" : this.author()});
return new app.models.Post(_.extend(defaultAttrs, overrides));

View file

@ -1,33 +1,33 @@
require 'spec_helper'
require "spec_helper"
describe Reshare, :type => :model do
it 'has a valid Factory' do
describe Reshare, type: :model do
it "has a valid Factory" do
expect(FactoryGirl.build(:reshare)).to be_valid
end
it 'requires root' do
reshare = FactoryGirl.build(:reshare, :root => nil)
it "requires root" do
reshare = FactoryGirl.build(:reshare, root: nil)
expect(reshare).not_to be_valid
end
it 'require public root' do
reshare = FactoryGirl.build(:reshare, :root => FactoryGirl.create(:status_message, :public => false))
it "require public root" do
reshare = FactoryGirl.build(:reshare, root: FactoryGirl.create(:status_message, public: false))
expect(reshare).not_to be_valid
expect(reshare.errors[:base]).to include('Only posts which are public may be reshared.')
expect(reshare.errors[:base]).to include("Only posts which are public may be reshared.")
end
it 'forces public' do
expect(FactoryGirl.create(:reshare, :public => false).public).to be true
it "forces public" do
expect(FactoryGirl.create(:reshare, public: false).public).to be true
end
describe "#root_diaspora_id" do
let(:reshare) { create(:reshare, root: FactoryGirl.build(:status_message, author: bob.person, public: true)) }
it "should return the root diaspora id" do
reshare = FactoryGirl.create(:reshare, root: FactoryGirl.build(:status_message, author: bob.person, public: true))
expect(reshare.root_diaspora_id).to eq(bob.person.diaspora_handle)
end
it "should be nil if no root found" do
reshare = FactoryGirl.create(:reshare, root: FactoryGirl.build(:status_message, author: bob.person, public: true))
reshare.root = nil
expect(reshare.root_diaspora_id).to be_nil
end
@ -37,195 +37,188 @@ describe Reshare, :type => :model do
let(:receive_reshare) { @reshare.receive(@root.author.owner, @reshare.author) }
before do
@reshare = FactoryGirl.create(:reshare, :root => FactoryGirl.build(:status_message, :author => bob.person, :public => true))
@reshare = FactoryGirl.create(:reshare, root:
FactoryGirl.build(:status_message, author: bob.person, public: true))
@root = @reshare.root
end
it 'increments the reshare count' do
it "increments the reshare count" do
receive_reshare
expect(@root.resharers.count).to eq(1)
end
it 'adds the resharer to the re-sharers of the post' do
it "adds the resharer to the re-sharers of the post" do
receive_reshare
expect(@root.resharers).to include(@reshare.author)
end
it 'does not error if the root author has a contact for the resharer' do
it "does not error if the root author has a contact for the resharer" do
bob.share_with @reshare.author, bob.aspects.first
expect {
Timeout.timeout(5) do
receive_reshare #This doesn't ever terminate on my machine before it was fixed.
receive_reshare # This doesn't ever terminate on my machine before it was fixed.
end
}.not_to raise_error
end
end
describe '#nsfw' do
before do
sfw = FactoryGirl.build(:status_message, :author => alice.person, :public => true)
nsfw = FactoryGirl.build(:status_message, :author => alice.person, :public => true, :text => "This is #nsfw")
@sfw_reshare = FactoryGirl.build(:reshare, :root => sfw)
@nsfw_reshare = FactoryGirl.build(:reshare, :root => nsfw)
end
describe "#nsfw" do
let(:sfw) { build(:status_message, author: alice.person, public: true) }
let(:nsfw) { build(:status_message, author: alice.person, public: true, text: "This is #nsfw") }
let(:sfw_reshare) { build(:reshare, root: sfw) }
let(:nsfw_reshare) { build(:reshare, root: nsfw) }
it 'deletates #nsfw to the root post' do
expect(@sfw_reshare.nsfw).not_to be true
expect(@nsfw_reshare.nsfw).to be_truthy
it "deletates #nsfw to the root post" do
expect(sfw_reshare.nsfw).not_to be true
expect(nsfw_reshare.nsfw).to be_truthy
end
end
describe '#poll' do
before do
@root_post = FactoryGirl.create(:status_message_with_poll, public: true)
@reshare = FactoryGirl.create(:reshare, root: @root_post)
end
describe "#poll" do
let(:root_post) { create(:status_message_with_poll, public: true) }
let(:reshare) { create(:reshare, root: root_post) }
it 'contains root poll' do
expect(@reshare.poll).to eq @root_post.poll
it "contains root poll" do
expect(reshare.poll).to eq root_post.poll
end
end
describe '#notification_type' do
before do
sm = FactoryGirl.build(:status_message, :author => alice.person, :public => true)
@reshare = FactoryGirl.build(:reshare, :root => sm)
end
it 'does not return anything for non-author of the original post' do
expect(@reshare.notification_type(bob, @reshare.author)).to be_nil
describe "#notification_type" do
let(:status_message) { build(:status_message, author: alice.person, public: true) }
let(:reshare) { build(:reshare, root: status_message) }
it "does not return anything for non-author of the original post" do
expect(reshare.notification_type(bob, reshare.author)).to be_nil
end
it 'returns "Reshared" for the original post author' do
expect(@reshare.notification_type(alice, @reshare.author)).to eq(Notifications::Reshared)
it "returns 'Reshared' for the original post author" do
expect(reshare.notification_type(alice, reshare.author)).to eq(Notifications::Reshared)
end
it 'does not error out if the root was deleted' do
@reshare.root = nil
it "does not error out if the root was deleted" do
reshare.root = nil
expect {
@reshare.notification_type(alice, @reshare.author)
reshare.notification_type(alice, reshare.author)
}.to_not raise_error
end
end
describe '#absolute_root' do
describe "#absolute_root" do
before do
@sm = FactoryGirl.build(:status_message, :author => alice.person, :public => true)
rs1 = FactoryGirl.build(:reshare, :root=>@sm)
rs2 = FactoryGirl.build(:reshare, :root=>rs1)
@rs3 = FactoryGirl.build(:reshare, :root=>rs2)
@status_message = FactoryGirl.build(:status_message, author: alice.person, public: true)
reshare_1 = FactoryGirl.build(:reshare, root: @status_message)
reshare_2 = FactoryGirl.build(:reshare, root: reshare_1)
@reshare_3 = FactoryGirl.build(:reshare, root: reshare_2)
sm = FactoryGirl.create(:status_message, :author => alice.person, :public => true)
rs1 = FactoryGirl.create(:reshare, :root => sm)
@of_deleted = FactoryGirl.build(:reshare, :root => rs1)
sm.destroy
rs1.reload
status_message = FactoryGirl.create(:status_message, author: alice.person, public: true)
reshare_1 = FactoryGirl.create(:reshare, root: status_message)
@of_deleted = FactoryGirl.build(:reshare, root: reshare_1)
status_message.destroy
reshare_1.reload
end
it 'resolves root posts to the top level' do
expect(@rs3.absolute_root).to eq(@sm)
it "resolves root posts to the top level" do
expect(@reshare_3.absolute_root).to eq(@status_message)
end
it 'can handle deleted reshares' do
it "can handle deleted reshares" do
expect(@of_deleted.absolute_root).to be_nil
end
it 'is used everywhere' do
expect(@rs3.message).to eq @sm.message
it "is used everywhere" do
expect(@reshare_3.message).to eq @status_message.message
expect(@of_deleted.message).to be_nil
expect(@rs3.photos).to eq @sm.photos
expect(@reshare_3.photos).to eq @status_message.photos
expect(@of_deleted.photos).to be_empty
expect(@rs3.o_embed_cache).to eq @sm.o_embed_cache
expect(@reshare_3.o_embed_cache).to eq @status_message.o_embed_cache
expect(@of_deleted.o_embed_cache).to be_nil
expect(@rs3.open_graph_cache).to eq @sm.open_graph_cache
expect(@reshare_3.open_graph_cache).to eq @status_message.open_graph_cache
expect(@of_deleted.open_graph_cache).to be_nil
expect(@rs3.mentioned_people).to eq @sm.mentioned_people
expect(@reshare_3.mentioned_people).to eq @status_message.mentioned_people
expect(@of_deleted.mentioned_people).to be_empty
expect(@rs3.nsfw).to eq @sm.nsfw
expect(@reshare_3.nsfw).to eq @status_message.nsfw
expect(@of_deleted.nsfw).to be_nil
expect(@rs3.address).to eq @sm.location.try(:address)
expect(@reshare_3.address).to eq @status_message.location.try(:address)
expect(@of_deleted.address).to be_nil
end
end
describe "XML" do
before do
@reshare = FactoryGirl.build(:reshare)
@xml = @reshare.to_xml.to_s
end
let(:reshare) { build(:reshare) }
let(:xml) { reshare.to_xml.to_s }
context 'serialization' do
it 'serializes root_diaspora_id' do
expect(@xml).to include("root_diaspora_id")
expect(@xml).to include(@reshare.author.diaspora_handle)
context "serialization" do
it "serializes root_diaspora_id" do
expect(xml).to include("root_diaspora_id")
expect(xml).to include(reshare.author.diaspora_handle)
end
it 'serializes root_guid' do
expect(@xml).to include("root_guid")
expect(@xml).to include(@reshare.root.guid)
it "serializes root_guid" do
expect(xml).to include("root_guid")
expect(xml).to include(reshare.root.guid)
end
end
context 'marshalling' do
context 'local' do
before do
@original_author = @reshare.root.author
@root_object = @reshare.root
context "marshalling" do
let(:root_object) { reshare.root }
context "local" do
let(:original_author) { reshare.root.author }
it "marshals the guid" do
expect(Reshare.from_xml(xml).root_guid).to eq(root_object.guid)
end
it 'marshals the guid' do
expect(Reshare.from_xml(@xml).root_guid).to eq(@root_object.guid)
it "fetches the root post from root_guid" do
expect(Reshare.from_xml(xml).root).to eq(root_object)
end
it 'fetches the root post from root_guid' do
expect(Reshare.from_xml(@xml).root).to eq(@root_object)
end
it 'fetches the root author from root_diaspora_id' do
expect(Reshare.from_xml(@xml).root.author).to eq(@original_author)
it "fetches the root author from root_diaspora_id" do
expect(Reshare.from_xml(xml).root.author).to eq(original_author)
end
end
describe 'destroy' do
it 'allows you to destroy the reshare if the root post is missing' do
reshare = FactoryGirl.build(:reshare)
describe "destroy" do
it "allows you to destroy the reshare if the root post is missing" do
reshare
reshare.root = nil
expect{
expect {
reshare.destroy
}.to_not raise_error
end
end
context 'remote' do
context "remote" do
before do
@root_object = @reshare.root
@root_object.delete
# root_object = reshare.root
root_object.delete
@response = double
allow(@response).to receive(:status).and_return(200)
allow(@response).to receive(:success?).and_return(true)
end
it 'fetches the root author from root_diaspora_id' do
@original_profile = @reshare.root.author.profile.dup
@reshare.root.author.profile.delete
@original_author = @reshare.root.author.dup
@reshare.root.author.delete
it "fetches the root author from root_diaspora_id" do
@original_profile = reshare.root.author.profile.dup
reshare.root.author.profile.delete
@original_author = reshare.root.author.dup
reshare.root.author.delete
@original_author.profile = @original_profile
expect(Person).to receive(:find_or_fetch_by_identifier).and_return(@original_author)
allow(@response).to receive(:body).and_return(@root_object.to_diaspora_xml)
allow(@response).to receive(:body).and_return(root_object.to_diaspora_xml)
expect(Faraday.default_connection).to receive(:get).with(
URI.join(
@original_author.url,
Rails.application.routes.url_helpers.short_post_path(
@root_object.guid,
root_object.guid,
format: "xml"
)
)
).and_return(@response)
Reshare.from_xml(@xml)
Reshare.from_xml(xml)
end
context "fetching post" do
@ -234,7 +227,7 @@ describe Reshare, :type => :model do
expect(Faraday.default_connection).to receive(:get).and_return(@response)
expect {
Reshare.from_xml(@xml)
Reshare.from_xml(xml)
}.to raise_error(Diaspora::PostNotFetchable)
end
@ -244,57 +237,79 @@ describe Reshare, :type => :model do
expect(Faraday.default_connection).to receive(:get).and_return(@response)
expect {
Reshare.from_xml(@xml)
Reshare.from_xml(xml)
}.to raise_error RuntimeError
end
end
context 'saving the post' do
context "saving the post" do
before do
allow(@response).to receive(:body).and_return(@root_object.to_diaspora_xml)
allow(@response).to receive(:body).and_return(root_object.to_diaspora_xml)
allow(Faraday.default_connection).to receive(:get).with(
URI.join(
@reshare.root.author.url,
reshare.root.author.url,
Rails.application.routes.url_helpers.short_post_path(
@root_object.guid,
root_object.guid,
format: "xml"
)
)
).and_return(@response)
end
it 'fetches the root post from root_guid' do
root = Reshare.from_xml(@xml).root
it "fetches the root post from root_guid" do
root = Reshare.from_xml(xml).root
[:text, :guid, :diaspora_handle, :type, :public].each do |attr|
expect(root.send(attr)).to eq(@reshare.root.send(attr))
%i(text guid diaspora_handle type public).each do |attr|
expect(root.send(attr)).to eq(reshare.root.send(attr))
end
end
it 'correctly saves the type' do
expect(Reshare.from_xml(@xml).root.reload.type).to eq("StatusMessage")
it "correctly saves the type" do
expect(Reshare.from_xml(xml).root.reload.type).to eq("StatusMessage")
end
it 'correctly sets the author' do
@original_author = @reshare.root.author
expect(Reshare.from_xml(@xml).root.reload.author.reload).to eq(@original_author)
it "correctly sets the author" do
@original_author = reshare.root.author
expect(Reshare.from_xml(xml).root.reload.author.reload).to eq(@original_author)
end
it 'verifies that the author of the post received is the same as the author in the reshare xml' do
@original_author = @reshare.root.author.dup
@xml = @reshare.to_xml.to_s
it "verifies that the author of the post received is the same as the author in the reshare xml" do
@original_author = reshare.root.author.dup
xml = reshare.to_xml.to_s
different_person = FactoryGirl.build(:person)
expect(Person).to receive(:find_or_fetch_by_identifier).and_return(different_person)
allow(different_person).to receive(:url).and_return(@original_author.url)
expect{
Reshare.from_xml(@xml)
expect {
Reshare.from_xml(xml)
}.to raise_error /^Diaspora ID \(.+\) in the root does not match the Diaspora ID \(.+\) specified in the reshare!$/
end
end
end
end
end
describe "#post_location" do
let(:status_message) { build(:status_message, text: "This is a status_message", author: bob.person, public: true) }
let(:reshare) { create(:reshare, root: status_message) }
context "with location" do
let(:location) { build(:location) }
it "should deliver address and coordinates" do
status_message.location = location
expect(reshare.post_location).to include(address: location.address, lat: location.lat, lng: location.lng)
end
end
context "without location" do
it "should deliver empty address and coordinates" do
expect(reshare.post_location[:address]).to be_nil
expect(reshare.post_location[:lat]).to be_nil
expect(reshare.post_location[:lng]).to be_nil
end
end
end
end

View file

@ -2,56 +2,54 @@
# licensed under the Affero General Public License version 3 or later. See
# the COPYRIGHT file.
require 'spec_helper'
require "spec_helper"
describe StatusMessage, :type => :model do
describe StatusMessage, type: :model do
include PeopleHelper
before do
@user = alice
@aspect = @user.aspects.first
end
let!(:user) { alice }
let!(:aspect) { user.aspects.first }
let(:status) { build(:status_message) }
describe 'scopes' do
describe '.where_person_is_mentioned' do
it 'returns status messages where the given person is mentioned' do
@bo = bob.person
@test_string = "@{Daniel; #{@bo.diaspora_handle}} can mention people like Raph"
describe "scopes" do
describe ".where_person_is_mentioned" do
it "returns status messages where the given person is mentioned" do
@bob = bob.person
@test_string = "@{Daniel; #{@bob.diaspora_handle}} can mention people like Raph"
FactoryGirl.create(:status_message, text: @test_string)
FactoryGirl.create(:status_message, text: @test_string)
FactoryGirl.create(:status_message)
FactoryGirl.create(:status_message, :text => @test_string )
FactoryGirl.create(:status_message, :text => @test_string )
FactoryGirl.create(:status_message)
expect(StatusMessage.where_person_is_mentioned(@bo).count).to eq(2)
expect(StatusMessage.where_person_is_mentioned(bob).count).to eq(2)
end
end
context "tag_streams" do
before do
@sm1 = FactoryGirl.create(:status_message, :text => "#hashtag" , :public => true)
@sm2 = FactoryGirl.create(:status_message, :text => "#hashtag" )
@sm3 = FactoryGirl.create(:status_message, :text => "hashtags are #awesome", :public => true )
@sm4 = FactoryGirl.create(:status_message, :text => "hashtags are #awesome" )
@status_message_1 = FactoryGirl.create(:status_message, text: "#hashtag", public: true)
@status_message_2 = FactoryGirl.create(:status_message, text: "#hashtag")
@status_message_3 = FactoryGirl.create(:status_message, text: "hashtags are #awesome", public: true)
@status_message_4 = FactoryGirl.create(:status_message, text: "hashtags are #awesome")
@tag_id = ActsAsTaggableOn::Tag.where(:name => "hashtag").first.id
@tag_id = ActsAsTaggableOn::Tag.where(name: "hashtag").first.id
end
describe '.tag_steam' do
it 'returns status messages tagged with the tag' do
describe ".tag_steam" do
it "returns status messages tagged with the tag" do
tag_stream = StatusMessage.send(:tag_stream, [@tag_id])
expect(tag_stream).to include @sm1
expect(tag_stream).to include @sm2
expect(tag_stream).to include @status_message_1
expect(tag_stream).to include @status_message_2
end
end
describe '.public_tag_stream' do
it 'returns public status messages tagged with the tag' do
expect(StatusMessage.public_tag_stream([@tag_id])).to eq([@sm1])
describe ".public_tag_stream" do
it "returns public status messages tagged with the tag" do
expect(StatusMessage.public_tag_stream([@tag_id])).to eq([@status_message_1])
end
end
describe '.user_tag_stream' do
it 'returns tag stream thats owned or visible by' do
describe ".user_tag_stream" do
it "returns tag stream thats owned or visible by" do
relation = double
expect(StatusMessage).to receive(:owned_or_visible_by_user).with(bob).and_return(relation)
expect(relation).to receive(:tag_stream).with([@tag_id])
@ -63,48 +61,45 @@ describe StatusMessage, :type => :model do
end
describe ".guids_for_author" do
it 'returns an array of the status_message guids' do
sm1 = FactoryGirl.create(:status_message, :author => alice.person)
sm2 = FactoryGirl.create(:status_message, :author => bob.person)
it "returns an array of the status_message guids" do
status_message_1 = FactoryGirl.create(:status_message, author: alice.person)
FactoryGirl.create(:status_message, author: bob.person)
guids = StatusMessage.guids_for_author(alice.person)
expect(guids).to eq([sm1.guid])
expect(guids).to eq([status_message_1.guid])
end
end
describe '.before_validation' do
it 'calls build_tags' do
status = FactoryGirl.build(:status_message)
describe ".before_validation" do
it "calls build_tags" do
expect(status).to receive(:build_tags)
status.save
end
end
describe '.before_create' do
it 'calls build_tags' do
status = FactoryGirl.build(:status_message)
describe ".before_create" do
it "calls build_tags" do
expect(status).to receive(:build_tags)
status.save
end
it 'calls filter_mentions' do
status = FactoryGirl.build(:status_message)
it "calls filter_mentions" do
expect(status).to receive(:filter_mentions)
status.save
end
end
describe '.after_create' do
it 'calls create_mentions' do
describe ".after_create" do
it "calls create_mentions" do
status = FactoryGirl.build(:status_message, text: "text @{Test; #{alice.diaspora_handle}}")
expect(status).to receive(:create_mentions).and_call_original
status.save
end
end
describe '#diaspora_handle=' do
it 'sets #author' do
describe "#diaspora_handle=" do
it "sets #author" do
person = FactoryGirl.create(:person)
post = FactoryGirl.build(:status_message, :author => @user.person)
post = FactoryGirl.build(:status_message, author: user.person)
post.diaspora_handle = person.diaspora_handle
expect(post.author).to eq(person)
end
@ -112,105 +107,104 @@ describe StatusMessage, :type => :model do
context "emptyness" do
it "needs either a message or at least one photo" do
n = @user.build_post(:status_message, :text => nil)
expect(n).not_to be_valid
post = user.build_post(:status_message, text: nil)
expect(post).not_to be_valid
n.text = ""
expect(n).not_to be_valid
post.text = ""
expect(post).not_to be_valid
n.text = "wales"
expect(n).to be_valid
n.text = nil
post.text = "wales"
expect(post).to be_valid
post.text = nil
photo = @user.build_post(:photo, :user_file => uploaded_photo, :to => @aspect.id)
photo = user.build_post(:photo, user_file: uploaded_photo, to: aspect.id)
photo.save!
n.photos << photo
expect(n).to be_valid
expect(n.errors.full_messages).to eq([])
post.photos << photo
expect(post).to be_valid
expect(post.errors.full_messages).to eq([])
end
it "doesn't check for content when author is remote (federation...)" do
p = FactoryGirl.build(:status_message, text: nil)
expect(p).to be_valid
post = FactoryGirl.build(:status_message, text: nil)
expect(post).to be_valid
end
end
it 'should be postable through the user' do
it "should be postable through the user" do
message = "Users do things"
status = @user.post(:status_message, :text => message, :to => @aspect.id)
status = user.post(:status_message, text: message, to: aspect.id)
db_status = StatusMessage.find(status.id)
expect(db_status.text).to eq(message)
end
it 'should require status messages not be more than 65535 characters long' do
message = 'a' * (65535+1)
status_message = FactoryGirl.build(:status_message, :text => message)
it "should require status messages not be more than 65535 characters long" do
message = "a" * (65_535 + 1)
status_message = FactoryGirl.build(:status_message, text: message)
expect(status_message).not_to be_valid
end
describe 'mentions' do
before do
@people = [alice, bob, eve].map{|u| u.person}
@test_string = <<-STR
@{Raphael; #{@people[0].diaspora_handle}} can mention people like Raphael @{Ilya; #{@people[1].diaspora_handle}}
can mention people like Raphaellike Raphael @{Daniel; #{@people[2].diaspora_handle}} can mention people like Raph
STR
@sm = FactoryGirl.create(:status_message, :text => @test_string )
end
describe "mentions" do
let(:people) { [alice, bob, eve].map(&:person) }
let(:test_string) {
"@{Raphael; #{people[0].diaspora_handle}} can mention people like Raphael @{Ilya; #{people[1].diaspora_handle}}
can mention people like Raphaellike Raphael @{Daniel; #{people[2].diaspora_handle}} can mention people like Raph"
}
let(:status_message) { create(:status_message, text: test_string) }
describe '#create_mentions' do
it 'creates a mention for everyone mentioned in the message' do
expect(Diaspora::Mentionable).to receive(:people_from_string).and_return(@people)
@sm.mentions.delete_all
@sm.create_mentions
expect(@sm.mentions(true).map{|m| m.person}.to_set).to eq(@people.to_set)
describe "#create_mentions" do
it "creates a mention for everyone mentioned in the message" do
status_message
expect(Diaspora::Mentionable).to receive(:people_from_string).and_return(people)
status_message.mentions.delete_all
status_message.create_mentions
expect(status_message.mentions(true).map(&:person).to_set).to eq(people.to_set)
end
it 'does not barf if it gets called twice' do
@sm.create_mentions
it "does not barf if it gets called twice" do
status_message.create_mentions
expect{
@sm.create_mentions
expect {
status_message.create_mentions
}.to_not raise_error
end
end
describe '#mentioned_people' do
it 'calls create_mentions if there are no mentions in the db' do
@sm.mentions.delete_all
expect(@sm).to receive(:create_mentions)
@sm.mentioned_people
describe "#mentioned_people" do
it "calls create_mentions if there are no mentions in the db" do
status_message.mentions.delete_all
expect(status_message).to receive(:create_mentions)
status_message.mentioned_people
end
it 'returns the mentioned people' do
@sm.mentions.delete_all
expect(@sm.mentioned_people.to_set).to eq(@people.to_set)
it "returns the mentioned people" do
status_message.mentions.delete_all
expect(status_message.mentioned_people.to_set).to eq(people.to_set)
end
it 'does not call create_mentions if there are mentions in the db' do
expect(@sm).not_to receive(:create_mentions)
@sm.mentioned_people
it "does not call create_mentions if there are mentions in the db" do
expect(status_message).not_to receive(:create_mentions)
status_message.mentioned_people
end
end
describe "#mentions?" do
it 'returns true if the person was mentioned' do
expect(@sm.mentions?(@people[0])).to be true
it "returns true if the person was mentioned" do
expect(status_message.mentions?(people[0])).to be true
end
it 'returns false if the person was not mentioned' do
expect(@sm.mentions?(FactoryGirl.build(:person))).to be false
it "returns false if the person was not mentioned" do
expect(status_message.mentions?(FactoryGirl.build(:person))).to be false
end
end
describe "#notify_person" do
it 'notifies the person mentioned' do
it "notifies the person mentioned" do
expect(Notification).to receive(:notify).with(alice, anything, anything)
@sm.notify_person(alice.person)
status_message.notify_person(alice.person)
end
end
describe "#filter_mentions" do
it 'calls Diaspora::Mentionable#filter_for_aspects' do
it "calls Diaspora::Mentionable#filter_for_aspects" do
msg = FactoryGirl.build(:status_message_in_aspect)
msg_txt = msg.raw_message
@ -233,222 +227,241 @@ STR
end
describe "#nsfw" do
it 'returns MatchObject (true) if the post contains #nsfw (however capitalised)' do
status = FactoryGirl.build(:status_message, :text => "This message is #nSFw")
it "returns MatchObject (true) if the post contains #nsfw (however capitalised)" do
status = FactoryGirl.build(:status_message, text: "This message is #nSFw")
expect(status.nsfw).to be_truthy
end
it 'returns nil (false) if the post does not contain #nsfw' do
status = FactoryGirl.build(:status_message, :text => "This message is #sFW")
it "returns nil (false) if the post does not contain #nsfw" do
status = FactoryGirl.build(:status_message, text: "This message is #sFW")
expect(status.nsfw).to be false
end
end
describe 'tags' do
describe "tags" do
before do
@object = FactoryGirl.build(:status_message)
end
it_should_behave_like 'it is taggable'
it_should_behave_like "it is taggable"
it 'associates different-case tags to the same tag entry' do
it "associates different-case tags to the same tag entry" do
assert_equal ActsAsTaggableOn.force_lowercase, true
msg_lc = FactoryGirl.build(:status_message, :text => '#newhere')
msg_uc = FactoryGirl.build(:status_message, :text => '#NewHere')
msg_cp = FactoryGirl.build(:status_message, :text => '#NEWHERE')
msg_lc = FactoryGirl.build(:status_message, text: "#newhere")
msg_uc = FactoryGirl.build(:status_message, text: "#NewHere")
msg_cp = FactoryGirl.build(:status_message, text: "#NEWHERE")
msg_lc.save; msg_uc.save; msg_cp.save
msg_lc.save
msg_uc.save
msg_cp.save
tag_array = msg_lc.tags
expect(msg_uc.tags).to match_array(tag_array)
expect(msg_cp.tags).to match_array(tag_array)
end
it 'should require tag name not be more than 255 characters long' do
message = "##{'a' * (255+1)}"
status_message = FactoryGirl.build(:status_message, :text => message)
it "should require tag name not be more than 255 characters long" do
message = "##{'a' * (255 + 1)}"
status_message = FactoryGirl.build(:status_message, text: message)
expect(status_message).not_to be_valid
end
end
describe "XML" do
let(:message) { FactoryGirl.build(:status_message, text: "I hate WALRUSES!", author: @user.person) }
let(:message) { FactoryGirl.build(:status_message, text: "I hate WALRUSES!", author: user.person) }
let(:xml) { message.to_xml.to_s }
let(:marshalled) { StatusMessage.from_xml(xml) }
it 'serializes the escaped, unprocessed message' do
it "serializes the escaped, unprocessed message" do
text = "[url](http://example.org)<script> alert('xss should be federated');</script>"
message.text = text
expect(xml).to include Builder::XChar.encode(text)
end
it 'serializes the message' do
it "serializes the message" do
expect(xml).to include "<raw_message>I hate WALRUSES!</raw_message>"
end
it 'serializes the author address' do
expect(xml).to include(@user.person.diaspora_handle)
it "serializes the author address" do
expect(xml).to include(user.person.diaspora_handle)
end
describe '.from_xml' do
it 'marshals the message' do
describe ".from_xml" do
it "marshals the message" do
expect(marshalled.text).to eq("I hate WALRUSES!")
end
it 'marshals the guid' do
it "marshals the guid" do
expect(marshalled.guid).to eq(message.guid)
end
it 'marshals the author' do
it "marshals the author" do
expect(marshalled.author).to eq(message.author)
end
it 'marshals the diaspora_handle' do
it "marshals the diaspora_handle" do
expect(marshalled.diaspora_handle).to eq(message.diaspora_handle)
end
end
context 'with some photos' do
context "with some photos" do
before do
message.photos << FactoryGirl.build(:photo)
message.photos << FactoryGirl.build(:photo)
end
it 'serializes the photos' do
it "serializes the photos" do
expect(xml).to include "photo"
expect(xml).to include message.photos.first.remote_photo_path
end
describe '.from_xml' do
it 'marshals the photos' do
describe ".from_xml" do
it "marshals the photos" do
expect(marshalled.photos.size).to eq(2)
end
it 'handles existing photos' do
it "handles existing photos" do
message.photos.each(&:save!)
expect(marshalled).to be_valid
end
end
end
context 'with a location' do
context "with a location" do
before do
message.location = FactoryGirl.build(:location)
end
it 'serializes the location' do
it "serializes the location" do
expect(xml).to include "location"
expect(xml).to include "lat"
expect(xml).to include "lng"
end
describe ".from_xml" do
it 'marshals the location' do
it "marshals the location" do
expect(marshalled.location).to be_present
end
end
end
context 'with a poll' do
context "with a poll" do
before do
message.poll = FactoryGirl.build(:poll)
end
it 'serializes the poll' do
it "serializes the poll" do
expect(xml).to include "poll"
expect(xml).to include "question"
expect(xml).to include "poll_answer"
end
describe ".from_xml" do
it 'marshals the poll' do
it "marshals the poll" do
expect(marshalled.poll).to be_present
end
it 'marshals the poll answers' do
it "marshals the poll answers" do
expect(marshalled.poll.poll_answers.size).to eq(2)
end
end
end
end
describe '#after_dispatch' do
describe "#after_dispatch" do
before do
@photos = [alice.build_post(:photo, :pending => true, :user_file=> File.open(photo_fixture_name)),
alice.build_post(:photo, :pending => true, :user_file=> File.open(photo_fixture_name))]
@photos = [alice.build_post(:photo, pending: true, user_file: File.open(photo_fixture_name)),
alice.build_post(:photo, pending: true, user_file: File.open(photo_fixture_name))]
@photos.each(&:save!)
@status_message = alice.build_post(:status_message, :text => "the best pebble.")
@status_message.photos << @photos
@status_message = alice.build_post(:status_message, text: "the best pebble.")
@status_message.photos << @photos
@status_message.save!
alice.add_to_streams(@status_message, alice.aspects)
end
it 'sets pending to false on any attached photos' do
it "sets pending to false on any attached photos" do
@status_message.after_dispatch(alice)
expect(@photos.all?{|p| p.reload.pending}).to be false
expect(@photos.all? {|p| p.reload.pending }).to be false
end
it 'dispatches any attached photos' do
it "dispatches any attached photos" do
expect(alice).to receive(:dispatch_post).twice
@status_message.after_dispatch(alice)
end
end
describe 'oembed' do
before do
@youtube_url = "https://www.youtube.com/watch?v=3PtFwlKfvHI"
@message_text = "#{@youtube_url} is so cool. so is this link -> https://joindiaspora.com"
end
describe "oembed" do
let(:youtube_url) { "https://www.youtube.com/watch?v=3PtFwlKfvHI" }
let(:message_text) { "#{youtube_url} is so cool. so is this link -> https://joindiaspora.com" }
let(:status_message) { FactoryGirl.build(:status_message, text: message_text) }
it 'should queue a GatherOembedData if it includes a link' do
sm = FactoryGirl.build(:status_message, :text => @message_text)
it "should queue a GatherOembedData if it includes a link" do
status_message
expect(Workers::GatherOEmbedData).to receive(:perform_async).with(instance_of(Fixnum), instance_of(String))
sm.save
status_message.save
end
describe '#contains_oembed_url_in_text?' do
it 'returns the oembed urls found in the raw message' do
sm = FactoryGirl.build(:status_message, :text => @message_text)
expect(sm.contains_oembed_url_in_text?).not_to be_nil
expect(sm.oembed_url).to eq(@youtube_url)
describe "#contains_oembed_url_in_text?" do
it "returns the oembed urls found in the raw message" do
expect(status_message.contains_oembed_url_in_text?).not_to be_nil
expect(status_message.oembed_url).to eq(youtube_url)
end
end
end
describe 'opengraph' do
before do
@ninegag_url = "http://9gag.com/gag/a1AMW16"
@youtube_url = "https://www.youtube.com/watch?v=3PtFwlKfvHI"
@message_text = "#{@ninegag_url} is so cool. so is this link -> https://joindiaspora.com"
@oemessage_text = "#{@youtube_url} is so cool. so is this link -> https://joindiaspora.com"
end
describe "opengraph" do
let(:ninegag_url) { "http://9gag.com/gag/a1AMW16" }
let(:youtube_url) { "https://www.youtube.com/watch?v=3PtFwlKfvHI" }
let(:message_text) { "#{ninegag_url} is so cool. so is this link -> https://joindiaspora.com" }
let(:oemessage_text) { "#{youtube_url} is so cool. so is this link -> https://joindiaspora.com" }
let(:status_message) { build(:status_message, text: message_text) }
it 'should queue a GatherOpenGraphData if it includes a link' do
sm = FactoryGirl.build(:status_message, :text => @message_text)
it "should queue a GatherOpenGraphData if it includes a link" do
status_message
expect(Workers::GatherOpenGraphData).to receive(:perform_async).with(instance_of(Fixnum), instance_of(String))
sm.save
status_message.save
end
describe '#contains_open_graph_url_in_text?' do
it 'returns the opengraph urls found in the raw message' do
sm = FactoryGirl.build(:status_message, :text => @message_text)
expect(sm.contains_open_graph_url_in_text?).not_to be_nil
expect(sm.open_graph_url).to eq(@ninegag_url)
describe "#contains_open_graph_url_in_text?" do
it "returns the opengraph urls found in the raw message" do
expect(status_message.contains_open_graph_url_in_text?).not_to be_nil
expect(status_message.open_graph_url).to eq(ninegag_url)
end
it 'returns nil if the link is from trusted oembed provider' do
sm = FactoryGirl.build(:status_message, :text => @oemessage_text)
expect(sm.contains_open_graph_url_in_text?).to be_nil
expect(sm.open_graph_url).to be_nil
it "returns nil if the link is from trusted oembed provider" do
status_message = FactoryGirl.build(:status_message, text: oemessage_text)
expect(status_message.contains_open_graph_url_in_text?).to be_nil
expect(status_message.open_graph_url).to be_nil
end
end
end
describe "validation" do
let(:status_message) { build(:status_message, text: @message_text) }
it "should not be valid if the author is missing" do
sm = FactoryGirl.build(:status_message, text: @message_text)
sm.author = nil
expect(sm).not_to be_valid
status_message.author = nil
expect(status_message).not_to be_valid
end
end
describe "#coordinates" do
let(:status_message) { build(:status_message, text: @message_text) }
context "with location" do
let(:location) { build(:location) }
it "should deliver address and coordinates" do
status_message.location = location
expect(status_message.post_location).to include(address: location.address, lat: location.lat, lng: location.lng)
end
end
context "without location" do
it "should deliver empty address and coordinates" do
expect(status_message.post_location[:address]).to be_nil
expect(status_message.post_location[:lat]).to be_nil
expect(status_message.post_location[:lng]).to be_nil
end
end
end
end