Merge branch 'master' of github.com:discourse/discourse

This commit is contained in:
Neil Lalonde 2013-07-05 10:57:05 -04:00
commit 7bcf1c5aaa
32 changed files with 402 additions and 174 deletions

View File

@ -13,18 +13,6 @@ Discourse.URL = Em.Object.createWithMixins({
// Used for matching a /more URL
MORE_REGEXP: /\/more$/,
/**
@private
Get a handle on the application's router. Note that currently it uses `__container__` which is not
advised but there is no other way to access the router.
@method router
**/
router: function() {
return Discourse.__container__.lookup('router:main');
}.property(),
/**
Browser aware replaceState. Will only be invoked if the browser supports it.
@ -70,11 +58,65 @@ Discourse.URL = Em.Object.createWithMixins({
path = path.replace(rootURL, '');
}
/*
If the URL is in the topic form, /t/something/:topic_id/:post_number
then we want to apply some special logic. If the post_number changes within the
same topic, use replaceState and instruct our controller to load more posts.
*/
// TODO: Extract into rules we can inject into the URL handler
if (this.navigatedToHome(oldPath, path)) { return; }
if (this.navigatedToListMore(oldPath, path)) { return; }
if (this.navigatedToHome(oldPath, path)) { return; }
// Be wary of looking up the router. In this case, we have links in our
// HTML, say form compiled markdown posts, that need to be routed.
var router = this.get('router');
router.router.updateURL(path);
return router.handleURL(path);
},
/**
Replaces the query parameters in the URL. Use no parameters to clear them.
@method replaceQueryParams
**/
queryParams: Em.computed.alias('router.location.queryParams'),
/**
Redirect to a URL.
This has been extracted so it can be tested.
@method redirectTo
**/
redirectTo: function(url) {
window.location = Discourse.getURL(url);
},
/**
@private
If we're viewing more topics, scroll to where we were previously.
@method navigatedToListMore
@param {String} oldPath the previous path we were on
@param {String} path the path we're navigating to
**/
navigatedToListMore: function(oldPath, path) {
// If we transition from a /more path, scroll to the top
if (this.MORE_REGEXP.exec(oldPath) && (oldPath.indexOf(path) === 0)) {
window.scrollTo(0, 0);
}
return false;
},
/**
@private
If the URL is in the topic form, /t/something/:topic_id/:post_number
then we want to apply some special logic. If the post_number changes within the
same topic, use replaceState and instruct our controller to load more posts.
@method navigatedToPost
@param {String} oldPath the previous path we were on
@param {String} path the path we're navigating to
**/
navigatedToPost: function(oldPath, path) {
var newMatches = this.TOPIC_REGEXP.exec(path),
newTopicId = newMatches ? newMatches[2] : null;
@ -99,28 +141,33 @@ Discourse.URL = Em.Object.createWithMixins({
});
// Abort routing, we have replaced our state.
return;
return true;
}
}
// If we transition from a /more path, scroll to the top
if (this.MORE_REGEXP.exec(oldPath) && (oldPath.indexOf(path) === 0)) {
window.scrollTo(0, 0);
}
// Be wary of looking up the router. In this case, we have links in our
// HTML, say form compiled markdown posts, that need to be routed.
var router = this.get('router');
router.router.updateURL(path);
return router.handleURL(path);
return false;
},
/**
Replaces the query parameters in the URL. Use no parameters to clear them.
@private
@method replaceQueryParams
Handle the custom case of routing to the root path from itself.
@param {String} oldPath the previous path we were on
@param {String} path the path we're navigating to
**/
queryParams: Em.computed.alias('router.location.queryParams'),
navigatedToHome: function(oldPath, path) {
var defaultFilter = "/" + Discourse.ListController.filters[0];
if (path === "/" && (oldPath === "/" || oldPath === defaultFilter)) {
// Refresh our list
this.controllerFor('list').refresh();
return true;
}
return false;
},
/**
@private
@ -137,13 +184,27 @@ Discourse.URL = Em.Object.createWithMixins({
/**
@private
Redirect to a URL.
This has been extracted so it can be tested.
Get a handle on the application's router. Note that currently it uses `__container__` which is not
advised but there is no other way to access the router.
@method redirectTo
@property router
**/
redirectTo: function(url) {
window.location = Discourse.getURL(url);
router: function() {
return Discourse.__container__.lookup('router:main');
}.property(),
/**
@private
Get a controller. Note that currently it uses `__container__` which is not
advised but there is no other way to access the router.
@method controllerFor
@param {String} name the name of the controller
**/
controllerFor: function(name) {
return Discourse.__container__.lookup('controller:' + name);
}
});

View File

@ -172,6 +172,11 @@ Discourse.Utilities = {
return false;
} else if (files.length > 0) {
var upload = files[0];
// ensures that new users can upload image
if (Discourse.User.current('trust_level') === 0 && Discourse.SiteSettings.newuser_max_images === 0) {
bootbox.alert(Em.String.i18n('post.errors.upload_not_allowed_for_new_user'));
return false;
}
// if the image was pasted, sets its name to a default one
if (upload instanceof Blob && !(upload instanceof File) && upload.type === "image/png") { upload.name = "blob.png"; }
// check that the uploaded file is authorized

View File

@ -24,6 +24,19 @@ Discourse.ListController = Discourse.Controller.extend({
});
}.property(),
/**
Refresh our current topic list
@method refresh
**/
refresh: function() {
var listTopicsController = this.get('controllers.listTopics');
listTopicsController.set('model.loaded', false);
this.load(this.get('filterMode')).then(function (topicList) {
listTopicsController.set('model', topicList);
});
},
/**
Load a list based on a filter

View File

@ -20,7 +20,7 @@ Discourse.StaticController = Discourse.Controller.extend({
text = text[1];
this.set('content', text);
} else {
return Discourse.ajax(path + ".json", {dataType: 'html'}).then(function (result) {
return Discourse.ajax(path).then(function (result) {
staticController.set('content', result);
});
}

View File

@ -17,13 +17,18 @@ Discourse.UserStream = Discourse.Model.extend({
}.observes('filter'),
findItems: function() {
var url = Discourse.getURL("/user_actions?offset=") + this.get('itemsLoaded') + "&username=" + (this.get('user.username_lower'));
var me = this;
if(this.get("loading")) { return; }
this.set("loading",true);
var url = Discourse.getURL("/user_actions.json?offset=") + this.get('itemsLoaded') + "&username=" + (this.get('user.username_lower'));
if (this.get('filter')) {
url += "&filter=" + (this.get('filter'));
}
var stream = this;
return Discourse.ajax(url, {cache: 'false'}).then( function(result) {
me.set("loading",false);
if (result && result.user_actions) {
var copy = Em.A();
_.each(result.user_actions,function(action) {
@ -33,7 +38,7 @@ Discourse.UserStream = Discourse.Model.extend({
stream.get('content').pushObjects(copy);
stream.set('itemsLoaded', stream.get('itemsLoaded') + result.user_actions.length);
}
});
}, function(){ me.set("loading", false); });
}
});

View File

@ -31,7 +31,7 @@ Discourse.FilteredListRoute = Discourse.Route.extend({
var listTopicsController = this.controllerFor('listTopics');
listController.set('filterMode', this.filter);
var listContent = listTopicsController.get('content');
var listContent = listTopicsController.get('model');
if (listContent) {
listContent.set('loaded', false);
}
@ -39,7 +39,7 @@ Discourse.FilteredListRoute = Discourse.Route.extend({
listController.load(this.filter).then(function(topicList) {
listController.set('category', null);
listController.set('canCreateTopic', topicList.get('can_create_topic'));
listTopicsController.set('content', topicList);
listTopicsController.set('model', topicList);
});
}
});

View File

@ -255,7 +255,6 @@ Discourse.TopicView = Discourse.View.extend(Discourse.Scrolling, {
@method updatePosition
**/
updatePosition: function() {
this.processSeenPosts();
var offset = window.pageYOffset || $('html').scrollTop();
@ -277,7 +276,10 @@ Discourse.TopicView = Discourse.View.extend(Discourse.Scrolling, {
// Dock the counter if necessary
var $lastPost = $('article[data-post-id=' + topic.get('postStream.lastPostId') + "]");
var lastPostOffset = $lastPost.offset();
if (!lastPostOffset) { return; }
if (!lastPostOffset) {
this.set('controller.dockedCounter', false);
return;
}
this.set('controller.dockedCounter', (offset >= (lastPostOffset.top + $lastPost.height()) - $(window).height()));
},

View File

@ -140,9 +140,22 @@ class Users::OmniauthCallbacksController < ApplicationController
def create_or_sign_on_user_using_cas(auth_token)
logger.error "authtoken #{auth_token}"
email = "#{auth_token[:extra][:user]}@#{SiteSetting.cas_domainname}"
email = auth_token[:info][:email] if auth_token[:info]
email ||= if SiteSetting.cas_domainname.present?
"#{auth_token[:extra][:user]}@#{SiteSetting.cas_domainname}"
else
auth_token[:extra][:user]
end
username = auth_token[:extra][:user]
name = auth_token["uid"]
name = if auth_token[:info] && auth_token[:info][:name]
auth_token[:info][:name]
else
auth_token["uid"]
end
cas_user_id = auth_token["uid"]
session[:authentication] = {

View File

@ -3,7 +3,6 @@ class IncomingLink < ActiveRecord::Base
belongs_to :user
validates :url, presence: true
validate :referer_valid
before_validation :extract_domain
@ -40,7 +39,8 @@ class IncomingLink < ActiveRecord::Base
# Internal: Extract the domain from link.
def extract_domain
if referer.present?
self.domain = URI.parse(referer).host
self.domain = URI.parse(self.referer).host
self.referer = nil unless self.domain
end
end
@ -50,6 +50,7 @@ class IncomingLink < ActiveRecord::Base
parsed = URI.parse(url)
begin
# TODO achieve same thing with no exception
params = Rails.application.routes.recognize_path(parsed.path)
self.topic_id = params[:topic_id]
self.post_number = params[:post_number]

View File

@ -205,7 +205,7 @@ class SiteSetting < ActiveRecord::Base
setting(:max_word_length, 30)
setting(:newuser_max_links, 2)
setting(:newuser_max_images, 0)
client_setting(:newuser_max_images, 0)
setting(:newuser_spam_host_threshold, 3)

View File

@ -34,6 +34,11 @@ class Upload < ActiveRecord::Base
return unless width > SiteSetting.auto_link_images_wider_than
return if has_thumbnail?
thumbnail = OptimizedImage.create_for(self, width, height)
# TODO: @regis we may want to do the more cleanly, create_for may change the dimensions
# this avoids a duplicate key, it should be done more cleanly, this is cheating
thumbnail.width = width
thumbnail.height = height
optimized_images << thumbnail if thumbnail
end

View File

@ -6,6 +6,7 @@ development:
pool: 5
timeout: 5000
host_names:
# /!\ Make sure you *also* restart sidekiq if you change this setting /!\
- "localhost"
# Warning: The database defined as "test" will be erased and
@ -29,6 +30,7 @@ production:
pool: 5
timeout: 5000
host_names:
# /!\ Make sure you *also* restart sidekiq if you change this setting /!\
- production.localhost # Update this to be the domain of your production site
profile:

View File

@ -8,6 +8,7 @@ production:
timeout: 5000
# db_id: 0 # database ID if hosting multiple sites
host_names:
# /!\ Make sure you *also* restart sidekiq if you change this setting /!\
- production.localhost # Update this to be the domain of your production site
test:

View File

@ -746,6 +746,7 @@ en:
upload_too_large: "Sorry, the file you are trying to upload is too big (maximum size is {{max_size_kb}}kb), please resize it and try again."
too_many_uploads: "Sorry, you can only upload one file at a time."
upload_not_authorized: "Sorry, the file you are trying to upload is not authorized (authorized extension: {{authorized_extensions}})."
upload_not_allowed_for_new_user: "Sorry, new users can not upload images."
abandon: "Are you sure you want to abandon your post?"

View File

@ -729,6 +729,7 @@ fr:
upload_too_large: "Désolé, le fichier que vous êtes en train d'envoyer est trop grand (maximum {{max_size_kb}}Kb). Merci de le redimensionner et de réessayer."
too_many_uploads: "Désolé, vous ne pouvez envoyer qu'un seul fichier à la fois."
upload_not_authorized: "Désole, le fichier que vous êtes en train d'uploader n'est pas autorisé (extensions autorisées : {{authorized_extensions}})."
upload_not_allowed_for_new_user: "Désolé, les nouveaux utilisateurs ne peuvent pas uploader d'images."
abandon: "Voulez-vous vraiment abandonner ce message ?"

View File

@ -2,9 +2,9 @@ module Jobs
class CloseTopic < Jobs::Base
def execute(args)
topic = Topic.find(args[:topic_id])
topic = Topic.where(id: args[:topic_id]).first
if topic and topic.auto_close_at and !topic.closed? and !topic.deleted_at
closer = User.find(args[:user_id])
closer = User.where(id: args[:user_id]).first
if Guardian.new(closer).can_moderate?(topic)
topic.update_status('autoclosed', true, closer)
end

View File

@ -0,0 +1,50 @@
require 'open-uri'
desc 'Creates the integration fixtures. Requires a development instance running.'
task 'integration:create_fixtures' => :environment do
fixtures = {
list: ["/latest.json", "/categories.json", "/category/bug.json"],
topic: ["/t/280.json"],
user: ["/users/eviltrout.json", "/user_actions.json?offset=0&username=eviltrout"],
static: ["/faq", '/tos', '/privacy']
}
fixtures.each do |type, urls|
filename = "#{Rails.root}/test/javascripts/fixtures/#{type}_fixtures.js"
content = "/*jshint maxlen:10000000 */\n"
urls.each do |url|
http_result = fake_xhr("http://localhost:3000#{url}")
# If the result is not JSON, convert it to JSON
begin
parsed = ::JSON.parse(http_result)
rescue
http_result = http_result.to_json
end
content << "Discourse.URL_FIXTURES[\"#{url}\"] = #{http_result};\n"
end
File.write(filename, content)
end
end
def fake_xhr(url)
uri = URI(url)
result = nil
Net::HTTP.start(uri.host, uri.port) do |http|
request = Net::HTTP::Get.new uri
request.add_field "X-Requested-With", "XMLHttpRequest"
response = http.request(request)
result = response.body.force_encoding("UTF-8")
end
result
end

View File

@ -8,34 +8,39 @@ task 'posts:refresh_oneboxes' => :environment do
rebake_posts invalidate_oneboxes: true
end
def rebake_post(post,opts)
cooked = post.cook(
post.raw,
topic_id: post.topic_id,
invalidate_oneboxes: opts.fetch(:invalidate_oneboxes, false)
)
if cooked != post.cooked
Post.exec_sql(
'update posts set cooked = ? where id = ?', cooked, post.id
)
post.cooked = cooked
putc "#"
else
putc "."
end
TopicLink.extract_from post
# make sure we trigger the post process
post.trigger_post_process
rescue => e
puts "\n\nFailed to bake topic_id #{post.topic_id} post_id #{post.id} #{e}\n#{e.backtrace.join("\n")} \n\n"
end
def rebake_posts(opts = {})
RailsMultisite::ConnectionManagement.each_connection do |db|
puts "Re baking post markdown for #{db} , changes are denoted with # , no change with ."
total = Post.select([
total = 0
Post.select([
:id, :user_id, :cooked, :raw, :topic_id, :post_number
]).inject(0) do |total, post|
cooked = post.cook(
post.raw,
topic_id: post.topic_id,
invalidate_oneboxes: opts.fetch(:invalidate_oneboxes, false)
)
if cooked != post.cooked
Post.exec_sql(
'update posts set cooked = ? where id = ?', cooked, post.id
)
post.cooked = cooked
putc "#"
else
putc "."
end
TopicLink.extract_from post
# make sure we trigger the post process
post.trigger_post_process
]).each do |post|
rebake_post(post,opts)
total += 1
end

View File

@ -8,16 +8,16 @@ describe Jobs::CloseTopic do
it 'closes a topic that is set to auto-close' do
topic = Fabricate.build(:topic, auto_close_at: Time.zone.now, user: admin)
topic.expects(:update_status).with('autoclosed', true, admin)
Topic.stubs(:find).returns(topic)
User.stubs(:find).returns(admin)
Topic.stubs(:where).returns([topic])
User.stubs(:where).returns([admin])
Jobs::CloseTopic.new.execute( topic_id: 123, user_id: 234 )
end
shared_examples_for "cases when CloseTopic does nothing" do
it 'does nothing to the topic' do
topic.expects(:update_status).never
Topic.stubs(:find).returns(topic)
User.stubs(:find).returns(admin)
Topic.stubs(:where).returns([topic])
User.stubs(:where).returns([admin])
Jobs::CloseTopic.new.execute( topic_id: 123, user_id: 234 )
end
end

View File

@ -3,7 +3,49 @@ require 'spec_helper'
describe Users::OmniauthCallbacksController do
let(:auth) { {info: {email: 'eviltrout@made.up.email', name: 'Robin Ward', uid: 123456789}, "extra" => {"raw_info" => {} } } }
let(:cas_auth) {{ uid: "caluser2", extra: {user: "caluser2"} } }
let(:cas_auth) { { 'uid' => 'casuser', extra: { user: 'casuser'} } }
shared_examples_for "an authenticaton provider" do |provider|
context "when #{provider} logins are disabled" do
before do
SiteSetting.stubs("enable_#{provider}_logins?").returns(false)
end
it "fails" do
get :complete, provider: provider
response.should_not be_success
end
end
context "when #{provider} logins are enabled" do
before do
SiteSetting.stubs("enable_#{provider}_logins?").returns(true)
end
it "succeeds" do
get :complete, provider: provider
response.should be_success
end
context "and 'invite only' site setting is enabled" do
before do
SiteSetting.stubs(:invite_only?).returns(true)
end
it "informs the user they are awaiting approval" do
xhr :get, :complete, provider: provider, format: :json
expect(
JSON.parse(response.body)['awaiting_approval']
).to be_true
end
end
end
end
describe 'invalid provider' do
it "fails" do
@ -20,29 +62,8 @@ describe Users::OmniauthCallbacksController do
request.env["omniauth.auth"] = auth
end
it "fails when twitter logins are disabled" do
SiteSetting.stubs(:enable_twitter_logins?).returns(false)
get :complete, provider: 'twitter'
response.should_not be_success
end
it_behaves_like "an authenticaton provider", 'twitter'
it "succeeds when twitter logins are enabled" do
SiteSetting.stubs(:enable_twitter_logins?).returns(true)
get :complete, provider: 'twitter'
response.should be_success
end
context "when 'invite only' site setting is enabled" do
before { SiteSetting.stubs(:invite_only?).returns(true) }
it 'informs the user they are awaiting approval' do
xhr :get, :complete, provider: 'twitter', format: :json
expect(
JSON.parse(response.body)['awaiting_approval']
).to be_true
end
end
end
describe 'facebook' do
@ -51,17 +72,7 @@ describe Users::OmniauthCallbacksController do
request.env["omniauth.auth"] = auth
end
it "fails when facebook logins are disabled" do
SiteSetting.stubs(:enable_facebook_logins?).returns(false)
get :complete, provider: 'facebook'
response.should_not be_success
end
it "succeeds when facebook logins are enabled" do
SiteSetting.stubs(:enable_facebook_logins?).returns(true)
get :complete, provider: 'facebook'
response.should be_success
end
it_behaves_like "an authenticaton provider", 'facebook'
end
@ -71,16 +82,47 @@ describe Users::OmniauthCallbacksController do
request.env["omniauth.auth"] = cas_auth
end
it "fails when cas logins are disabled" do
SiteSetting.stubs(:enable_cas_logins?).returns(false)
get :complete, provider: 'cas'
response.should_not be_success
end
it_behaves_like "an authenticaton provider", 'cas'
describe "extracted user data" do
before do
SiteSetting.stubs(:enable_cas_logins?).returns(true)
end
subject {
xhr :get, :complete, provider: 'cas', format: :json
OpenStruct.new(JSON.parse(response.body))
}
context "when no user infos are returned by cas" do
its(:username) { should eq 'casuser' }
its(:name) { should eq 'casuser' }
its(:email) { should eq 'casuser' } # No cas_domainname configured!
context "when cas_domainname is configured" do
before do
SiteSetting.stubs(:cas_domainname).returns("example.com")
end
its(:email) { should eq 'casuser@example.com' }
end
end
context "when user infos are returned by cas" do
before do
request.env["omniauth.auth"] = cas_auth.merge({
info: {
name: 'Proper Name',
email: 'public@example.com'
}
})
end
its(:username) { should eq 'casuser' }
its(:name) { should eq 'Proper Name' }
its(:email) { should eq 'public@example.com' }
end
it "succeeds when cas logins are enabled" do
SiteSetting.stubs(:enable_cas_logins?).returns(true)
get :complete, provider: 'cas'
response.should be_success
end
end
@ -93,31 +135,11 @@ describe Users::OmniauthCallbacksController do
end
describe "google" do
it "fails when google logins are disabled" do
SiteSetting.stubs(:enable_google_logins?).returns(false)
get :complete, provider: 'google'
response.should_not be_success
end
it "succeeds when google logins are enabled" do
SiteSetting.stubs(:enable_google_logins?).returns(true)
get :complete, provider: 'google'
response.should be_success
end
it_behaves_like "an authenticaton provider", 'google'
end
describe "yahoo" do
it "fails when yahoo logins are disabled" do
SiteSetting.stubs(:enable_yahoo_logins?).returns(false)
get :complete, provider: 'yahoo'
response.should_not be_success
end
it "succeeds when yahoo logins are enabled" do
SiteSetting.stubs(:enable_yahoo_logins?).returns(true)
get :complete, provider: 'yahoo'
response.should be_success
end
it_behaves_like "an authenticaton provider", 'yahoo'
end
end
@ -128,17 +150,7 @@ describe Users::OmniauthCallbacksController do
request.env["omniauth.auth"] = auth
end
it "fails when github logins are disabled" do
SiteSetting.stubs(:enable_github_logins?).returns(false)
get :complete, provider: 'github'
response.should_not be_success
end
it "succeeds when github logins are enabled" do
SiteSetting.stubs(:enable_github_logins?).returns(true)
get :complete, provider: 'github'
response.should be_success
end
it_behaves_like "an authenticaton provider", 'github'
end
@ -148,17 +160,7 @@ describe Users::OmniauthCallbacksController do
request.env["omniauth.auth"] = auth
end
it "fails when persona logins are disabled" do
SiteSetting.stubs(:enable_persona_logins?).returns(false)
get :complete, provider: 'persona'
response.should_not be_success
end
it "succeeds when persona logins are enabled" do
SiteSetting.stubs(:enable_persona_logins?).returns(true)
get :complete, provider: 'persona'
response.should be_success
end
it_behaves_like "an authenticaton provider", 'persona'
end

View File

@ -48,6 +48,10 @@ describe IncomingLink do
TestRequest.new(env)
end
it "does not explode with bad referer" do
IncomingLink.add(req('http://sam.com','file:///Applications/Install/75067ABC-C9D1-47B7-8ACE-76AEDE3911B2/Install/'))
end
it "does nothing if referer is empty" do
IncomingLink.expects(:create).never
IncomingLink.add(req('http://somesite.com'))

View File

@ -23,6 +23,15 @@ test("uploading one file", function() {
ok(bootbox.alert.calledWith(Em.String.i18n('post.errors.too_many_uploads')));
});
test("new user", function() {
Discourse.SiteSettings.newuser_max_images = 0;
this.stub(Discourse.User, 'current').withArgs("trust_level").returns(0);
this.stub(bootbox, "alert");
ok(!validUpload([1]));
ok(bootbox.alert.calledWith(Em.String.i18n('post.errors.upload_not_allowed_for_new_user')));
});
test("ensures an authorized upload", function() {
var html = { name: "unauthorized.html" };
var extensions = Discourse.SiteSettings.authorized_extensions.replace(/\|/g, ", ");

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
function integration(name) {
module(name, {
module("Integration: " + name, {
setup: function() {
sinon.stub(Discourse.ScrollingDOMMethods, "bindOnScroll");
sinon.stub(Discourse.ScrollingDOMMethods, "unbindOnScroll");

View File

@ -1,24 +1,29 @@
integration("List Topics");
test("Default List", function() {
expect(2);
visit("/").then(function() {
expect(2);
ok(exists("#topic-list"), "The list of topics was rendered");
ok(exists('#topic-list .topic-list-item'), "has topics");
});
});
test("List one Category", function() {
expect(2);
visit("/category/bug").then(function() {
ok(exists("#topic-list"), "The list of topics was rendered");
ok(exists('#topic-list .topic-list-item'), "has topics");
});
});
test("Categories List", function() {
expect(1);
visit("/categories").then(function() {
expect(1);
ok(exists('.category-list-item'), "has a list of categories");
});
});

View File

@ -0,0 +1,22 @@
integration("Static");
test("Faq", function() {
expect(1);
visit("/faq").then(function() {
ok(exists(".body-page"), "The content is present");
});
});
test("Terms of Service", function() {
expect(1);
visit("/tos").then(function() {
ok(exists(".body-page"), "The content is present");
});
});
test("Privacy", function() {
expect(1);
visit("/privacy").then(function() {
ok(exists(".body-page"), "The content is present");
});
});

View File

@ -0,0 +1,12 @@
integration("User");
test("Profile", function() {
visit("/users/eviltrout").then(function() {
expect(2);
ok(exists(".user-heading"), "The heading is rendered");
ok(exists("#user-stream"), "The stream is rendered");
});
});

View File

@ -1,6 +1,6 @@
integration("View Topic");
test("View a Topic", function() {
test("Enter a Topic", function() {
visit("/t/internationalization-localization/280").then(function() {
expect(2);

View File

@ -70,6 +70,7 @@ d.write('<style>#ember-testing-container { position: absolute; background: white
Discourse.rootElement = '#ember-testing';
Discourse.setupForTesting();
Discourse.injectTestHelpers();
Discourse.bindDOMEvents();
Discourse.Router.map(function() {