Finish new Message component, integrate into application
Also: - New schema version 8 with video/image thumbnails, screenshots, sizes - Upgrade messages not at current schema version when loading messages to show in conversation - New MessageDetail react component - New ConversationHeader react component
This commit is contained in:
parent
69f11c4a7b
commit
3c69886320
102 changed files with 9644 additions and 7381 deletions
|
@ -12,7 +12,10 @@ describe('i18n', function() {
|
|||
});
|
||||
it('returns message with multiple substitutions', function() {
|
||||
const actual = i18n('theyChangedTheTimer', ['Someone', '5 minutes']);
|
||||
assert.equal(actual, 'Someone set the timer to 5 minutes.');
|
||||
assert.equal(
|
||||
actual,
|
||||
'Someone set the disappearing message timer to 5 minutes'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
151
test/index.html
151
test/index.html
|
@ -96,37 +96,7 @@
|
|||
<p> {{ content }}</p>
|
||||
</script>
|
||||
<script type='text/x-tmpl-mustache' id='conversation'>
|
||||
<div class='conversation-header {{ avatar.color }}'>
|
||||
<div class='header-buttons left'>
|
||||
<div class='vertical-align'>
|
||||
<button class='back hide'></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class='header-buttons right'>
|
||||
<div class='vertical-align'>
|
||||
<div class='conversation-menu menu'>
|
||||
<button class='hamburger' alt='conversation menu'></button>
|
||||
<ul class='menu-list'>
|
||||
<li class='disappearing-messages'>{{ disappearing-messages }}</li>
|
||||
{{#group}}
|
||||
<li class='show-members'>{{ show-members }}</li>
|
||||
<!-- <li class='update-group'>Update group</li> -->
|
||||
<!-- <li class='leave-group'>Leave group</li> -->
|
||||
{{/group}}
|
||||
{{^group}}
|
||||
{{ ^isMe }}
|
||||
<li class='show-identity'>{{ show-identity }}</li>
|
||||
{{ /isMe }}
|
||||
<li class='end-session'>{{ end-session }}</li>
|
||||
{{/group}}
|
||||
<li class='destroy'>{{ destroy }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class='conversation-title'></span>
|
||||
{{> avatar }}
|
||||
</div>
|
||||
<div class='conversation-header'></div>
|
||||
<div class='main panel'>
|
||||
<div class='discussion-container'>
|
||||
<div class='bar-container hide'>
|
||||
|
@ -172,62 +142,6 @@
|
|||
<img src='{{ source }}' class='preview' />
|
||||
<a class='x close' alt='remove attachment' href='#'></a>
|
||||
</script>
|
||||
<script type='text/x-tmpl-mustache' id='hasRetry'>
|
||||
{{ messageNotSent }}
|
||||
<span href='#' class='retry'>{{ resend }}</span>
|
||||
</script>
|
||||
<script type='text/x-tmpl-mustache' id='keychange'>
|
||||
<span class='content' dir='auto'><span class='shield icon'></span> {{ content }}</span>
|
||||
</script>
|
||||
<script type='text/x-tmpl-mustache' id='verified-change'>
|
||||
<span class='content' dir='auto'><span class='{{ icon }} icon'></span> {{ content }}</span>
|
||||
</script>
|
||||
<script type='text/x-tmpl-mustache' id='message'>
|
||||
{{> avatar }}
|
||||
<div class='bubble {{ avatar.color }}'>
|
||||
<div class='sender' dir='auto'>
|
||||
{{ sender }}
|
||||
{{ #profileName }}
|
||||
<span class='profileName'>{{ profileName }} </span>
|
||||
{{ /profileName }}
|
||||
</div>
|
||||
<div class='tail-wrapper {{ innerBubbleClasses }}'>
|
||||
<div class='inner-bubble'>
|
||||
{{ #hasAttachments }}
|
||||
<div class='attachments'></div>
|
||||
{{ /hasAttachments }}
|
||||
{{ #hasBody }}
|
||||
<div class='content' dir='auto'>
|
||||
{{ #message }}
|
||||
<div class='body'></div>
|
||||
{{ /message }}
|
||||
</div>
|
||||
{{ /hasBody }}
|
||||
</div>
|
||||
</div>
|
||||
<div class='meta'>
|
||||
<span class='timestamp' data-timestamp={{ timestamp }}></span>
|
||||
<span class='status hide'></span>
|
||||
<span class='timer'></span>
|
||||
</div>
|
||||
{{ #hoverIcon }}
|
||||
<div class='menu-container menu'>
|
||||
<div class='menu-anchor'>
|
||||
<span class='dots-horizontal-icon'></span>
|
||||
<ul class='menu-list'>
|
||||
<li class='reply'>{{ reply }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{{ /hoverIcon }}
|
||||
</div>
|
||||
</script>
|
||||
<script type='text/x-tmpl-mustache' id='hourglass'>
|
||||
<span class='hourglass'><span class='sand'></span></span>
|
||||
</script>
|
||||
<script type='text/x-tmpl-mustache' id='expirationTimerUpdate'>
|
||||
<span class='content'><span class='icon clock'></span> {{ content }}</span>
|
||||
</script>
|
||||
<script type='text/x-tmpl-mustache' id='new-group-update'>
|
||||
<div class='conversation-header'>
|
||||
<button class='back'></button>
|
||||
|
@ -304,48 +218,6 @@
|
|||
<script type='text/x-tmpl-mustache' id='attachment-type-modal'>
|
||||
Sorry, your attachment has a type, {{type}}, that is not currently supported.
|
||||
</script>
|
||||
<script type='text/x-tmpl-mustache' id='message-detail'>
|
||||
<div class='container'>
|
||||
<div class='message-container'></div>
|
||||
<div class='info'>
|
||||
<table>
|
||||
{{ #errors }}
|
||||
<tr>
|
||||
<td class='label'>{{ errorLabel }}</td>
|
||||
<td> <span class='error-message'>{{message}}</span> </td>
|
||||
</tr>
|
||||
{{ /errors }}
|
||||
<tr>
|
||||
<td class='label'>{{ sent }}</td>
|
||||
<td> {{ sent_at }}</td>
|
||||
</tr>
|
||||
{{ #received_at }}
|
||||
<tr>
|
||||
<td class='label'>{{ received }}</td>
|
||||
<td> {{ received_at }}</td>
|
||||
</tr>
|
||||
{{ /received_at }}
|
||||
<tr> <td class='tofrom label'>{{tofrom}}</td> </tr>
|
||||
</table>
|
||||
<div class='contacts'>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
<script type='text/x-tmpl-mustache' id='identity-key-send-error'>
|
||||
<div class='container'>
|
||||
<div class='explanation'>
|
||||
{{ errorExplanation }}
|
||||
</div>
|
||||
<div class='safety-number'>
|
||||
<button class='show-safety-number grey'>{{ showSafetyNumber }}</button>
|
||||
</div>
|
||||
<div class='actions'>
|
||||
<button class='send-anyway grey'>{{ sendAnyway }}</button>
|
||||
<button class='cancel grey'>{{ cancel }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
<script type='text/x-tmpl-mustache' id='group-member-list'>
|
||||
<div class='container'>
|
||||
{{ #summary }} <div class='summary'>{{ summary }}</div>{{ /summary }}
|
||||
|
@ -425,24 +297,6 @@
|
|||
<span class='error-message'>{{message}}</span>
|
||||
{{ /message }}
|
||||
</script>
|
||||
<script type='text/x-tmpl-mustache' id='contact-detail'>
|
||||
<div class='clearfix'>
|
||||
{{> avatar }}
|
||||
<div class='contact-details'>
|
||||
{{ #errors }}
|
||||
<div class='error-icon-container'>
|
||||
<span class='error-icon'></span>
|
||||
</div>
|
||||
{{ /errors }}
|
||||
<span class='name' dir='auto'>{{ name }}</span>
|
||||
{{ #errors }}
|
||||
{{ #message }}
|
||||
<p class='error-message'>{{message}}</p>
|
||||
{{ /message }}
|
||||
{{ /errors }}
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
<script type='text/x-tmpl-mustache' id='link_to_support'>
|
||||
<a href='http://support.signal.org/hc/articles/213134107' target='_blank'>
|
||||
{{ learnMore }}
|
||||
|
@ -514,7 +368,6 @@
|
|||
<script type='text/javascript' src='../js/views/timestamp_view.js' data-cover></script>
|
||||
<script type='text/javascript' src='../js/views/message_view.js' data-cover></script>
|
||||
<script type='text/javascript' src='../js/views/key_verification_view.js' data-cover></script>
|
||||
<script type='text/javascript' src='../js/views/message_detail_view.js' data-cover></script>
|
||||
<script type='text/javascript' src='../js/views/message_list_view.js' data-cover></script>
|
||||
<script type='text/javascript' src='../js/views/group_member_list_view.js' data-cover></script>
|
||||
<script type='text/javascript' src='../js/views/recorder_view.js' data-cover></script>
|
||||
|
@ -528,12 +381,10 @@
|
|||
<script type='text/javascript' src='../js/views/last_seen_indicator_view.js' data-cover></script>
|
||||
<script type='text/javascript' src='../js/views/scroll_down_button_view.js' data-cover></script>
|
||||
<script type='text/javascript' src='../js/views/banner_view.js' data-cover></script>
|
||||
<script type="text/javascript" src='../js/views/identity_key_send_error_view.js' data-cover></script>
|
||||
<script type='text/javascript' src='../js/views/clear_data_view.js'></script>
|
||||
|
||||
<script type="text/javascript" src="views/whisper_view_test.js"></script>
|
||||
<script type="text/javascript" src="views/group_update_view_test.js"></script>
|
||||
<script type="text/javascript" src="views/message_view_test.js"></script>
|
||||
<script type="text/javascript" src="views/attachment_view_test.js"></script>
|
||||
<script type="text/javascript" src="views/timestamp_view_test.js"></script>
|
||||
<script type="text/javascript" src="views/list_view_test.js"></script>
|
||||
|
|
|
@ -164,21 +164,21 @@
|
|||
message = messages.add({ group_update: { left: 'Alice' } });
|
||||
assert.equal(
|
||||
message.getDescription(),
|
||||
'Alice left the group.',
|
||||
'Alice left the group',
|
||||
'Notes one person leaving the group.'
|
||||
);
|
||||
|
||||
message = messages.add({ group_update: { name: 'blerg' } });
|
||||
assert.equal(
|
||||
message.getDescription(),
|
||||
"Updated the group. Title is now 'blerg'.",
|
||||
"Title is now 'blerg'",
|
||||
'Returns a single notice if only group_updates.name changes.'
|
||||
);
|
||||
|
||||
message = messages.add({ group_update: { joined: ['Bob'] } });
|
||||
assert.equal(
|
||||
message.getDescription(),
|
||||
'Updated the group. Bob joined the group.',
|
||||
'Bob joined the group',
|
||||
'Returns a single notice if only group_updates.joined changes.'
|
||||
);
|
||||
|
||||
|
@ -187,7 +187,7 @@
|
|||
});
|
||||
assert.equal(
|
||||
message.getDescription(),
|
||||
'Updated the group. Bob, Alice, Eve joined the group.',
|
||||
'Bob, Alice, Eve joined the group',
|
||||
'Notes when >1 person joins the group.'
|
||||
);
|
||||
|
||||
|
@ -196,7 +196,7 @@
|
|||
});
|
||||
assert.equal(
|
||||
message.getDescription(),
|
||||
"Updated the group. Title is now 'blerg'. Bob joined the group.",
|
||||
"Title is now 'blerg', Bob joined the group",
|
||||
'Notes when there are multiple changes to group_updates properties.'
|
||||
);
|
||||
|
||||
|
|
|
@ -278,6 +278,12 @@ describe('Message', () => {
|
|||
return 'abc/abcdefg';
|
||||
},
|
||||
getRegionCode: () => 'US',
|
||||
getAbsoluteAttachmentPath: () => 'some/path/on/disk',
|
||||
makeObjectUrl: () => 'blob://FAKE',
|
||||
revokeObjectUrl: () => null,
|
||||
getImageDimensions: () => ({ height: 10, width: 15 }),
|
||||
makeImageThumbnail: () => new Blob(),
|
||||
makeVideoScreenshot: () => new Blob(),
|
||||
};
|
||||
const actual = await Message.upgradeSchema(input, context);
|
||||
assert.deepEqual(actual, expected);
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
/* global window: false */
|
||||
|
||||
// Because we aren't hosting the Style Guide in Electron, we can't rely on preload.js
|
||||
// to set things up for us. This gives us the minimum bar shims for everything it
|
||||
// provdes.
|
||||
//
|
||||
// Remember, the idea here is just to enable visual testing, no full functionality. Most
|
||||
// of thise can be very simple.
|
||||
|
||||
window.PROTO_ROOT = '/protos';
|
||||
window.nodeSetImmediate = () => {};
|
||||
|
||||
window.libphonenumber = {
|
||||
parse: number => ({
|
||||
e164: number,
|
||||
isValidNumber: true,
|
||||
getCountryCode: () => '1',
|
||||
getNationalNumber: () => number,
|
||||
}),
|
||||
isValidNumber: () => true,
|
||||
getRegionCodeForNumber: () => '1',
|
||||
format: number => number.e164,
|
||||
PhoneNumberFormat: {},
|
||||
};
|
||||
|
||||
window.Signal = {};
|
||||
window.Signal.Backup = {};
|
||||
window.Signal.Crypto = {};
|
||||
window.Signal.Logs = {};
|
||||
window.Signal.Migrations = {
|
||||
getPlaceholderMigrations: () => [
|
||||
{
|
||||
migrate: (transaction, next) => {
|
||||
console.log('migration version 1');
|
||||
transaction.db.createObjectStore('conversations');
|
||||
next();
|
||||
},
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
migrate: (transaction, next) => {
|
||||
console.log('migration version 2');
|
||||
const messages = transaction.db.createObjectStore('messages');
|
||||
messages.createIndex('expires_at', 'expireTimer', { unique: false });
|
||||
next();
|
||||
},
|
||||
version: 2,
|
||||
},
|
||||
{
|
||||
migrate: (transaction, next) => {
|
||||
console.log('migration version 3');
|
||||
transaction.db.createObjectStore('items');
|
||||
next();
|
||||
},
|
||||
version: 3,
|
||||
},
|
||||
],
|
||||
loadAttachmentData: attachment => Promise.resolve(attachment),
|
||||
getAbsoluteAttachmentPath: path => path,
|
||||
};
|
||||
|
||||
window.Signal.Components = {};
|
||||
|
||||
window.i18n = () => '';
|
||||
|
||||
// Ideally we don't need to add things here. We want to add them in StyleGuideUtil, which
|
||||
// means that references to these things can't be early-bound, not capturing the direct
|
||||
// reference to the function on file load.
|
||||
window.Signal.Migrations.V17 = {};
|
||||
window.Signal.OS = {};
|
||||
window.Signal.Types = {};
|
||||
window.Signal.Types.Attachment = {};
|
||||
window.Signal.Types.Conversation = {};
|
||||
window.Signal.Types.Errors = {};
|
||||
window.Signal.Types.Message = {
|
||||
initializeSchemaVersion: attributes => attributes,
|
||||
};
|
||||
window.Signal.Types.MIME = {};
|
||||
window.Signal.Types.Settings = {};
|
||||
window.Signal.Views = {};
|
||||
window.Signal.Views.Initialization = {};
|
||||
window.Signal.Workflow = {};
|
|
@ -1,90 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
/* global window: false */
|
||||
|
||||
// Taken from background.html.
|
||||
// Templates are here solely to support the Backbone views rendered in the Style Guide.
|
||||
|
||||
// Note: Any change here must be reflected in background.html to be reflected in the app
|
||||
// and test/index.html to be reflected in the unit tests.
|
||||
|
||||
window.Whisper.View.Templates = {
|
||||
hasRetry: `
|
||||
{{ messageNotSent }} <span href='#' class='retry'>{{ resend }}</span>
|
||||
`,
|
||||
'some-failed': `
|
||||
{{ someFailed }}
|
||||
`,
|
||||
keychange: `
|
||||
<span class='content' dir='auto'>
|
||||
<span class='shield icon'></span> {{ content }}
|
||||
</span>
|
||||
`,
|
||||
'verified-change': `
|
||||
<span class='content' dir='auto'>
|
||||
<span class='{{ icon }} icon'></span> {{ content }}
|
||||
</span>
|
||||
`,
|
||||
message: `
|
||||
{{> avatar }}
|
||||
<div class='bubble {{ avatar.color }}'>
|
||||
<div class='sender' dir='auto'>
|
||||
{{ sender }}
|
||||
{{ #profileName }}
|
||||
<span class='profileName'>{{ profileName }} </span>
|
||||
{{ /profileName }}
|
||||
</div>
|
||||
<div class='tail-wrapper {{ innerBubbleClasses }}'>
|
||||
<div class='inner-bubble'>
|
||||
{{ #hasAttachments }}
|
||||
<div class='attachments'></div>
|
||||
{{ /hasAttachments }}
|
||||
{{ #hasBody }}
|
||||
<div class='content' dir='auto'>
|
||||
{{ #message }}
|
||||
<div class='body'></div>
|
||||
{{ /message }}
|
||||
</div>
|
||||
{{ /hasBody }}
|
||||
</div>
|
||||
</div>
|
||||
<div class='meta'>
|
||||
<span class='timestamp' data-timestamp={{ timestamp }}></span>
|
||||
<span class='status hide'></span>
|
||||
<span class='timer'></span>
|
||||
</div>
|
||||
{{ #hoverIcon }}
|
||||
<div class='menu-container menu'>
|
||||
<div class='menu-anchor'>
|
||||
<span class='dots-horizontal-icon'></span>
|
||||
<ul class='menu-list'>
|
||||
<li class='reply'>{{ reply }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{{ /hoverIcon }}
|
||||
</div>
|
||||
`,
|
||||
hourglass: `
|
||||
<span class='hourglass'><span class='sand'></span></span>
|
||||
`,
|
||||
expirationTimerUpdate: `
|
||||
<span class='content'><span class='icon clock'></span> {{ content }}</span>
|
||||
`,
|
||||
'file-view': `
|
||||
<div class='icon {{ mediaType }}'></div>
|
||||
<div class='text'>
|
||||
<div class='fileName' title='{{ altText }}'>
|
||||
{{ fileName }}
|
||||
</div>
|
||||
<div class='fileSize'>{{ fileSize }}</div>
|
||||
</div>
|
||||
`,
|
||||
'error-icon': `
|
||||
<span class='error-icon'>
|
||||
</span>
|
||||
{{ #message }}
|
||||
<span class='error-message'>{{message}}</span>
|
||||
{{ /message }}
|
||||
`,
|
||||
};
|
|
@ -5,6 +5,22 @@
|
|||
'use strict';
|
||||
|
||||
describe('AttachmentView', () => {
|
||||
var convo, message;
|
||||
|
||||
before(async () => {
|
||||
await clearDatabase();
|
||||
convo = new Whisper.Conversation({ id: 'foo' });
|
||||
message = convo.messageCollection.add({
|
||||
conversationId: convo.id,
|
||||
body: 'hello world',
|
||||
type: 'outgoing',
|
||||
source: '+14158675309',
|
||||
received_at: Date.now(),
|
||||
});
|
||||
|
||||
await storage.put('number_id', '+18088888888.1');
|
||||
});
|
||||
|
||||
describe('with arbitrary files', () => {
|
||||
it('should render a file view', () => {
|
||||
const attachment = {
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
describe('MessageView', function() {
|
||||
var convo, message;
|
||||
|
||||
before(async () => {
|
||||
await clearDatabase();
|
||||
convo = new Whisper.Conversation({ id: 'foo' });
|
||||
message = convo.messageCollection.add({
|
||||
conversationId: convo.id,
|
||||
body: 'hello world',
|
||||
type: 'outgoing',
|
||||
source: '+14158675309',
|
||||
received_at: Date.now(),
|
||||
});
|
||||
|
||||
await storage.put('number_id', '+18088888888.1');
|
||||
});
|
||||
|
||||
it('should display the message text', function() {
|
||||
var view = new Whisper.MessageView({ model: message }).render();
|
||||
assert.match(view.$el.text(), /hello world/);
|
||||
});
|
||||
|
||||
it('should auto-update the message text', function() {
|
||||
var view = new Whisper.MessageView({ model: message }).render();
|
||||
message.set('body', 'goodbye world');
|
||||
assert.match(view.$el.html(), /goodbye world/);
|
||||
});
|
||||
|
||||
it('should have a nice timestamp', function() {
|
||||
var view = new Whisper.MessageView({ model: message });
|
||||
message.set({ sent_at: Date.now() - 5000 });
|
||||
view.render();
|
||||
assert.match(view.$el.html(), /now/);
|
||||
|
||||
message.set({ sent_at: Date.now() - 60000 });
|
||||
view.render();
|
||||
assert.match(view.$el.html(), /min/);
|
||||
|
||||
message.set({ sent_at: Date.now() - 3600000 });
|
||||
view.render();
|
||||
assert.match(view.$el.html(), /hour/);
|
||||
});
|
||||
it('should not imply messages are from the future', function() {
|
||||
var view = new Whisper.MessageView({ model: message });
|
||||
message.set({ sent_at: Date.now() + 60000 });
|
||||
view.render();
|
||||
assert.match(view.$el.html(), /now/);
|
||||
});
|
||||
|
||||
it('should go away when the model is destroyed', function() {
|
||||
var view = new Whisper.MessageView({ model: message });
|
||||
var div = $('<div>').append(view.$el);
|
||||
message.destroy();
|
||||
assert.strictEqual(div.find(view.$el).length, 0);
|
||||
});
|
||||
|
||||
it('allows links', function() {
|
||||
var url = 'http://example.com';
|
||||
message.set('body', url);
|
||||
var view = new Whisper.MessageView({ model: message });
|
||||
view.render();
|
||||
var link = view.$el.find('.body a');
|
||||
assert.strictEqual(link.length, 1);
|
||||
assert.strictEqual(link.text(), url);
|
||||
assert.strictEqual(link.attr('href'), url);
|
||||
});
|
||||
|
||||
it('disallows xss', function() {
|
||||
var xss = '<script>alert("pwnd")</script>';
|
||||
message.set('body', xss);
|
||||
var view = new Whisper.MessageView({ model: message });
|
||||
view.render();
|
||||
assert.include(view.$el.text(), xss); // should appear as escaped text
|
||||
assert.strictEqual(view.$el.find('script').length, 0); // should not appear as html
|
||||
});
|
||||
|
||||
it('supports emoji', function() {
|
||||
message.set('body', 'I \u2764\uFE0F emoji!');
|
||||
var view = new Whisper.MessageView({ model: message });
|
||||
view.render();
|
||||
var img = view.$el.find('.content img');
|
||||
assert.strictEqual(img.length, 1);
|
||||
assert.strictEqual(
|
||||
img.attr('src'),
|
||||
'node_modules/emoji-datasource-apple/img/apple/64/2764-fe0f.png'
|
||||
);
|
||||
assert.strictEqual(img.attr('title'), ':heart:');
|
||||
assert.strictEqual(img.attr('class'), 'emoji');
|
||||
});
|
||||
});
|
|
@ -10,7 +10,6 @@ describe('ScrollDownButtonView', function() {
|
|||
var view = new Whisper.ScrollDownButtonView({ count: 1 });
|
||||
view.render();
|
||||
assert.equal(view.count, 1);
|
||||
assert.match(view.$el.html(), /new-messages/);
|
||||
assert.match(view.$el.html(), /New message below/);
|
||||
});
|
||||
|
||||
|
@ -19,7 +18,6 @@ describe('ScrollDownButtonView', function() {
|
|||
view.render();
|
||||
assert.equal(view.count, 2);
|
||||
|
||||
assert.match(view.$el.html(), /new-messages/);
|
||||
assert.match(view.$el.html(), /New messages below/);
|
||||
});
|
||||
|
||||
|
@ -27,9 +25,9 @@ describe('ScrollDownButtonView', function() {
|
|||
var view = new Whisper.ScrollDownButtonView();
|
||||
view.render();
|
||||
assert.equal(view.count, 0);
|
||||
assert.notMatch(view.$el.html(), /new-messages/);
|
||||
assert.notMatch(view.$el.html(), /New message below/);
|
||||
view.increment(1);
|
||||
assert.equal(view.count, 1);
|
||||
assert.match(view.$el.html(), /new-messages/);
|
||||
assert.match(view.$el.html(), /New message below/);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue