write some basic tests

This commit is contained in:
joshua stein 2012-06-30 17:41:14 -05:00
parent f801932a03
commit 1bf98318c8
5 changed files with 225 additions and 0 deletions

77
spec/fixtures/story_pages/1.html vendored Normal file
View file

@ -0,0 +1,77 @@
<!DOCTYPE html>
<meta name="description" content="B2G demo &amp; quick hack - Paul Rouget's Blog"/>
<meta name="author" content="Paul Rouget"/>
<meta charset="utf8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="alternate" type="application/atom+xml" href="/index.xml"/>
<link rel="stylesheet" href="/css/mop.css">
<title>B2G demo &amp; quick hack // by Paul Rouget</title>
<body>
<article>
<p>Very very quick demo of B2G followed by a quick hack of the homescreen:</p>
<iframe style="max-width: 600px; width:100%;height:400px" src="http://www.youtube.com/embed/K_g29YeLzyg" frameborder="0" allowfullscreen>
<a href="http://www.youtube.com/watch?v=K_g29YeLzyg">Youtube video.</a>
</iframe>
<p>You can find the code of the UI of B2g (gaia) <a href="https://github.com/mozilla-b2g/gaia">on github</a>. Reminder:</p>
<h2>What is <a href="https://wiki.mozilla.org/B2G">Boot To Gecko</a>:</h2>
<ul>
<li>An Operating System;
<li>Linux Kernel + drivers from Android (so B2G can run on most of the phones that are capable of running Android). Works on ARMv7, will probably work on ARMv6;
<li>Gecko on top of it. The same version you'll find on a regular Firefox;
<li>Apps are written in HTML, JavaScript and CSS;
<li><strong>You don't need to be online to use the Apps</strong>;
<li>A set of new JavaScript APIs:
<ul>
<li><a href="http://wiki.mozilla.org/WebAPI">WebAPIs</a>, for bluetooth, sms, telephony, …
<li><a href="http://www.webrtc.org/">WebRTC</a>, for streaming, webcam, P2P, …
</ul>
<li>We are working on <a href="http://brendaneich.com/2012/02/mobile-web-api-evolution/">standardizing these APIs</a>;
<li>The homescreen/launcher is an HTML page. Mozilla's version is named <a href="http://wiki.mozilla.org/gaia">Gaia</a>;
<li>You can build your own homescreen/launcher;
<li>The name of the project will probably change;
<li>It's possible to implement Boot To Webkit too;
<li>It's Open Source;
<li>We (Mozilla) <a href="http://blog.mozilla.com/blog/2012/02/22/mozilla-marketplace-opening-for-app-submissions-soon/">are working on a MarketPlace</a>.
</ul>
</p>
<footer>
<span>11 Jun 2012</span>
/ <a onclick="window.open('http://twitter.com/share?text=B2G demo &amp; quick hack&via=paulrouget&url=http%3A%2F%2Fpaulrouget.com%2Fe/b2ghack','','width=400,height=300,personalbar=0,toolbar=0,scrollbars=1,resizable=1'); return false;" href="http://twitter.com/share?text=B2G demo &amp; quick hack&via=paulrouget&url=http%3A%2F%2Fpaulrouget.com%2Fe/b2ghack" id="twitter" title="tweet this" target="_blank">
<span>tweet&nbsp;this</span>
</a>
</footer>
</article>
</body>
<aside>
<p><a href="http://paulrouget.com"><img id="avatar" src="http://paulrouget.com/assets/twitter_avatar.jpg"></a>Hi. I'm Paul Rouget (<a href="/s/contact">contact</a>). I work at <a href="http://mozilla.org">Mozilla</a>. I write <a href="https://wiki.mozilla.org/DevTools">code</a>. I give talks. I <a href="http://twitter.com/paulrouget">tweet</a>.
This is my blog (<a href="http://paulrouget.com/index.xml">rss</a>).
<hr>
</aside>
<!-- Analytics -->
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-22781000-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>

19
spec/fixtures/story_pages/2.html vendored Normal file
View file

@ -0,0 +1,19 @@
<!doctype html><html itemscope itemtype="http://schema.org/WebPage"><head><meta http-equiv="content-type" content="text/html; charset=ISO-8859-1"><meta name="description" content="Search the world&#39;s information, including webpages, images, videos and more. Google has many special features to help you find exactly what you&#39;re looking for."><meta name="robots" content="noodp"><meta itemprop="image" content="/images/google_favicon_128.png"><title>Google</title><script>window.google={kEI:"nH3vT8qiA8LWqgHK0uiNDw",getEI:function(a){var b;while(a&&!(a.getAttribute&&(b=a.getAttribute("eid"))))a=a.parentNode;return b||google.kEI},https:function(){return window.location.protocol=="https:"},kEXPI:"25657,37607,38788,38816,39366,39370,39411,39604,39664,39730",kCSI:{e:"25657,37607,38788,38816,39366,39370,39411,39604,39664,39730",ei:"nH3vT8qiA8LWqgHK0uiNDw"},authuser:0,
ml:function(){},kHL:"en",time:function(){return(new Date).getTime()},log:function(a,b,c,e){var d=new Image,h=google,i=h.lc,f=h.li,j="";d.onerror=(d.onload=(d.onabort=function(){delete i[f]}));i[f]=d;if(!c&&b.search("&ei=")==-1)j="&ei="+google.getEI(e);var g=c||"/gen_204?atyp=i&ct="+a+"&cad="+b+j+"&zx="+google.time();
var k=/^http:/i;if(k.test(g)&&google.https()){google.ml(new Error("GLMM"),false,{src:g});delete i[f];return}d.src=g;h.li=f+1},lc:[],li:0,Toolbelt:{},y:{},x:function(a,b){google.y[a.id]=[a,b];return false}};
window.google.sn="webhp";window.google.timers={};window.google.startTick=function(a,b){window.google.timers[a]={t:{start:(new Date).getTime()},bfr:!(!b)}};window.google.tick=function(a,b,c){if(!window.google.timers[a])google.startTick(a);window.google.timers[a].t[b]=c||(new Date).getTime()};google.startTick("load",true);try{}catch(u){}
var _gjwl=location;function _gjuc(){var e=_gjwl.href.indexOf("#");if(e>=0){var a=_gjwl.href.substring(e);if(a.indexOf("&q=")>0||a.indexOf("#q=")>=0){a=a.substring(1);if(a.indexOf("#")==-1){for(var c=0;c<a.length;){var d=c;if(a.charAt(d)=="&")++d;var b=a.indexOf("&",d);if(b==-1)b=a.length;var f=a.substring(d,b);if(f.indexOf("fp=")==0){a=a.substring(0,c)+a.substring(b,a.length);b=c}else if(f=="cad=h")return 0;c=b}_gjwl.href="/search?"+a+"&cad=h";return 1}}}return 0}function _gjp(){!(window._gjwl.hash&&
window._gjuc())&&setTimeout(_gjp,500)};
window._gjp && _gjp()</script><style>#gbar,#guser{font-size:13px;padding-top:1px !important;}#gbar{height:22px}#guser{padding-bottom:7px !important;text-align:right}.gbh,.gbd{border-top:1px solid #c9d7f1;font-size:1px}.gbh{height:0;position:absolute;top:24px;width:100%}@media all{.gb1{height:22px;margin-right:.5em;vertical-align:top}#gbar{float:left}}a.gb1,a.gb4{text-decoration:underline !important}a.gb1,a.gb4{color:#00c !important}</style><style id=gstyle>body{margin:0;overflow-y:scroll}#gog{padding:3px 8px 0}td{line-height:.8em}.gac_m td{line-height:17px}form{margin-bottom:20px}body,td,a,p,.h{font-family:arial,sans-serif}.h{color:#36c;font-size:20px}.q{color:#00c}.ts td{padding:0}.ts{border-collapse:collapse}em{font-weight:bold;font-style:normal}.lst{height:25px;width:496px}.gsfi,.lst{font:18px arial,sans-serif}.gsfs{font:17px arial,sans-serif}.ds{display:-moz-inline-box;display:inline-block;margin:3px 0 4px;margin-left:4px}input{font-family:inherit}a.gb1,a.gb2,a.gb3,a.gb4{color:#11c !important}body{background:#fff;color:black}a{color:#11c;text-decoration:none}a:hover,a:active{text-decoration:underline}.fl a{color:#36c}a:visited{color:#551a8b}a.gb1,a.gb4{text-decoration:underline}a.gb3:hover{text-decoration:none}#ghead a.gb2:hover{color:#fff!important}.sblc{padding-top:5px}.sblc a{display:block;margin:2px 0;margin-left:13px;font-size:11px;}.lsbb{background:#eee;border:solid 1px;border-color:#ccc #999 #999 #ccc;height:30px;display:block}.ftl,#fll a{display:inline-block;margin:0 12px}.lsb{background:url(/images/srpr/nav_logo80.png) 0 -258px repeat-x;border:none;color:#000;cursor:pointer;height:30px;margin:0;outline:0;font:15px arial,sans-serif;vertical-align:top}.lsb:active{background:#ccc}.lst:focus{outline:none}#addlang a{padding:0 3px}.gac_v div{display:none}.gac_v .gac_v2,.gac_bt{display:block!important}table.gssb_c{z-index:986 }.nbcl{background:url(/images/srpr/nav_logo80.png) no-repeat ;height:px;width:px}</style><script>window.rwt=function(a,f,g,l,m,h,c,n,i){try{if(a===window){a=window.event.srcElement;while(a){if(a.href)break;a=a.parentNode}}var b=encodeURIComponent||escape,d;d=a.getAttribute("href");if(c&&c.substring(0,6)!="&sig2=")c="&sig2="+c;var o=["/url?sa=t","","&cd=",b(m),i?"&authuser="+b(i):"",google.j&&google.j.pf?"&sqi=2":"","&ved=",b(n),"&url=",b(d).replace(/\+/g,"%2B"),"&ei=","nH3vT8qiA8LWqgHK0uiNDw",h?"&usg="+h:"",c].join("");a.href=o;a.onmousedown=""}catch(p){}return true};
</script></head><body bgcolor=#ffffff text=#000000 link=#0000cc vlink=#551a8b alink=#ff0000 onload="document.f&&document.f.q.focus();document.gbqf&&document.gbqf.q.focus();if(document.images)new Image().src='/images/srpr/nav_logo80.png'" ><textarea id=csi style=display:none></textarea><div id=mngb><div id=guser width=100%><nobr><span id=gbn class=gbi></span><span id=gbf class=gbf></span><span id=gbe><a href="/url?sa=p&pref=ig&pval=3&q=http://www.google.com/ig%3Fhl%3Den%26source%3Diglk&usg=AFQjCNFA18XPfgb7dKnXfKz7x7g1GDH1tg" class=gb4>iGoogle</a> | </span><a href="http://www.google.com/history/optout?hl=en" class=gb4>Web History</a> | <a href="/preferences?hl=en" class=gb4>Settings</a> | <a target=_top id=gb_70 href="https://accounts.google.com/ServiceLogin?hl=en&continue=https://encrypted.google.com/" class=gb4>Sign in</a></nobr></div><div class=gbh style=left:0></div><div class=gbh style=right:0></div></div><iframe name=wgjf style=display:none></iframe><center><br clear=all id=lgpd><div id=lga><img alt="Google" height=95 id=hplogo src="/intl/en_ALL/images/srpr/logo1w.png" width=275 style="padding:28px 0 14px" onload="window.lol&&lol()"><br><br></div><form action="/search" name=f><table cellpadding=0 cellspacing=0><tr valign=top><td width=25%>&nbsp;</td><td align=center nowrap><input type=hidden name=ie value="ISO-8859-1"><input name=hl type=hidden value=en><input name=source type=hidden value=hp><div class=ds style="height:32px;margin:4px 0"><input autocomplete=off maxlength=2048 name=q class="lst" title="Google Search" value="" size=57 style="color:#000;margin:0;padding:5px 8px 0 6px;vertical-align:top"></div><br style="line-height:0"><span class=ds ><span class=lsbb><input name=btnG type=submit value="Google Search" class=lsb></span></span><span class=ds><span class=lsbb><input name=btnI type=submit class=lsb value="I&#39;m Feeling Lucky" onclick="if(this.form.q.value)this.checked=1;else top.location='/doodles/'"></span></span></td><td nowrap width=25% align=left class="fl sblc"><a href="/advanced_search?hl=en">Advanced search</a><a href="/language_tools?hl=en">Language tools</a></td></tr></table><input type=hidden id=gbv name=gbv value="1"></form><div style="font-size:83%;min-height:3.5em"><br></div><span id=footer><center id=fctr><div style="font-size:10pt"><div id=fll style="margin:19px auto;text-align:center"><a href="/intl/en/ads/">Advertising&nbsp;Programs</a><a href="/services/">Business Solutions</a><a href="https://plus.google.com/116899029375914044550" rel="publisher">+Google</a><a href="/intl/en/about.html">About Google</a></div></div><p style="color:#767676;font-size:8pt">&copy; 2012 - <a href="/intl/en/policies/">Privacy & Terms</a></p></center></span> </center><div id=xjsd></div><div id=xjsi><script>if(google.y)google.y.first=[];google.dlj=function(b){window.setTimeout(function(){var a=document.createElement("script");a.src=b;document.getElementById("xjsd").appendChild(a)},0)};
(function(){
var c,d,e=false;function f(a){var b={_sn:a?"FAILURE":"FALLBACK",_pu:c,_fu:d},h=google.ml(new Error("pml"),false,b,true);google.log(0,"",h)}function g(){if(!google.pml)f(true)}function i(a){var b=document.createElement("script");b.src=a;document.getElementById("xjsd").appendChild(b)}function j(){if(!e&&!google.pml){e=true;f();i(d,g)}}google.dljp=
function(a,b){c=a;google.xjsu=a;d=b;if(!google.xjsi)window.setTimeout(function(){i(c,j)},0)};
})();
(function(){google.xjsol=function(a){if(google.sc&&(a=google.sc[a])){for(var b=0;b<a.cb.length;b++)a.cb[b]();a.cb=[];a.lc=!0}};google.xjsl=function(a,b){var c=google.sc[a];c&&(b&&(c.lc?b():c.cb.push(b)),!c.ls&&!c.lc&&(google.dlj(c.u),c.ls=!0))};google.register=google.register||function(a,b){google.y.first.push(function(){google.register(a,b)})};})();
google.y.first=[];if(!google.xjs){google.dstr=[];google.rein=[];window._=window._||{};window._._DumpException=function(e){throw e};if(google.timers&&google.timers.load.t){google.timers.load.t.xjsls=new Date().getTime();}google.dljp('/xjs/_/js/s/s,st,anim,bbd,sb_he,hv,pj,tbpr,tbui,spp,rsn,ob,mb,lc,du,ada,lu,pcc,csitl/rt\x3dj/ver\x3d2uka1iBbpRI.en_US./d\x3d1/rs\x3dAItRSTPlPDh3JqT4hZcG--RlbldBDxGPAA','/xjs/_/js/s/s,st,anim,bbd,sb_he,hv,pj,tbpr,tbui,spp,rsn,ob,mb,lc,du,ada,lu,pcc,csitl/rt\x3dj/ver\x3dlF_gVJ_XJjE.en_US./d\x3d1/rs\x3dAItRSTMH2J5B_R4Xy7But51SMlbZzc-o1Q');google.xjs=1;}google.pmc={14:{},263:{},60:{},81:{},10:{"client":"hp","dh":true,"ds":"","fl":true,"host":"google.com","jsonp":true,"msgs":{"lcky":"I\u0026#39;m Feeling Lucky","lml":"Learn more","psrc":"This search was removed from your \u003Ca href=\"/history\"\u003EWeb History\u003C/a\u003E","psrl":"Remove","srch":"Google Search"},"ovr":{"l":1,"ms":1,"p":1},"pq":"","rgen":true,"scd":10,"sce":5,"stok":"K8z3qMfDOsUNfqRVZ3e9Gd4ePt4"},152:{},52:{},213:{"pberr":"\u003Cfont color=red\u003EError:\u003C/font\u003E The server could not complete your request. Try again in 30 seconds."},114:{"rvu_report_msg":"Report","rvu_reported_msg":"Reported"},78:{},25:{"g":8,"k":false,"m":{"app":true,"bks":true,"blg":true,"dsc":true,"evn":true,"fin":true,"flm":true,"frm":true,"isch":true,"klg":true,"mbl":true,"nws":true,"plcs":true,"ppl":true,"prc":true,"pts":true,"rcp":true,"shop":true,"vid":true},"t":null},216:{},105:{},22:{"db":false,"m_errors":{"32":"Sorry, no more results to show.","default":"\u003Cfont color=red\u003EError:\u003C/font\u003E The server could not complete your request. Try again in 30 seconds."},"m_tip":"Click for more information","nlpm":"-153px -84px","nlpp":"-153px -70px","utp":false},77:{},254:{},146:{},144:{},121:{},63:{"cnfrm":"Reported","prmpt":"Report"},84:{"tt_kft":true},97:{},24:{},38:{}};google.y.first.push(function(){if(google.med){google.med('init');google.initHistory();google.med('history');}google.History&&google.History.initialize('/')});if(google.j&&google.j.en&&google.j.xi){window.setTimeout(google.j.xi,0);}</script></div><script>(function(){
var b,d,e,f;function g(a,c){if(a.removeEventListener){a.removeEventListener("load",c,false);a.removeEventListener("error",c,false)}else{a.detachEvent("onload",c);a.detachEvent("onerror",c)}}function h(a){f=(new Date).getTime();++d;a=a||window.event;var c=a.target||a.srcElement;g(c,h)}var i=document.getElementsByTagName("img");b=i.length;d=0;for(var j=0,k;j<b;++j){k=i[j];if(k.complete||typeof k.src!="string"||!k.src)++d;else if(k.addEventListener){k.addEventListener("load",h,false);k.addEventListener("error",
h,false)}else{k.attachEvent("onload",h);k.attachEvent("onerror",h)}}e=b-d;function l(){if(!google.timers.load.t)return;google.timers.load.t.ol=(new Date).getTime();google.timers.load.t.iml=f;google.kCSI.imc=d;google.kCSI.imn=b;google.kCSI.imp=e;if(google.stt!==undefined)google.kCSI.stt=google.stt;google.timers.load.t.xjs&&google.report&&google.report(google.timers.load,google.kCSI)}if(window.addEventListener)window.addEventListener("load",
l,false);else if(window.attachEvent)window.attachEvent("onload",l);google.timers.load.t.prt=(f=(new Date).getTime());
})();
</script>

91
spec/models/story_spec.rb Normal file
View file

@ -0,0 +1,91 @@
require "spec_helper"
describe Story do
it "should get a short id" do
s = Story.make!(:title => "hello", :url => "http://example.com/")
s.short_id.should match(/^\A[a-zA-Z0-9]{1,10}\z/)
end
it "requires a url or a description" do
expect { Story.make!(:title => "hello", :url => "",
:description => "") }.to raise_error
expect { Story.make!(:title => "hello", :description => "hi", :url => nil)
}.to_not raise_error
expect { Story.make!(:title => "hello", :url => "http://ex.com/",
:description => nil) }.to_not raise_error
end
it "does not allow too-short titles" do
expect { Story.make!(:title => "") }.to raise_error
expect { Story.make!(:title => "hi") }.to raise_error
expect { Story.make!(:title => "hello") }.to_not raise_error
end
it "does not allow too-long titles" do
expect { Story.make!(:title => ("hello" * 100)) }.to raise_error
end
it "checks for invalid urls" do
expect { Story.make!(:title => "test", :url => "http://gooses.com/")
}.to_not raise_error
expect { Story.make!(:title => "test", url => "ftp://gooses/")
}.to raise_error
end
it "checks for a previously posted story with same url" do
Story.count.should == 0
Story.make!(:title => "flim flam", :url => "http://example.com/")
Story.count.should == 1
expect { Story.make!(:title => "flim flam 2",
:url => "http://example.com/") }.to raise_error
Story.count.should == 1
end
it "parses domain properly" do
s = Story.make!(:url => "http://example.com")
s.domain.should == "example.com"
s = Story.make!(:url => "http://www3.example.com")
s.domain.should == "example.com"
s = Story.make!(:url => "http://flub.example.com")
s.domain.should == "flub.example.com"
end
it "converts a title to a url properly" do
s = Story.make!(:title => "Hello there, this is a title")
s.title_as_url.should == "hello_there_this_is_a_title"
s = Story.make!(:title => "Hello _ underscore")
s.title_as_url.should == "hello_underscore"
end
it "is not editable by another non-admin user" do
u = User.make!
s = Story.make!(:user_id => u.id)
s.is_editable_by_user?(u).should == true
u = User.make!
s.is_editable_by_user?(u).should == false
end
it "can fetch its title properly" do
s = Story.make
s.fetched_content = File.read(Rails.root +
"spec/fixtures/story_pages/1.html")
s.fetched_title.should == "B2G demo & quick hack // by Paul Rouget"
s = Story.make
s.fetched_content = File.read(Rails.root +
"spec/fixtures/story_pages/2.html")
s.fetched_title.should == "Google"
end
end

26
spec/models/user_spec.rb Normal file
View file

@ -0,0 +1,26 @@
require "spec_helper"
describe User do
it "has a valid username" do
expect { User.make!(:username => nil) }.to raise_error
expect { User.make!(:username => "") }.to raise_error
expect { User.make!(:username => "*") }.to raise_error
User.make!(:username => "test")
expect { User.make!(:username => "test") }.to raise_error
end
it "has a valid email address" do
User.make!(:email => "user@example.com")
expect { User.make!(:email => "user@example.com") }.to raise_error
end
it "authenticates properly" do
u = User.make!(:password => "pilgrim")
u.password_digest.length.should > 20
u.authenticate("pilgrim").should == u
u.authenticate("pilgriM").should == false
end
end

View file

@ -4,4 +4,16 @@ User.blueprint do
email { "user-#{sn}@example.com" }
password { "blah blah" }
password_confirmation { object.password }
username { "username#{sn}" }
end
Story.blueprint do
user_id { User.make }
title { "story title #{sn}" }
url { "http://example.com/#{sn}" }
end
Tag.blueprint do
tag { "tag-#{sn}" }
description { "tag #{sn}" }
end