FEATURE: Use virtual dom framework for faster post rendering

This commit is contained in:
Robin Ward
2016-01-04 15:18:09 -05:00
parent 3bf931ce54
commit d1e85bdd8b
127 changed files with 5724 additions and 2827 deletions

View File

@@ -192,8 +192,8 @@ test("Edit the first post", () => {
ok(!exists('.topic-post:eq(0) .post-info.edits'), 'it has no edits icon at first');
click('.topic-post:eq(0) button[data-action=showMoreActions]');
click('.topic-post:eq(0) button[data-action=edit]');
click('.topic-post:eq(0) button.show-more-actions');
click('.topic-post:eq(0) button.edit');
andThen(() => {
equal(find('.d-editor-input').val().indexOf('Any plans to support'), 0, 'it populates the input with the post text');
});
@@ -212,11 +212,11 @@ test("Edit the first post", () => {
test("Composer can switch between edits", () => {
visit("/t/this-is-a-test-topic/9");
click('.topic-post:eq(0) button[data-action=edit]');
click('.topic-post:eq(0) button.edit');
andThen(() => {
equal(find('.d-editor-input').val().indexOf('This is the first post.'), 0, 'it populates the input with the post text');
});
click('.topic-post:eq(1) button[data-action=edit]');
click('.topic-post:eq(1) button.edit');
andThen(() => {
equal(find('.d-editor-input').val().indexOf('This is the second post.'), 0, 'it populates the input with the post text');
});
@@ -225,9 +225,9 @@ test("Composer can switch between edits", () => {
test("Composer with dirty edit can toggle to another edit", () => {
visit("/t/this-is-a-test-topic/9");
click('.topic-post:eq(0) button[data-action=edit]');
click('.topic-post:eq(0) button.edit');
fillIn('.d-editor-input', 'This is a dirty reply');
click('.topic-post:eq(1) button[data-action=edit]');
click('.topic-post:eq(1) button.edit');
andThen(() => {
ok(exists('.bootbox.modal'), 'it pops up a confirmation dialog');
});
@@ -240,15 +240,15 @@ test("Composer with dirty edit can toggle to another edit", () => {
test("Composer can toggle between edit and reply", () => {
visit("/t/this-is-a-test-topic/9");
click('.topic-post:eq(0) button[data-action=edit]');
click('.topic-post:eq(0) button.edit');
andThen(() => {
equal(find('.d-editor-input').val().indexOf('This is the first post.'), 0, 'it populates the input with the post text');
});
click('.topic-post:eq(0) button[data-action=reply]');
click('.topic-post:eq(0) button.reply');
andThen(() => {
equal(find('.d-editor-input').val(), "", 'it clears the input');
});
click('.topic-post:eq(0) button[data-action=edit]');
click('.topic-post:eq(0) button.edit');
andThen(() => {
equal(find('.d-editor-input').val().indexOf('This is the first post.'), 0, 'it populates the input with the post text');
});
@@ -257,9 +257,9 @@ test("Composer can toggle between edit and reply", () => {
test("Composer with dirty reply can toggle to edit", () => {
visit("/t/this-is-a-test-topic/9");
click('.topic-post:eq(0) button[data-action=reply]');
click('.topic-post:eq(0) button.reply');
fillIn('.d-editor-input', 'This is a dirty reply');
click('.topic-post:eq(0) button[data-action=edit]');
click('.topic-post:eq(0) button.edit');
andThen(() => {
ok(exists('.bootbox.modal'), 'it pops up a confirmation dialog');
});
@@ -272,10 +272,10 @@ test("Composer with dirty reply can toggle to edit", () => {
test("Composer draft with dirty reply can toggle to edit", () => {
visit("/t/this-is-a-test-topic/9");
click('.topic-post:eq(0) button[data-action=reply]');
click('.topic-post:eq(0) button.reply');
fillIn('.d-editor-input', 'This is a dirty reply');
click('.toggler');
click('.topic-post:eq(0) button[data-action=edit]');
click('.topic-post:eq(0) button.edit');
andThen(() => {
ok(exists('.bootbox.modal'), 'it pops up a confirmation dialog');
});

View File

@@ -1,52 +0,0 @@
import componentTest from 'helpers/component-test';
moduleForComponent('post-menu', {integration: true});
function setup(store) {
const topic = store.createRecord('topic', {id: 123});
const post = store.createRecord('post', {
id: 1,
post_number: 1,
topic,
like_count: 3,
actions_summary: [
{id: 2, count: 3, hidden: false, can_act: true}
]
});
this.on('toggleLike', function() {
post.toggleProperty('likeAction.acted');
});
this.set('post', post);
}
componentTest('basic render', {
template: '{{post-menu post=post}}',
setup,
test(assert) {
assert.ok(!!this.$('.post-menu-area').length, 'it renders a post menu');
assert.ok(!!this.$('.actions button[data-share-url]').length, 'it renders a share button');
}
});
componentTest('liking', {
template: '{{post-menu post=post toggleLike="toggleLike"}}',
setup,
test(assert) {
assert.ok(!!this.$('.actions button.like').length);
assert.ok(!!this.$('.actions button.like-count').length);
click('.actions button.like');
andThen(() => {
assert.ok(!this.$('.actions button.like').length);
assert.ok(!!this.$('.actions button.has-like').length);
});
click('.actions button.has-like');
andThen(() => {
assert.ok(!!this.$('.actions button.like').length);
assert.ok(!this.$('.actions button.has-like').length);
});
}
});

View File

@@ -6,6 +6,7 @@ moduleFor('controller:topic', 'controller:topic', {
});
import Topic from 'discourse/models/topic';
import AppEvents from 'discourse/lib/app-events';
var buildTopic = function() {
return Topic.create({
@@ -62,7 +63,7 @@ test("toggledSelectedPost", function() {
});
test("selectAll", function() {
var tc = this.subject({model: buildTopic()}),
var tc = this.subject({model: buildTopic(), appEvents: AppEvents.create()}),
post = Discourse.Post.create({id: 123, post_number: 2}),
postStream = tc.get('model.postStream');

View File

@@ -19,7 +19,8 @@ export default {
"post_types":{
"regular":1,
"moderator_action":2,
"small_action":3
"small_action":3,
"whisper":4
},
"group_names":[
"admins",

View File

@@ -6,21 +6,27 @@ export default function(name, opts) {
opts = opts || {};
test(name, function(assert) {
if (opts.setup) {
const store = createStore();
opts.setup.call(this, store);
}
const appEvents = AppEvents.create();
loadAllHelpers();
this.container.register('site-settings:main', Discourse.SiteSettings, { instantiate: false });
this.container.register('app-events:main', appEvents, { instantiate: false });
this.container.register('capabilities:main', Ember.Object);
this.container.register('site:main', Discourse.Site.current(), { instantiate: false });
this.container.injection('component', 'siteSettings', 'site-settings:main');
this.container.injection('component', 'appEvents', 'app-events:main');
this.container.injection('component', 'capabilities', 'capabilities:main');
this.siteSettings = Discourse.SiteSettings;
loadAllHelpers();
if (opts.setup) {
const store = createStore();
this.currentUser = Discourse.User.create();
this.container.register('store:main', store, { instantiate: false });
this.container.register('current-user:main', this.currentUser, { instantiate: false });
opts.setup.call(this, store);
}
andThen(() => this.render(opts.template));
andThen(() => opts.test.call(this, assert));
});

View File

@@ -85,7 +85,23 @@ export default function() {
this.get('/users/:username/staff-info.json', () => response({}));
this.put('/categories/:category_id', function(request) {
this.get('/post_action_users', () => {
return response({
post_action_users: [
{id: 1, username: 'eviltrout', avatar_template: '/user_avatar/default/eviltrout/{size}/1.png', username_lower: 'eviltrout' }
]
});
});
this.get('/post_replies', () => {
return response({ post_replies: [{ id: 1234, cooked: 'wat' }] });
});
this.get('/post_reply_histories', () => {
return response({ post_reply_histories: [{ id: 1234, cooked: 'wat' }] });
});
this.put('/categories/:category_id', request => {
const category = parsePostData(request.requestBody);
return response({category});
});
@@ -132,6 +148,7 @@ export default function() {
this.delete('/posts/:post_id', success);
this.put('/posts/:post_id/recover', success);
this.get('/posts/:post_id/expand-embed', success);
this.put('/posts/:post_id', request => {
const data = parsePostData(request.requestBody);

View File

@@ -41,7 +41,6 @@ function acceptance(name, options) {
Discourse.Utilities.avatarImg = () => "";
// For now don't do scrolling stuff in Test Mode
Ember.CloakedCollectionView.scrolled = Ember.K;
HeaderView.reopen({examineDockHeader: Ember.K});
var siteJson = siteFixtures['site.json'].site;

View File

@@ -0,0 +1,9 @@
import componentTest from 'helpers/component-test';
export function moduleForWidget(name) {
moduleForComponent(name, `widget:${name}`, { integration: true });
}
export function widgetTest(name, opts) {
return componentTest(name, opts);
}

View File

@@ -27,40 +27,6 @@ test('defaults', function() {
present(postStream.get('topic'));
});
test('daysSincePrevious when appending', function(assert) {
const postStream = buildStream(10000001, [1,2,3]);
const store = postStream.store;
const p1 = store.createRecord('post', {id: 1, post_number: 1, created_at: "2015-05-29T18:17:35.868Z"}),
p2 = store.createRecord('post', {id: 2, post_number: 2, created_at: "2015-06-01T01:07:25.761Z"}),
p3 = store.createRecord('post', {id: 3, post_number: 3, created_at: "2015-06-02T01:07:25.761Z"});
postStream.appendPost(p1);
postStream.appendPost(p2);
postStream.appendPost(p3);
assert.ok(!p1.get('daysSincePrevious'));
assert.equal(p2.get('daysSincePrevious'), 2);
assert.equal(p3.get('daysSincePrevious'), 1);
});
test('daysSincePrevious when prepending', function(assert) {
const postStream = buildStream(10000001, [1,2,3]);
const store = postStream.store;
const p1 = store.createRecord('post', {id: 1, post_number: 1, created_at: "2015-05-29T18:17:35.868Z"}),
p2 = store.createRecord('post', {id: 2, post_number: 2, created_at: "2015-06-01T01:07:25.761Z"}),
p3 = store.createRecord('post', {id: 3, post_number: 3, created_at: "2015-06-02T01:07:25.761Z"});
postStream.prependPost(p3);
postStream.prependPost(p2);
postStream.prependPost(p1);
assert.ok(!p1.get('daysSincePrevious'));
assert.equal(p2.get('daysSincePrevious'), 2);
assert.equal(p3.get('daysSincePrevious'), 1);
});
test('appending posts', function() {
const postStream = buildStream(4567, [1, 3, 4]);
const store = postStream.store;
@@ -320,17 +286,6 @@ test("loadIntoIdentityMap with post ids", function() {
});
});
test("loading a post's history", function() {
const postStream = buildStream(1234);
const store = postStream.store;
const post = store.createRecord('post', {id: 4321});
postStream.findReplyHistory(post).then(function() {
present(postStream.findLoadedPost(2222), "it stores the returned post in the identity map");
present(post.get('replyHistory'), "it sets the replyHistory attribute for the post");
});
});
test("staging and undoing a new post", function() {
const postStream = buildStream(10101, [1]);
const store = postStream.store;

View File

@@ -14,7 +14,6 @@ test('defaults', function() {
var post = Discourse.Post.create({id: 1});
blank(post.get('deleted_at'), "it has no deleted_at by default");
blank(post.get('deleted_by'), "there is no deleted_by by default");
equal(post.get('replyHistory.length'), 0, "there is no reply history by default");
});
test('new_user', function() {
@@ -47,16 +46,6 @@ test('updateFromPost', function() {
equal(post.get('raw'), "different raw", "raw field updated");
});
test('hasHistory', function() {
var post = Discourse.Post.create({id: 1});
ok(!post.get('hasHistory'), 'posts without versions have no history');
post.set('version', 1);
ok(!post.get('hasHistory'), 'posts with one version have no history');
post.set('version', 2);
ok(post.get('hasHistory'), 'posts with more than one version have a history');
});
test('destroy by staff', function() {
var user = Discourse.User.create({username: 'staff', staff: true}),
post = buildPost({user: user});

View File

@@ -111,9 +111,6 @@ QUnit.testStart(function(ctx) {
}
});
// Don't cloak in testing
Ember.CloakedCollectionView = Ember.CollectionView;
QUnit.testDone(function() {
Ember.run.debounce = origDebounce;
window.sandbox.restore();

View File

@@ -0,0 +1,80 @@
import { moduleForWidget, widgetTest } from 'helpers/widget-test';
moduleForWidget('actions-summary');
widgetTest('listing actions', {
template: '{{mount-widget widget="actions-summary" args=args}}',
setup() {
this.set('args', {
actionsSummary: [
{action: 'off_topic', description: 'very off topic'},
{action: 'spam', description: 'suspicious message'}
]
});
},
test(assert) {
assert.equal(this.$('.post-actions .post-action').length, 2);
click('.post-action:eq(0) .action-link a');
andThen(() => {
assert.equal(this.$('.post-action:eq(0) img.avatar').length, 1, 'clicking it shows the user');
});
}
});
widgetTest('undo', {
template: '{{mount-widget widget="actions-summary" args=args undoPostAction="undoPostAction"}}',
setup() {
this.set('args', {
actionsSummary: [
{action: 'off_topic', description: 'very off topic', canUndo: true},
]
});
this.on('undoPostAction', () => this.undid = true);
},
test(assert) {
assert.equal(this.$('.post-actions .post-action').length, 1);
click('.action-link.undo');
andThen(() => {
assert.ok(this.undid, 'it triggered the action');
});
}
});
widgetTest('deferFlags', {
template: '{{mount-widget widget="actions-summary" args=args deferPostActionFlags="deferPostActionFlags"}}',
setup() {
this.set('args', {
actionsSummary: [
{action: 'off_topic', description: 'very off topic', canDeferFlags: true, count: 1},
]
});
this.on('deferPostActionFlags', () => this.deferred = true);
},
test(assert) {
assert.equal(this.$('.post-actions .post-action').length, 1);
click('.action-link.defer-flags');
andThen(() => {
assert.ok(this.deferred, 'it triggered the action');
});
}
});
widgetTest('post deleted', {
template: '{{mount-widget widget="actions-summary" args=args}}',
setup() {
this.set('args', {
isDeleted: true,
deletedByUsername: 'eviltrout',
deletedByAvatarTemplate: '/images/avatar.png'
});
},
test(assert) {
assert.ok(this.$('.post-action .fa-trash-o').length === 1, 'it has the deleted icon');
assert.ok(this.$('.avatar[title=eviltrout]').length === 1, 'it has the deleted by avatar');
}
});

View File

@@ -0,0 +1,54 @@
import { moduleForWidget, widgetTest } from 'helpers/widget-test';
moduleForWidget('post-gutter');
widgetTest("duplicate links", {
template: '{{mount-widget widget="post-gutter" args=args}}',
setup() {
this.set('args', {
links: [
{ title: "Evil Trout Link", url: "http://eviltrout.com" },
{ title: "Evil Trout Link", url: "http://dupe.eviltrout.com" }
]
});
},
test(assert) {
assert.equal(this.$('.post-links a.track-link').length, 1, 'it hides the dupe link');
}
});
widgetTest("collapsed links", {
template: '{{mount-widget widget="post-gutter" args=args}}',
setup() {
this.set('args', {
links: [
{ title: "Link 1", url: "http://eviltrout.com?1" },
{ title: "Link 2", url: "http://eviltrout.com?2" },
{ title: "Link 3", url: "http://eviltrout.com?3" },
{ title: "Link 4", url: "http://eviltrout.com?4" },
{ title: "Link 5", url: "http://eviltrout.com?5" },
{ title: "Link 6", url: "http://eviltrout.com?6" },
{ title: "Link 7", url: "http://eviltrout.com?7" },
]
});
},
test(assert) {
assert.equal(this.$('.post-links a.track-link').length, 5, 'collapses by default');
click('a.toggle-more');
andThen(() => {
assert.equal(this.$('.post-links a.track-link').length, 7);
});
}
});
widgetTest("reply as new topic", {
template: '{{mount-widget widget="post-gutter" args=args newTopicAction="newTopicAction"}}',
setup() {
this.set('args', { canReplyAsNewTopic: true });
this.on('newTopicAction', () => this.newTopicTriggered = true);
},
test(assert) {
click('a.reply-new');
andThen(() => assert.ok(this.newTopicTriggered));
}
});

View File

@@ -0,0 +1,66 @@
import { moduleForWidget, widgetTest } from 'helpers/widget-test';
import Topic from 'discourse/models/topic';
import Post from 'discourse/models/post';
moduleForWidget('post-stream');
function postStreamTest(name, attrs) {
widgetTest(name, {
template: `{{mount-widget widget="post-stream" args=(as-hash posts=posts)}}`,
setup() {
this.set('posts', attrs.posts.call(this));
},
test: attrs.test
});
}
postStreamTest('basics', {
posts() {
const site = this.container.lookup('site:main');
const topic = Topic.create({ details: { created_by: { id: 123 } } });
return [
Post.create({ topic, id: 1, post_number: 1, user_id: 123, primary_group_name: 'trout',
avatar_template: '/images/avatar.png' }),
Post.create({ topic, id: 2, post_number: 2, post_type: site.get('post_types.moderator_action') }),
Post.create({ topic, id: 3, post_number: 3, hidden: true }),
Post.create({ topic, id: 4, post_number: 4, post_type: site.get('post_types.whisper') }),
Post.create({ topic, id: 5, post_number: 5, wiki: true, via_email: true })
];
},
test(assert) {
assert.equal(this.$('.post-stream').length, 1);
assert.equal(this.$('.topic-post').length, 5, 'renders all posts');
// look for special class bindings
assert.equal(this.$('.topic-post:eq(0).topic-owner').length, 1, 'it applies the topic owner class');
assert.equal(this.$('.topic-post:eq(0).group-trout').length, 1, 'it applies the primary group class');
assert.equal(this.$('.topic-post:eq(0).regular').length, 1, 'it applies the regular class');
assert.equal(this.$('.topic-post:eq(1).moderator').length, 1, 'it applies the moderator class');
assert.equal(this.$('.topic-post:eq(2).post-hidden').length, 1, 'it applies the hidden class');
assert.equal(this.$('.topic-post:eq(3).whisper').length, 1, 'it applies the whisper class');
assert.equal(this.$('.topic-post:eq(4).wiki').length, 1, 'it applies the wiki class');
// it renders an article for the body with appropriate attributes
assert.equal(this.$('article#post_2').length, 1);
assert.equal(this.$('article[data-user-id=123]').length, 1);
assert.equal(this.$('article[data-post-id=3]').length, 1);
assert.equal(this.$('article#post_5.via-email').length, 1);
assert.equal(this.$('article:eq(0) .main-avatar').length, 1, 'renders the main avatar');
}
});
postStreamTest('deleted posts', {
posts() {
const topic = Topic.create({ details: { created_by: { id: 123 } } });
return [
Post.create({ topic, id: 1, post_number: 1, deleted_at: new Date().getTime() }),
];
},
test(assert) {
assert.equal(this.$('.topic-post.deleted').length, 1, 'it applies the deleted class');
assert.equal(this.$('.deleted-user-avatar').length, 1, 'it has the trash avatar');
}
});

View File

@@ -0,0 +1,784 @@
import { moduleForWidget, widgetTest } from 'helpers/widget-test';
moduleForWidget('post');
widgetTest('basic elements', {
template: '{{mount-widget widget="post" args=args}}',
setup() {
this.set('args', { shareUrl: '/example', post_number: 1 });
},
test(assert) {
assert.ok(this.$('.names').length, 'includes poster name');
assert.ok(this.$('a.post-date').length, 'includes post date');
assert.ok(this.$('a.post-date[data-share-url]').length);
assert.ok(this.$('a.post-date[data-post-number]').length);
}
});
widgetTest('wiki', {
template: '{{mount-widget widget="post" args=args editPost="editPost"}}',
setup() {
this.set('args', { wiki: true });
this.on('editPost', () => this.editPostCalled = true);
},
test(assert) {
click('.post-info.wiki');
andThen(() => {
assert.ok(this.editPostCalled, 'clicking the wiki icon edits the post');
});
}
});
widgetTest('via-email', {
template: '{{mount-widget widget="post" args=args showRawEmail="showRawEmail"}}',
setup() {
this.set('args', { via_email: true, canViewRawEmail: true });
this.on('showRawEmail', () => this.rawEmailShown = true);
},
test(assert) {
click('.post-info.via-email');
andThen(() => {
assert.ok(this.rawEmailShown, 'clicking the enveloppe shows the raw email');
});
}
});
widgetTest('via-email without permission', {
template: '{{mount-widget widget="post" args=args showRawEmail="showRawEmail"}}',
setup() {
this.set('args', { via_email: true, canViewRawEmail: false });
this.on('showRawEmail', () => this.rawEmailShown = true);
},
test(assert) {
click('.post-info.via-email');
andThen(() => {
assert.ok(!this.rawEmailShown, `clicking the enveloppe doesn't show the raw email`);
});
}
});
widgetTest('history', {
template: '{{mount-widget widget="post" args=args showHistory="showHistory"}}',
setup() {
this.set('args', { version: 3, canViewEditHistory: true });
this.on('showHistory', () => this.historyShown = true);
},
test(assert) {
click('.post-info.edits');
andThen(() => {
assert.ok(this.historyShown, 'clicking the pencil shows the history');
});
}
});
widgetTest('history without view permission', {
template: '{{mount-widget widget="post" args=args showHistory="showHistory"}}',
setup() {
this.set('args', { version: 3, canViewEditHistory: false });
this.on('showHistory', () => this.historyShown = true);
},
test(assert) {
click('.post-info.edits');
andThen(() => {
assert.ok(!this.historyShown, `clicking the pencil doesn't show the history`);
});
}
});
widgetTest('whisper', {
template: '{{mount-widget widget="post" args=args}}',
setup() {
this.set('args', { isWhisper: true });
},
test(assert) {
assert.ok(this.$('.topic-post.whisper').length === 1);
assert.ok(this.$('.post-info.whisper').length === 1);
}
});
widgetTest('like count button', {
template: '{{mount-widget widget="post" model=post args=args}}',
setup(store) {
const topic = store.createRecord('topic', {id: 123});
const post = store.createRecord('post', {
id: 1,
post_number: 1,
topic,
like_count: 3,
actions_summary: [ {id: 2, count: 1, hidden: false, can_act: true} ]
});
this.set('post', post);
this.set('args', { likeCount: 1 });
},
test(assert) {
assert.ok(this.$('button.like-count').length === 1);
assert.ok(this.$('.who-liked').length === 0);
// toggle it on
click('button.like-count');
andThen(() => {
assert.ok(this.$('.who-liked').length === 1);
assert.ok(this.$('.who-liked a.trigger-user-card').length === 1);
});
// toggle it off
click('button.like-count');
andThen(() => {
assert.ok(this.$('.who-liked').length === 0);
assert.ok(this.$('.who-liked a.trigger-user-card').length === 0);
});
}
});
widgetTest(`like count with no likes`, {
template: '{{mount-widget widget="post" model=post args=args}}',
setup() {
this.set('args', { likeCount: 0 });
},
test(assert) {
assert.ok(this.$('button.like-count').length === 0);
}
});
widgetTest('share button', {
template: '{{mount-widget widget="post" args=args}}',
setup() {
this.set('args', { shareUrl: 'http://share-me.example.com' });
},
test(assert) {
assert.ok(!!this.$('.actions button[data-share-url]').length, 'it renders a share button');
}
});
widgetTest('liking', {
template: '{{mount-widget widget="post-menu" args=args toggleLike="toggleLike"}}',
setup() {
const args = { showLike: true, canToggleLike: true };
this.set('args', args);
this.on('toggleLike', () => {
args.liked = !args.liked;
args.likeCount = args.liked ? 1 : 0;
});
},
test(assert) {
assert.ok(!!this.$('.actions button.like').length);
assert.ok(this.$('.actions button.like-count').length === 0);
click('.actions button.like');
andThen(() => {
assert.ok(!this.$('.actions button.like').length);
assert.ok(!!this.$('.actions button.has-like').length);
assert.ok(this.$('.actions button.like-count').length === 1);
});
click('.actions button.has-like');
andThen(() => {
assert.ok(!!this.$('.actions button.like').length);
assert.ok(!this.$('.actions button.has-like').length);
assert.ok(this.$('.actions button.like-count').length === 0);
});
}
});
widgetTest('edit button', {
template: '{{mount-widget widget="post" args=args editPost="editPost"}}',
setup() {
this.set('args', { canEdit: true });
this.on('editPost', () => this.editPostCalled = true);
},
test(assert) {
click('button.edit');
andThen(() => {
assert.ok(this.editPostCalled, 'it triggered the edit action');
});
}
});
widgetTest(`edit button - can't edit`, {
template: '{{mount-widget widget="post" args=args editPost="editPost"}}',
setup() {
this.set('args', { canEdit: false });
},
test(assert) {
assert.equal(this.$('button.edit').length, 0, `button is not displayed`);
}
});
widgetTest('recover button', {
template: '{{mount-widget widget="post" args=args deletePost="deletePost"}}',
setup() {
this.set('args', { canDelete: true });
this.on('deletePost', () => this.deletePostCalled = true);
},
test(assert) {
click('button.delete');
andThen(() => {
assert.ok(this.deletePostCalled, 'it triggered the delete action');
});
}
});
widgetTest('delete topic button', {
template: '{{mount-widget widget="post" args=args deletePost="deletePost"}}',
setup() {
this.set('args', { canDeleteTopic: true });
this.on('deletePost', () => this.deletePostCalled = true);
},
test(assert) {
click('button.delete');
andThen(() => {
assert.ok(this.deletePostCalled, 'it triggered the delete action');
});
}
});
widgetTest(`delete topic button - can't delete`, {
template: '{{mount-widget widget="post" args=args deletePost="deletePost"}}',
setup() {
this.set('args', { canDeleteTopic: false });
},
test(assert) {
assert.equal(this.$('button.delete').length, 0, `button is not displayed`);
}
});
widgetTest('recover topic button', {
template: '{{mount-widget widget="post" args=args recoverPost="recoverPost"}}',
setup() {
this.set('args', { canRecoverTopic: true });
this.on('recoverPost', () => this.recovered = true);
},
test(assert) {
click('button.recover');
andThen(() => assert.ok(this.recovered));
}
});
widgetTest(`recover topic button - can't recover`, {
template: '{{mount-widget widget="post" args=args deletePost="deletePost"}}',
setup() {
this.set('args', { canRecoverTopic: false });
},
test(assert) {
assert.equal(this.$('button.recover').length, 0, `button is not displayed`);
}
});
widgetTest('delete post button', {
template: '{{mount-widget widget="post" args=args deletePost="deletePost"}}',
setup() {
this.set('args', { canDelete: true });
this.on('deletePost', () => this.deletePostCalled = true);
},
test(assert) {
click('button.delete');
andThen(() => {
assert.ok(this.deletePostCalled, 'it triggered the delete action');
});
}
});
widgetTest(`delete post button - can't delete`, {
template: '{{mount-widget widget="post" args=args}}',
setup() {
this.set('args', { canDelete: false });
},
test(assert) {
assert.equal(this.$('button.delete').length, 0, `button is not displayed`);
}
});
widgetTest('recover post button', {
template: '{{mount-widget widget="post" args=args recoverPost="recoverPost"}}',
setup() {
this.set('args', { canRecover: true });
this.on('recoverPost', () => this.recovered = true);
},
test(assert) {
click('button.recover');
andThen(() => assert.ok(this.recovered));
}
});
widgetTest(`recover post button - can't recover`, {
template: '{{mount-widget widget="post" args=args deletePost="deletePost"}}',
setup() {
this.set('args', { canRecover: false });
},
test(assert) {
assert.equal(this.$('button.recover').length, 0, `button is not displayed`);
}
});
widgetTest(`flagging`, {
template: '{{mount-widget widget="post" args=args showFlags="showFlags"}}',
setup() {
this.set('args', { canFlag: true });
this.on('showFlags', () => this.flagsShown = true);
},
test(assert) {
assert.ok(this.$('button.create-flag').length === 1);
click('button.create-flag');
andThen(() => {
assert.ok(this.flagsShown, 'it triggered the action');
});
}
});
widgetTest(`flagging: can't flag`, {
template: '{{mount-widget widget="post" args=args}}',
setup() {
this.set('args', { canFlag: false });
},
test(assert) {
assert.ok(this.$('button.create-flag').length === 0);
}
});
widgetTest(`read indicator`, {
template: '{{mount-widget widget="post" args=args}}',
setup() {
this.set('args', { read: true });
},
test(assert) {
assert.ok(this.$('.read-state.read').length);
}
});
widgetTest(`unread indicator`, {
template: '{{mount-widget widget="post" args=args}}',
setup() {
this.set('args', { read: false });
},
test(assert) {
assert.ok(this.$('.read-state').length);
}
});
widgetTest("reply directly above (supressed)", {
template: '{{mount-widget widget="post" args=args}}',
setup() {
this.set('args', {
replyToUsername: 'eviltrout',
replyToAvatarTemplate: '/images/avatar.png',
replyDirectlyAbove: true
});
},
test(assert) {
assert.equal(this.$('a.reply-to-tab').length, 0, 'hides the tab');
assert.equal(this.$('.avoid-tab').length, 0, "doesn't have the avoid tab class");
}
});
widgetTest("reply a few posts above (supressed)", {
template: '{{mount-widget widget="post" args=args}}',
setup() {
this.set('args', {
replyToUsername: 'eviltrout',
replyToAvatarTemplate: '/images/avatar.png',
replyDirectlyAbove: false
});
},
test(assert) {
assert.ok(this.$('a.reply-to-tab').length, 'shows the tab');
assert.equal(this.$('.avoid-tab').length, 1, "has the avoid tab class");
}
});
widgetTest("reply directly above", {
template: '{{mount-widget widget="post" args=args}}',
setup() {
this.set('args', {
replyToUsername: 'eviltrout',
replyToAvatarTemplate: '/images/avatar.png',
replyDirectlyAbove: true
});
this.siteSettings.suppress_reply_directly_above = false;
},
test(assert) {
assert.equal(this.$('.avoid-tab').length, 1, "has the avoid tab class");
click('a.reply-to-tab');
andThen(() => {
assert.equal(this.$('section.embedded-posts.top .cooked').length, 1);
assert.equal(this.$('section.embedded-posts i.fa-arrow-up').length, 1);
});
}
});
widgetTest("cooked content hidden", {
template: '{{mount-widget widget="post" args=args expandHidden="expandHidden"}}',
setup() {
this.set('args', { cooked_hidden: true });
this.on('expandHidden', () => this.unhidden = true);
},
test(assert) {
click('.topic-body .expand-hidden');
andThen(() => {
assert.ok(this.unhidden, 'triggers the action');
});
}
});
widgetTest("expand first post", {
template: '{{mount-widget widget="post" model=post args=args}}',
setup(store) {
this.set('args', { expandablePost: true });
this.set('post', store.createRecord('post', { id: 1234 }));
},
test(assert) {
click('.topic-body .expand-post');
andThen(() => {
assert.equal(this.$('.expand-post').length, 0, 'button is gone');
});
}
});
widgetTest("can't bookmark", {
template: '{{mount-widget widget="post" args=args}}',
setup() {
this.set('args', { canBookmark: false });
},
test(assert) {
assert.equal(this.$('button.bookmark').length, 0);
assert.equal(this.$('button.bookmarked').length, 0);
}
});
widgetTest("bookmark", {
template: '{{mount-widget widget="post" args=args toggleBookmark="toggleBookmark"}}',
setup() {
const args = { canBookmark: true };
this.set('args', args);
this.on('toggleBookmark', () => args.bookmarked = true);
},
test(assert) {
assert.equal(this.$('.post-menu-area .bookmark').length, 1);
assert.equal(this.$('button.bookmarked').length, 0);
click('button.bookmark');
andThen(() => {
assert.equal(this.$('button.bookmarked').length, 1);
});
}
});
widgetTest("can't show admin menu when you can't manage", {
template: '{{mount-widget widget="post" args=args}}',
setup() {
this.set('args', { canManage: false });
},
test(assert) {
assert.equal(this.$('.post-menu-area .show-post-admin-menu').length, 0);
}
});
widgetTest("show admin menu", {
template: '{{mount-widget widget="post" args=args}}',
setup() {
this.set('args', { canManage: true });
},
test(assert) {
assert.equal(this.$('.post-admin-menu').length, 0);
click('.post-menu-area .show-post-admin-menu');
andThen(() => {
assert.equal(this.$('.post-admin-menu').length, 1, 'it shows the popup');
});
click('.post-menu-area');
andThen(() => {
assert.equal(this.$('.post-admin-menu').length, 0, 'clicking outside clears the popup');
});
}
});
widgetTest("toggle moderator post", {
template: '{{mount-widget widget="post" args=args togglePostType="togglePostType"}}',
setup() {
this.set('args', { canManage: true });
this.on('togglePostType', () => this.toggled = true);
},
test(assert) {
click('.post-menu-area .show-post-admin-menu');
click('.post-admin-menu .toggle-post-type');
andThen(() => {
assert.ok(this.toggled);
assert.equal(this.$('.post-admin-menu').length, 0, 'also hides the menu');
});
}
});
widgetTest("toggle moderator post", {
template: '{{mount-widget widget="post" args=args togglePostType="togglePostType"}}',
setup() {
this.set('args', { canManage: true });
this.on('togglePostType', () => this.toggled = true);
},
test(assert) {
click('.post-menu-area .show-post-admin-menu');
click('.post-admin-menu .toggle-post-type');
andThen(() => {
assert.ok(this.toggled);
assert.equal(this.$('.post-admin-menu').length, 0, 'also hides the menu');
});
}
});
widgetTest("rebake post", {
template: '{{mount-widget widget="post" args=args rebakePost="rebakePost"}}',
setup() {
this.set('args', { canManage: true });
this.on('rebakePost', () => this.baked = true);
},
test(assert) {
click('.post-menu-area .show-post-admin-menu');
click('.post-admin-menu .rebuild-html');
andThen(() => {
assert.ok(this.baked);
assert.equal(this.$('.post-admin-menu').length, 0, 'also hides the menu');
});
}
});
widgetTest("unhide post", {
template: '{{mount-widget widget="post" args=args unhidePost="unhidePost"}}',
setup() {
this.set('args', { canManage: true, hidden: true });
this.on('unhidePost', () => this.unhidden = true);
},
test(assert) {
click('.post-menu-area .show-post-admin-menu');
click('.post-admin-menu .unhide-post');
andThen(() => {
assert.ok(this.unhidden);
assert.equal(this.$('.post-admin-menu').length, 0, 'also hides the menu');
});
}
});
widgetTest("change owner", {
template: '{{mount-widget widget="post" args=args changePostOwner="changePostOwner"}}',
setup() {
this.currentUser.admin = true;
this.set('args', { canManage: true });
this.on('changePostOwner', () => this.owned = true);
},
test(assert) {
click('.post-menu-area .show-post-admin-menu');
click('.post-admin-menu .change-owner');
andThen(() => {
assert.ok(this.owned);
assert.equal(this.$('.post-admin-menu').length, 0, 'also hides the menu');
});
}
});
widgetTest("reply", {
template: '{{mount-widget widget="post" args=args replyToPost="replyToPost"}}',
setup() {
this.set('args', { canCreatePost: true });
this.on('replyToPost', () => this.replied = true);
},
test(assert) {
click('.post-controls .create');
andThen(() => {
assert.ok(this.replied);
});
}
});
widgetTest("reply - without permissions", {
template: '{{mount-widget widget="post" args=args}}',
setup() {
this.set('args', { canCreatePost: false });
},
test(assert) {
assert.equal(this.$('.post-controls .create').length, 0);
}
});
widgetTest("replies - no replies", {
template: '{{mount-widget widget="post" args=args}}',
setup() {
this.set('args', {replyCount: 0});
},
test(assert) {
assert.equal(this.$('button.show-replies').length, 0);
}
});
widgetTest("replies - multiple replies", {
template: '{{mount-widget widget="post" args=args}}',
setup() {
this.siteSettings.suppress_reply_directly_below = true;
this.set('args', {replyCount: 2, replyDirectlyBelow: true});
},
test(assert) {
assert.equal(this.$('button.show-replies').length, 1);
}
});
widgetTest("replies - one below, suppressed", {
template: '{{mount-widget widget="post" args=args}}',
setup() {
this.siteSettings.suppress_reply_directly_below = true;
this.set('args', {replyCount: 1, replyDirectlyBelow: true});
},
test(assert) {
assert.equal(this.$('button.show-replies').length, 0);
}
});
widgetTest("replies - one below, not suppressed", {
template: '{{mount-widget widget="post" args=args}}',
setup() {
this.siteSettings.suppress_reply_directly_below = false;
this.set('args', {id: 6654, replyCount: 1, replyDirectlyBelow: true});
},
test(assert) {
click('button.show-replies');
andThen(() => {
assert.equal(this.$('section.embedded-posts.bottom .cooked').length, 1);
assert.equal(this.$('section.embedded-posts i.fa-arrow-down').length, 1);
});
}
});
widgetTest("topic map not shown", {
template: '{{mount-widget widget="post" args=args}}',
setup() {
this.set('args', { showTopicMap: false });
},
test(assert) {
assert.equal(this.$('.topic-map').length, 0);
}
});
widgetTest("topic map - few posts", {
template: '{{mount-widget widget="post" args=args}}',
setup() {
this.set('args', {
showTopicMap: true,
topicPostsCount: 2,
participants: [
{username: 'eviltrout'},
{username: 'codinghorror'},
]
});
},
test(assert) {
assert.equal(this.$('li.avatars a.poster').length, 0, 'shows no participants when collapsed');
click('nav.buttons button');
andThen(() => {
assert.equal(this.$('.topic-map-expanded a.poster').length, 2, 'shows all when expanded');
});
}
});
widgetTest("topic map - participants", {
template: '{{mount-widget widget="post" args=args toggleParticipant="toggleParticipant"}}',
setup() {
this.set('args', {
showTopicMap: true,
topicPostsCount: 10,
participants: [
{username: 'eviltrout'},
{username: 'codinghorror'},
{username: 'sam'},
{username: 'ZogStrIP'},
],
userFilters: ['sam', 'codinghorror']
});
this.on('toggleParticipant', () => this.participantToggled = true);
},
test(assert) {
assert.equal(this.$('li.avatars a.poster').length, 3, 'limits to three participants');
click('nav.buttons button');
andThen(() => {
assert.equal(this.$('li.avatars a.poster').length, 0);
assert.equal(this.$('.topic-map-expanded a.poster').length, 4, 'shows all when expanded');
assert.equal(this.$('a.poster.toggled').length, 2, 'two are toggled');
});
click('.topic-map-expanded a.poster:eq(0)');
andThen(() => assert.ok(this.participantToggled));
}
});
widgetTest("topic map - links", {
template: '{{mount-widget widget="post" args=args}}',
setup() {
this.set('args', {
showTopicMap: true,
topicLinks: [
{url: 'http://link1.example.com', clicks: 0},
{url: 'http://link2.example.com', clicks: 0},
{url: 'http://link3.example.com', clicks: 0},
{url: 'http://link4.example.com', clicks: 0},
{url: 'http://link5.example.com', clicks: 0},
{url: 'http://link6.example.com', clicks: 0},
]
});
},
test(assert) {
assert.equal(this.$('.topic-map').length, 1);
assert.equal(this.$('.map.map-collapsed').length, 1);
assert.equal(this.$('.topic-map-expanded').length, 0);
click('nav.buttons button');
andThen(() => {
assert.equal(this.$('.map.map-collapsed').length, 0);
assert.equal(this.$('.topic-map i.fa-chevron-up').length, 1);
assert.equal(this.$('.topic-map-expanded').length, 1);
assert.equal(this.$('.topic-map-expanded .topic-link').length, 5, 'it limits the links displayed');
});
click('.link-summary a');
andThen(() => {
assert.equal(this.$('.topic-map-expanded .topic-link').length, 6, 'all links now shown');
});
}
});
widgetTest("topic map - no summary", {
template: '{{mount-widget widget="post" args=args}}',
setup() {
this.set('args', { showTopicMap: true });
},
test(assert) {
assert.equal(this.$('.toggle-summary').length, 0);
}
});
widgetTest("topic map - has summary", {
template: '{{mount-widget widget="post" args=args toggleSummary="toggleSummary"}}',
setup() {
this.set('args', { showTopicMap: true, hasTopicSummary: true });
this.on('toggleSummary', () => this.summaryToggled = true);
},
test(assert) {
assert.equal(this.$('.toggle-summary').length, 1);
click('.toggle-summary button');
andThen(() => assert.ok(this.summaryToggled));
}
});
widgetTest("pm map", {
template: '{{mount-widget widget="post" args=args}}',
setup() {
this.set('args', {
showTopicMap: true,
showPMMap: true,
allowedGroups: [],
allowedUsers: [ Ember.Object.create({ username: 'eviltrout' }) ]
});
},
test(assert) {
assert.equal(this.$('.private-message-map').length, 1);
assert.equal(this.$('.private-message-map .user').length, 1);
}
});

View File

@@ -0,0 +1,67 @@
import { moduleForWidget, widgetTest } from 'helpers/widget-test';
moduleForWidget('poster-name');
widgetTest('basic rendering', {
template: '{{mount-widget widget="poster-name" args=args}}',
setup() {
this.set('args', {
username: 'eviltrout',
usernameUrl: '/users/eviltrout',
name: 'Robin Ward',
user_title: 'Trout Master' });
},
test(assert) {
assert.ok(this.$('.names').length);
assert.ok(this.$('span.username').length);
assert.ok(this.$('a[data-auto-route=true]').length);
assert.ok(this.$('a[data-user-card=eviltrout]').length);
assert.equal(this.$('.username a').text(), 'eviltrout');
assert.equal(this.$('.full-name a').text(), 'Robin Ward');
assert.equal(this.$('.user-title').text(), 'Trout Master');
}
});
widgetTest('extra classes and glyphs', {
template: '{{mount-widget widget="poster-name" args=args}}',
setup() {
this.set('args', {
username: 'eviltrout',
usernameUrl: '/users/eviltrout',
staff: true,
admin: true,
moderator: true,
new_user: true,
primary_group_name: 'fish'
});
},
test(assert) {
assert.ok(this.$('span.staff').length);
assert.ok(this.$('span.admin').length);
assert.ok(this.$('span.moderator').length);
assert.ok(this.$('i.fa-shield').length);
assert.ok(this.$('span.new-user').length);
assert.ok(this.$('span.fish').length);
}
});
widgetTest('disable display name on posts', {
template: '{{mount-widget widget="poster-name" args=args}}',
setup() {
this.siteSettings.display_name_on_posts = false;
this.set('args', { username: 'eviltrout', name: 'Robin Ward' });
},
test(assert) {
assert.equal(this.$('.full-name').length, 0);
}
});
widgetTest("doesn't render a name if it's similar to the username", {
template: '{{mount-widget widget="poster-name" args=args}}',
setup() {
this.set('args', { username: 'eviltrout', name: 'evil-trout' });
},
test(assert) {
assert.equal(this.$('.full-name').length, 0);
}
});