mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-03 21:43:21 +00:00 
			
		
		
		
	We now use vdom-ish techniques to track the
list items for the pm list.  When we go to update
the list, we only re-render nodes whose data
has changed, with two exceptions:
    - Obviously, the first time we do a full render.
    - If the keys for the items have changed (i.e.
      a new node has come in or the order has changed),
      we just re-render the whole list.
If the keys are the same since the last re-render, we
only re-render individual items if their data has
changed.
Most of the new code is in these two modules:
    - pm_list_dom.js
    - vdom.js
We remove all of the code in pm_list.js that is
related to updating DOM with unread counts.
For presence updates, we are now *never*
re-rendering the whole list, since presence
updates only change individual line items and
don't affect the keys.  Instead, we just update
any changed elements in place.
The main thing that makes this all work is the
`update` method in `vdom`, which is totally generic
and essentially does a few simple jobs:
    - detect if keys are different
    - just render the whole ul as needed
    - for items that change, do the appropriate
      jQuery to update the item in place
Note that this code seems to play nice with simplebar.
Also, this code continues to use templates to render
the individual list items.
FWIW this code isn't radically different than list_render,
but it's got some key differences:
    - There are fewer bells and whistles in this code.
      Some of the stuff that list_render does is overkill
      for the PM list.
    - This code detects data changes.
Note that the vdom scheme is agnostic about templates;
it simply requires the child nodes to provide a render
method.  (This is similar to list_render, which is also
technically agnostic about rendering, but which also
does use templates in most cases.)
These fixes are somewhat related to #13605, but we
haven't gotten a solid repro on that issue, and
the scrolling issues there may be orthogonal to the
redraws.  But having fewer moving parts here should
help, and we won't get the rug pulled out from under
us on every presence update.
There are two possible extensions to this that are
somewhat overlapping in nature, but can be done
one a time.
    * We can do a deeper vdom approach here that
      gets us away from templates, and just have
      nodes write to an AST.  I have this on another
      branch, but it might be overkill.
    * We can avoid some redraws by detecting where
      keys are moving up and down.  I'm not completely
      sure we need it for the PM list.
If this gets merged, we may want to try similar
things for the stream list, which also does a fairly
complicated mixture of big-hammer re-renders and
surgical updates-in-place (with custom code).
BTW we have 100% line coverage for vdom.js.
		
	
		
			
				
	
	
		
			1785 lines
		
	
	
		
			56 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			1785 lines
		
	
	
		
			56 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
set_global('Handlebars', global.make_handlebars());
 | 
						|
zrequire('templates');
 | 
						|
 | 
						|
set_global('i18n', global.stub_i18n);
 | 
						|
set_global('page_params', {});
 | 
						|
zrequire('settings_notifications');
 | 
						|
zrequire('stream_edit');
 | 
						|
 | 
						|
const { JSDOM } = require("jsdom");
 | 
						|
const { window } = new JSDOM();
 | 
						|
global.$ = require('jquery')(window);
 | 
						|
 | 
						|
// When writing these tests, the following command might be helpful:
 | 
						|
// ./tools/get-handlebar-vars static/templates/*.hbs
 | 
						|
 | 
						|
function render(template_name, args) {
 | 
						|
    return require('../../static/templates/' + template_name + '.hbs')(args);
 | 
						|
}
 | 
						|
 | 
						|
run_test('handlebars_bug', () => {
 | 
						|
    // There was a bug in 1.0.9 where identically structured
 | 
						|
    // blocks get confused, so when foo is false, it still
 | 
						|
    // renders the foo-is-true block.
 | 
						|
    let s = '';
 | 
						|
    s += '{{#if foo}}';
 | 
						|
    s += '{{#if bar}}';
 | 
						|
    s += 'a';
 | 
						|
    s += '{{else}}';
 | 
						|
    s += 'b';
 | 
						|
    s += '{{/if}}';
 | 
						|
    s += '{{else}}';
 | 
						|
    s += '{{#if bar}}';
 | 
						|
    s += 'c';
 | 
						|
    s += '{{else}}';
 | 
						|
    s += 'd';
 | 
						|
    s += '{{/if}}';
 | 
						|
    s += '{{/if}}';
 | 
						|
    const template = global.Handlebars.compile(s);
 | 
						|
    const output = template({});
 | 
						|
 | 
						|
    assert.equal(output, 'd'); // the buggy version would return 'b'
 | 
						|
});
 | 
						|
 | 
						|
run_test('actions_popover_content', () => {
 | 
						|
    const args = {
 | 
						|
        should_display_quote_and_reply: true,
 | 
						|
        can_edit_message: true,
 | 
						|
        can_mute_topic: true,
 | 
						|
        narrowed: true,
 | 
						|
    };
 | 
						|
 | 
						|
    let html = '<div style="height: 250px">';
 | 
						|
    html += render('actions_popover_content', args);
 | 
						|
    html += "</div>";
 | 
						|
    const link = $(html).find("a.respond_button");
 | 
						|
    assert.equal(link.text().trim(), 'translated: Quote and reply');
 | 
						|
 | 
						|
    const deletedArgs = {
 | 
						|
        should_display_edit_and_view_source: false,
 | 
						|
        should_display_quote_and_reply: false,
 | 
						|
        narrowed: true,
 | 
						|
    };
 | 
						|
 | 
						|
    let deletedHtml = '<div style="height: 250px">';
 | 
						|
    deletedHtml += render('actions_popover_content', deletedArgs);
 | 
						|
    deletedHtml += "</div>";
 | 
						|
    const viewSourceLink = $(deletedHtml).find("a.popover_edit_message");
 | 
						|
    assert.equal(viewSourceLink.length, 0);
 | 
						|
    const quoteLink = $(deletedHtml).find("a.respond_button");
 | 
						|
    assert.equal(quoteLink.length, 0);
 | 
						|
});
 | 
						|
 | 
						|
run_test('admin_realm_domains_list', () => {
 | 
						|
    let html = "<table>";
 | 
						|
    const args = {
 | 
						|
        realm_domain: {
 | 
						|
            domain: 'zulip.org',
 | 
						|
            allow_subdomains: true,
 | 
						|
        },
 | 
						|
    };
 | 
						|
    html += render("settings/admin_realm_domains_list", args);
 | 
						|
    html += "</table>";
 | 
						|
 | 
						|
    const button = $(html).find('.button');
 | 
						|
    const domain = $(html).find('.domain');
 | 
						|
    const row = button.closest('tr');
 | 
						|
    const subdomains_checkbox = row.find('.allow-subdomains');
 | 
						|
 | 
						|
    assert.equal(button.text().trim(), "translated: Remove");
 | 
						|
    assert(button.hasClass("delete_realm_domain"));
 | 
						|
    assert.equal(domain.text(), "zulip.org");
 | 
						|
 | 
						|
    assert.equal(subdomains_checkbox.prop('checked'), true);
 | 
						|
});
 | 
						|
 | 
						|
run_test('admin_realm_dropdown_stream_list', () => {
 | 
						|
    let html = "<ul>";
 | 
						|
    const args = {
 | 
						|
        stream: {
 | 
						|
            name: "Italy",
 | 
						|
            subscriber_count: 9,
 | 
						|
            stream_id: 18,
 | 
						|
        },
 | 
						|
    };
 | 
						|
    html += render("settings/admin_realm_dropdown_stream_list", args);
 | 
						|
    html += "</ul>";
 | 
						|
 | 
						|
    const link = $(html).find("a");
 | 
						|
    const list_item = $(html).find("li");
 | 
						|
 | 
						|
    assert.equal(link.text().trim(), "Italy");
 | 
						|
    assert(list_item.hasClass("stream_name"));
 | 
						|
    assert.equal(list_item.attr("data-stream-id"), "18");
 | 
						|
});
 | 
						|
 | 
						|
run_test('admin_default_streams_list', () => {
 | 
						|
    let html = '<table>';
 | 
						|
    const streams = ['devel', 'trac', 'zulip'];
 | 
						|
 | 
						|
    // When the logged in user is admin
 | 
						|
    _.each(streams, function (stream) {
 | 
						|
        const args = {
 | 
						|
            stream: {name: stream, invite_only: false},
 | 
						|
            can_modify: true,
 | 
						|
        };
 | 
						|
        html += render('admin_default_streams_list', args);
 | 
						|
    });
 | 
						|
    html += "</table>";
 | 
						|
    let span = $(html).find(".default_stream_name").first();
 | 
						|
    assert.equal(span.text(), "devel");
 | 
						|
 | 
						|
    // When the logged in user is not admin
 | 
						|
    html = '<table>';
 | 
						|
    _.each(streams, function (stream) {
 | 
						|
        const args = {
 | 
						|
            stream: {name: stream, invite_only: false},
 | 
						|
            can_modify: false,
 | 
						|
        };
 | 
						|
        html += render('admin_default_streams_list', args);
 | 
						|
    });
 | 
						|
    html += "</table>";
 | 
						|
    span = $(html).find(".default_stream_name").first();
 | 
						|
    assert.equal(span.text(), "devel");
 | 
						|
});
 | 
						|
 | 
						|
run_test('admin_emoji_list', () => {
 | 
						|
    const args = {
 | 
						|
        emoji: {
 | 
						|
            name: "MouseFace",
 | 
						|
            display_name: "MouseFace",
 | 
						|
            source_url: "http://emojipedia-us.s3.amazonaws.com/cache/46/7f/467fe69069c408e07517621f263ea9b5.png",
 | 
						|
        },
 | 
						|
    };
 | 
						|
 | 
						|
    let html = '';
 | 
						|
    html += '<tbody id="admin_emoji_table">';
 | 
						|
    html += render('admin_emoji_list', args);
 | 
						|
    html += '</tbody>';
 | 
						|
 | 
						|
    const emoji_name = $(html).find('tr.emoji_row').first().find('span.emoji_name');
 | 
						|
    const emoji_url = $(html).find('tr.emoji_row').first().find('span.emoji_image img');
 | 
						|
 | 
						|
    assert.equal(emoji_name.text(), 'MouseFace');
 | 
						|
    assert.equal(emoji_url.attr('src'), 'http://emojipedia-us.s3.amazonaws.com/cache/46/7f/467fe69069c408e07517621f263ea9b5.png');
 | 
						|
});
 | 
						|
 | 
						|
run_test('admin_profile_field_list', () => {
 | 
						|
 | 
						|
    // When the logged in user is admin
 | 
						|
    let args = {
 | 
						|
        profile_field: {
 | 
						|
            name: "teams",
 | 
						|
            type: "Long text",
 | 
						|
        },
 | 
						|
        can_modify: true,
 | 
						|
    };
 | 
						|
 | 
						|
    let html = '';
 | 
						|
    html += '<tbody id="admin_profile_fields_table">';
 | 
						|
    html += render('admin_profile_field_list', args);
 | 
						|
    html += '</tbody>';
 | 
						|
 | 
						|
    let field_name = $(html).find('tr.profile-field-row').first().find('span.profile_field_name');
 | 
						|
    let field_type = $(html).find('tr.profile-field-row').first().find('span.profile_field_type');
 | 
						|
    let td = $(html).find('tr.profile-field-row').first().find('td');
 | 
						|
 | 
						|
    assert.equal(field_name.text(), 'teams');
 | 
						|
    assert.equal(field_type.text(), 'Long text');
 | 
						|
    assert.equal(td.length, 4);
 | 
						|
 | 
						|
    // When the logged in user is not admin
 | 
						|
    args = {
 | 
						|
        profile_field: {
 | 
						|
            name: "teams",
 | 
						|
            type: "Long text",
 | 
						|
        },
 | 
						|
        can_modify: false,
 | 
						|
    };
 | 
						|
 | 
						|
    html = '';
 | 
						|
    html += '<tbody id="admin_profile_fields_table">';
 | 
						|
    html += render('admin_profile_field_list', args);
 | 
						|
    html += '</tbody>';
 | 
						|
 | 
						|
    field_name = $(html).find('tr.profile-field-row').first().find('span.profile_field_name');
 | 
						|
    field_type = $(html).find('tr.profile-field-row').first().find('span.profile_field_type');
 | 
						|
    td = $(html).find('tr.profile-field-row').first().find('td');
 | 
						|
 | 
						|
    assert.equal(field_name.text(), 'teams');
 | 
						|
    assert.equal(field_type.text(), 'Long text');
 | 
						|
    assert.equal(td.length, 3);
 | 
						|
});
 | 
						|
 | 
						|
run_test('admin_filter_list', () => {
 | 
						|
 | 
						|
    // When the logged in user is admin
 | 
						|
    let args = {
 | 
						|
        filter: {
 | 
						|
            pattern: "#(?P<id>[0-9]+)",
 | 
						|
            url_format_string: "https://trac.example.com/ticket/%(id)s",
 | 
						|
        },
 | 
						|
        can_modify: true,
 | 
						|
    };
 | 
						|
 | 
						|
    let html = '';
 | 
						|
    html += '<tbody id="admin_filters_table">';
 | 
						|
    html += render('admin_filter_list', args);
 | 
						|
    html += '</tbody>';
 | 
						|
 | 
						|
    let filter_pattern = $(html).find('tr.filter_row').first().find('span.filter_pattern');
 | 
						|
    let filter_format = $(html).find('tr.filter_row').first().find('span.filter_url_format_string');
 | 
						|
 | 
						|
    assert.equal(filter_pattern.text(), '#(?P<id>[0-9]+)');
 | 
						|
    assert.equal(filter_format.text(), 'https://trac.example.com/ticket/%(id)s');
 | 
						|
 | 
						|
    // When the logged in user is not admin
 | 
						|
    args = {
 | 
						|
        filter: {
 | 
						|
            pattern: "#(?P<id>[0-9]+)",
 | 
						|
            url_format_string: "https://trac.example.com/ticket/%(id)s",
 | 
						|
        },
 | 
						|
        can_modify: false,
 | 
						|
    };
 | 
						|
 | 
						|
    html = '';
 | 
						|
    html += '<tbody id="admin_filters_table">';
 | 
						|
    html += render('admin_filter_list', args);
 | 
						|
    html += '</tbody>';
 | 
						|
 | 
						|
    filter_pattern = $(html).find('tr.filter_row').first().find('span.filter_pattern');
 | 
						|
    filter_format = $(html).find('tr.filter_row').first().find('span.filter_url_format_string');
 | 
						|
 | 
						|
    assert.equal(filter_pattern.text(), '#(?P<id>[0-9]+)');
 | 
						|
    assert.equal(filter_format.text(), 'https://trac.example.com/ticket/%(id)s');
 | 
						|
});
 | 
						|
 | 
						|
run_test('admin_invites_list', () => {
 | 
						|
    let html = '<table>';
 | 
						|
    const invites = ['alice', 'bob', 'carl'];
 | 
						|
    let invite_id = 0;
 | 
						|
    _.each(invites, function (invite) {
 | 
						|
        const args = {
 | 
						|
            invite: {
 | 
						|
                email: invite + '@zulip.com',
 | 
						|
                ref: 'iago@zulip.com',
 | 
						|
                invited: "2017-01-01 01:01:01",
 | 
						|
                id: invite_id,
 | 
						|
                invited_as_admin: true,
 | 
						|
            },
 | 
						|
        };
 | 
						|
        html += render('admin_invites_list', args);
 | 
						|
        invite_id += 1;
 | 
						|
    });
 | 
						|
    html += "</table>";
 | 
						|
    const buttons = $(html).find('.button');
 | 
						|
 | 
						|
    assert.equal($(buttons[0]).text().trim(), "translated: Revoke");
 | 
						|
    assert($(buttons[0]).hasClass("revoke"));
 | 
						|
    assert.equal($(buttons[0]).attr("data-invite-id"), 0);
 | 
						|
 | 
						|
    assert.equal($(buttons[3]).text().trim(), "translated: Resend");
 | 
						|
    assert($(buttons[3]).hasClass("resend"));
 | 
						|
    assert.equal($(buttons[3]).attr("data-invite-id"), 1);
 | 
						|
 | 
						|
    const span = $(html).find(".email").first();
 | 
						|
    assert.equal(span.text(), "alice@zulip.com");
 | 
						|
});
 | 
						|
 | 
						|
run_test('admin_tab', () => {
 | 
						|
    const args = {
 | 
						|
        realm_name: 'Zulip',
 | 
						|
    };
 | 
						|
    const html = render('admin_tab', args);
 | 
						|
    const admin_features = ["admin_users_table", "admin_bots_table",
 | 
						|
                            "admin_deactivated_users_table", "admin_invites_table"];
 | 
						|
    _.each(admin_features, function (admin_feature) {
 | 
						|
        assert.notEqual($(html).find("#" + admin_feature).length, 0);
 | 
						|
    });
 | 
						|
    assert.equal($(html).find("input.admin-realm-name").val(), 'Zulip');
 | 
						|
});
 | 
						|
 | 
						|
run_test('admin_user_group_list', () => {
 | 
						|
    const args = {
 | 
						|
        user_group: {
 | 
						|
            id: "9",
 | 
						|
            name: "uranohoshi",
 | 
						|
            description: "Students at Uranohoshi Academy",
 | 
						|
        },
 | 
						|
    };
 | 
						|
 | 
						|
    let html = '';
 | 
						|
    html += '<div id="user-groups">';
 | 
						|
    html += render('admin_user_group_list', args);
 | 
						|
    html += '</div>';
 | 
						|
 | 
						|
    const group_id = $(html).find('.user-group').first().prop('id');
 | 
						|
    const group_pills_id = $(html).find('.user-group').first().find('.pill-container').attr('data-group-pills');
 | 
						|
    const group_name_display = $(html).find('.user-group').first().find('.name').text().trim().replace(/\s+/g, ' ');
 | 
						|
    const group_description = $(html).find('.user-group').first().find('.description').text().trim().replace(/\s+/g, ' ');
 | 
						|
 | 
						|
    assert.equal(group_id, '9');
 | 
						|
    assert.equal(group_pills_id, '9');
 | 
						|
    assert.equal(group_name_display, 'uranohoshi');
 | 
						|
    assert.equal(group_description, 'Students at Uranohoshi Academy');
 | 
						|
});
 | 
						|
 | 
						|
run_test('admin_user_list', () => {
 | 
						|
    let html = '<table>';
 | 
						|
    const users = ['alice', 'bob', 'carl'];
 | 
						|
 | 
						|
    // When the logged in user is admin
 | 
						|
    _.each(users, function (user) {
 | 
						|
        const args = {
 | 
						|
            user: {
 | 
						|
                is_active: true,
 | 
						|
                is_active_human: true,
 | 
						|
                email: user + '@zulip.com',
 | 
						|
                full_name: user,
 | 
						|
            },
 | 
						|
            can_modify: true,
 | 
						|
        };
 | 
						|
        html += render('admin_user_list', args);
 | 
						|
    });
 | 
						|
    html += "</table>";
 | 
						|
 | 
						|
    let buttons = $(html).find('.button');
 | 
						|
 | 
						|
    assert.equal($(buttons[0]).text().trim(), "translated: Deactivate");
 | 
						|
    assert($(buttons[0]).hasClass("deactivate"));
 | 
						|
 | 
						|
    assert.equal($(buttons[1]).attr('title').trim(), "translated: Edit user");
 | 
						|
    assert($(buttons[1]).hasClass("open-user-form"));
 | 
						|
 | 
						|
    // When the logged in user is not admin
 | 
						|
    html = '<table>';
 | 
						|
    _.each(users, function (user) {
 | 
						|
        const args = {
 | 
						|
            user: {
 | 
						|
                is_active: true,
 | 
						|
                is_active_human: true,
 | 
						|
                email: user + '@zulip.com',
 | 
						|
                full_name: user,
 | 
						|
            },
 | 
						|
            can_modify: false,
 | 
						|
        };
 | 
						|
        html += render('admin_user_list', args);
 | 
						|
    });
 | 
						|
    html += "</table>";
 | 
						|
 | 
						|
    buttons = $(html).find('.button');
 | 
						|
    // No buttons should be availabe to non-admins
 | 
						|
    assert.equal($(buttons).length, 0);
 | 
						|
});
 | 
						|
 | 
						|
run_test('alert_word_settings_item', () => {
 | 
						|
    let html = '<ul id="alert-words">';
 | 
						|
    const words = ['lunch', 'support'];
 | 
						|
    let args;
 | 
						|
    _.each(words, function (word) {
 | 
						|
        args = {
 | 
						|
            word: word,
 | 
						|
        };
 | 
						|
        html += render('alert_word_settings_item', args);
 | 
						|
    });
 | 
						|
    args = {
 | 
						|
        word: '',
 | 
						|
        editing: true,
 | 
						|
    };
 | 
						|
    html += render('alert_word_settings_item', args);
 | 
						|
    html += "</ul>";
 | 
						|
 | 
						|
    const li = $(html).find("li.alert-word-item").first();
 | 
						|
    const value = li.find('.value');
 | 
						|
    let button = li.find('button');
 | 
						|
    assert.equal(li.attr('data-word'), 'lunch');
 | 
						|
    assert.equal(value.length, 1);
 | 
						|
    assert.equal(value.text(), 'lunch');
 | 
						|
    assert.equal(button.attr('title'), 'translated: Delete alert word');
 | 
						|
    assert.equal(button.attr('data-word'), 'lunch');
 | 
						|
 | 
						|
    const title = $(html).find('.new-alert-word-section-title');
 | 
						|
    const textbox = $(html).find('#create_alert_word_name');
 | 
						|
    button = $(html).find('#create_alert_word_button');
 | 
						|
    assert.equal(title.length, 1);
 | 
						|
    assert.equal(title.text().trim(), 'translated: Add a new alert word');
 | 
						|
    assert.equal(textbox.length, 1);
 | 
						|
    assert.equal(textbox.attr('maxlength'), 100);
 | 
						|
    assert.equal(textbox.attr('placeholder'), 'translated: Alert word');
 | 
						|
    assert.equal(textbox.attr('class'), 'required');
 | 
						|
    assert.equal(button.length, 1);
 | 
						|
    assert.equal(button.text().trim(), 'translated: Add alert word');
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
run_test('all_messages_sidebar_actions', () => {
 | 
						|
    render('all_messages_sidebar_actions');
 | 
						|
});
 | 
						|
 | 
						|
run_test('announce_stream_docs', () => {
 | 
						|
    render('announce_stream_docs');
 | 
						|
});
 | 
						|
 | 
						|
run_test('bankruptcy_modal', () => {
 | 
						|
    const args = {
 | 
						|
        unread_count: 99,
 | 
						|
    };
 | 
						|
    const html = render('bankruptcy_modal', args);
 | 
						|
    const count = $(html).find("p b");
 | 
						|
    assert.equal(count.text(), 99);
 | 
						|
});
 | 
						|
 | 
						|
run_test('settings/admin_auth_methods_list', () => {
 | 
						|
    const args = {
 | 
						|
        method: "Email",
 | 
						|
        enabled: false,
 | 
						|
    };
 | 
						|
 | 
						|
    let html = '';
 | 
						|
    html += '<tbody>';
 | 
						|
    html += render('settings/admin_auth_methods_list', args);
 | 
						|
    html += '</tbody>';
 | 
						|
 | 
						|
    const method = $(html).find('tr.method_row').first().find('span.method');
 | 
						|
    assert.equal(method.text(), 'Email');
 | 
						|
    assert.equal(method.is(":checked"), false);
 | 
						|
});
 | 
						|
 | 
						|
run_test('bookend', () => {
 | 
						|
    // Do subscribed/unsubscribed cases here.
 | 
						|
    let args = {
 | 
						|
        bookend_content: "subscribed to stream",
 | 
						|
        trailing: true,
 | 
						|
        subscribed: true,
 | 
						|
    };
 | 
						|
    let html;
 | 
						|
 | 
						|
    html = render('bookend', args);
 | 
						|
    assert.equal($(html).text().trim(), "subscribed to stream\n    \n        \n            translated: Unsubscribe");
 | 
						|
 | 
						|
    args = {
 | 
						|
        bookend_content: "Not subscribed to stream",
 | 
						|
        trailing: true,
 | 
						|
        subscribed: false,
 | 
						|
    };
 | 
						|
 | 
						|
    html = render('bookend', args);
 | 
						|
    assert.equal($(html).text().trim(), 'Not subscribed to stream\n    \n        \n            translated: Subscribe');
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
run_test('bot_avatar_row', () => {
 | 
						|
    let html = '';
 | 
						|
    const args = {
 | 
						|
        email: "hamlet@zulip.com",
 | 
						|
        api_key: "123456ABCD",
 | 
						|
        name: "Hamlet",
 | 
						|
        avatar_url: "/hamlet/avatar/url",
 | 
						|
    };
 | 
						|
    html += render('bot_avatar_row', args);
 | 
						|
 | 
						|
    const img = $(html).find("img");
 | 
						|
    assert.equal(img.attr('src'), '/hamlet/avatar/url');
 | 
						|
});
 | 
						|
 | 
						|
run_test('bot_owner_select', () => {
 | 
						|
    const args = {
 | 
						|
        users_list: [
 | 
						|
            {
 | 
						|
                email: "hamlet@zulip.com",
 | 
						|
                api_key: "123456ABCD",
 | 
						|
                full_name: "Hamlet",
 | 
						|
                avatar_url: "/hamlet/avatar/url",
 | 
						|
            },
 | 
						|
        ],
 | 
						|
    };
 | 
						|
    const html = render('bot_owner_select', args);
 | 
						|
    const option = $(html).find("option").last();
 | 
						|
    assert.equal(option.val(), "hamlet@zulip.com");
 | 
						|
    assert.equal(option.text(), "Hamlet");
 | 
						|
});
 | 
						|
 | 
						|
 | 
						|
run_test('compose_invite_users', () => {
 | 
						|
    const args = {
 | 
						|
        email: 'hamlet@zulip.com',
 | 
						|
        name: 'Hamlet',
 | 
						|
        can_subscribe_other_users: true,
 | 
						|
    };
 | 
						|
    let html = render('compose_invite_users', args);
 | 
						|
    let button = $(html).find("button").first();
 | 
						|
    assert.equal(button.text(), "translated: Subscribe");
 | 
						|
 | 
						|
    args.can_subscribe_other_users = false;
 | 
						|
    html = render('compose_invite_users', args);
 | 
						|
    button = $(html).find("button").first();
 | 
						|
    assert.equal(button.length, 0);
 | 
						|
});
 | 
						|
 | 
						|
run_test('compose_all_everyone', () => {
 | 
						|
    const args = {
 | 
						|
        count: '101',
 | 
						|
        name: 'all',
 | 
						|
    };
 | 
						|
    const html = render('compose_all_everyone', args);
 | 
						|
    const button = $(html).find("button").first();
 | 
						|
    assert.equal(button.text(), "translated: Yes, send");
 | 
						|
    const error_msg = $(html).find('span.compose-all-everyone-msg').text().trim();
 | 
						|
    assert.equal(error_msg, "translated: Are you sure you want to mention all 101 people in this stream?");
 | 
						|
});
 | 
						|
 | 
						|
run_test('compose_announce', () => {
 | 
						|
    const args = {
 | 
						|
        count: '101',
 | 
						|
    };
 | 
						|
    const html = render('compose_announce', args);
 | 
						|
    const button = $(html).find("button").first();
 | 
						|
    assert.equal(button.text(), "translated: Yes, send");
 | 
						|
    const error_msg = $(html).find('span.compose-announce-msg').text().trim();
 | 
						|
    assert.equal(error_msg, "translated: This stream is reserved for announcements.  Are you sure you want to message all 101 people in this stream?");
 | 
						|
});
 | 
						|
 | 
						|
run_test('compose_not_subscribed', () => {
 | 
						|
    let html = render('compose_not_subscribed', {should_display_sub_button: true});
 | 
						|
    let button = $(html).find("button").first();
 | 
						|
    assert.equal(button.text(), "translated: Subscribe");
 | 
						|
    html = render('compose_not_subscribed', {should_display_sub_button: false});
 | 
						|
    button = $(html).find("button").first();
 | 
						|
    assert.equal(button.length, 0);
 | 
						|
});
 | 
						|
 | 
						|
run_test('compose_notification', () => {
 | 
						|
    const args = {
 | 
						|
        note: "You sent a message to a muted topic.",
 | 
						|
        link_text: "Narrow to here",
 | 
						|
        link_msg_id: "99",
 | 
						|
        link_class: "compose_notification_narrow_by_topic",
 | 
						|
    };
 | 
						|
    let html = '<div  id="out-of-view-notification" class="notification-alert">';
 | 
						|
    html += render('compose_notification', args);
 | 
						|
    html += '</div>';
 | 
						|
    const a = $(html).find("a.compose_notification_narrow_by_topic");
 | 
						|
    assert.equal(a.text(), "Narrow to here");
 | 
						|
});
 | 
						|
 | 
						|
run_test('compose_private_stream_alert', () => {
 | 
						|
    const args = {
 | 
						|
        stream_name: 'Denmark',
 | 
						|
    };
 | 
						|
    const html = render('compose_private_stream_alert', args);
 | 
						|
    assert($(html).hasClass('compose_private_stream_alert'));
 | 
						|
 | 
						|
    const actual_text = $(html).text();
 | 
						|
    const expected_text = 'translated: Warning: Denmark is a private stream.';
 | 
						|
    assert(actual_text.indexOf(expected_text) >= 1);
 | 
						|
});
 | 
						|
 | 
						|
run_test('custom_user_profile_field', () => {
 | 
						|
    const field = {name: "GitHub user name", id: 2, hint: "Or link to profile"};
 | 
						|
    const args = {field: field, field_value: {value: "@GitHub", rendered_value: "<p>@GitHub</p>"}, field_type: "text"};
 | 
						|
    const html = render('settings/custom_user_profile_field', args);
 | 
						|
    assert.equal($(html).attr('data-field-id'), 2);
 | 
						|
    assert.equal($(html).find('.custom_user_field_value').val(), "@GitHub");
 | 
						|
    assert.equal($(html).find('.field_hint').text(), "Or link to profile");
 | 
						|
    assert.equal($(html).find('label').text(), "GitHub user name");
 | 
						|
});
 | 
						|
 | 
						|
run_test('deactivate_stream_modal', () => {
 | 
						|
    const args = {
 | 
						|
        stream_name: "Public stream",
 | 
						|
        stream_id: 1,
 | 
						|
    };
 | 
						|
    const html = render('settings/deactivation_stream_modal', args);
 | 
						|
 | 
						|
    const modal_header = $(html).find("#deactivation_stream_modal_label");
 | 
						|
    assert.equal(modal_header.text(), "translated: Delete stream " + args.stream_name);
 | 
						|
 | 
						|
    const button = $(html).find("#do_deactivate_stream_button");
 | 
						|
    assert.equal(button.text(), "translated: Yes, delete this stream");
 | 
						|
    assert.equal(button.data('stream-id'), args.stream_id);
 | 
						|
});
 | 
						|
 | 
						|
run_test('settings/dev_env_email_access', () => {
 | 
						|
    render('settings/dev_env_email_access');
 | 
						|
});
 | 
						|
 | 
						|
run_test('draft_table_body', () => {
 | 
						|
    const args = {
 | 
						|
        drafts: [
 | 
						|
            {
 | 
						|
                draft_id: '1',
 | 
						|
                is_stream: true,
 | 
						|
                stream: 'all',
 | 
						|
                stream_color: '#FF0000',  // hsl(0, 100%, 50%)
 | 
						|
                topic: 'tests',
 | 
						|
                content: 'Public draft',
 | 
						|
            },
 | 
						|
            {
 | 
						|
                draft_id: '2',
 | 
						|
                is_stream: false,
 | 
						|
                recipients: 'Jordan, Michael',
 | 
						|
                content: 'Private draft',
 | 
						|
            },
 | 
						|
        ],
 | 
						|
    };
 | 
						|
 | 
						|
    let html = '';
 | 
						|
    html += '<div id="drafts_table">';
 | 
						|
    html += render('draft_table_body', args);
 | 
						|
    html += '</div>';
 | 
						|
 | 
						|
    const row_1 = $(html).find(".draft-row[data-draft-id='1']");
 | 
						|
    assert.equal(row_1.find(".stream_label").text().trim(), "all");
 | 
						|
    assert.equal(row_1.find(".stream_label").css("background"), "rgb(255, 0, 0)");
 | 
						|
    assert.equal(row_1.find(".stream_topic").text().trim(), "tests");
 | 
						|
    assert(!row_1.find(".message_row").hasClass("private-message"));
 | 
						|
    assert.equal(row_1.find(".message_content").text().trim(), "Public draft");
 | 
						|
 | 
						|
    const row_2 = $(html).find(".draft-row[data-draft-id='2']");
 | 
						|
    assert.equal(row_2.find(".stream_label").text().trim(), "translated: You and Jordan, Michael");
 | 
						|
    assert(row_2.find(".message_row").hasClass("private-message"));
 | 
						|
    assert.equal(row_2.find(".message_content").text().trim(), "Private draft");
 | 
						|
});
 | 
						|
 | 
						|
run_test('emoji_popover', () => {
 | 
						|
    const args = {
 | 
						|
        class: "emoji-info-popover",
 | 
						|
    };
 | 
						|
    let html = "<div>";
 | 
						|
    html += render('emoji_popover', args);
 | 
						|
    html += "</div>";
 | 
						|
    const popover = $(html).find(".popover");
 | 
						|
    assert(popover.hasClass("emoji-info-popover"));
 | 
						|
});
 | 
						|
 | 
						|
run_test('emoji_popover_content', () => {
 | 
						|
    const args = {
 | 
						|
        search: 'Search',
 | 
						|
        message_id: 1,
 | 
						|
        emoji_categories: [
 | 
						|
            {
 | 
						|
                name: 'Test',
 | 
						|
                emojis: [
 | 
						|
                    {
 | 
						|
                        has_reacted: false,
 | 
						|
                        is_realm_emoji: false,
 | 
						|
                        name: '100',
 | 
						|
                        emoji_code: '100',
 | 
						|
                    },
 | 
						|
                ],
 | 
						|
            },
 | 
						|
            {
 | 
						|
                name: 'Test1',
 | 
						|
                emojis: [
 | 
						|
                    {
 | 
						|
                        has_reacted: false,
 | 
						|
                        is_realm_emoji: true,
 | 
						|
                        name: 'zulip',
 | 
						|
                        url: 'zulip',
 | 
						|
                    },
 | 
						|
                ],
 | 
						|
            },
 | 
						|
        ],
 | 
						|
    };
 | 
						|
 | 
						|
    let html = '<div style="height: 250px">';
 | 
						|
    html += render('emoji_popover_content', args);
 | 
						|
    html += "</div>";
 | 
						|
    // test to make sure the first emoji is present in the popover
 | 
						|
    const first_emoji = $(html).find(".emoji-100");
 | 
						|
    assert.equal(first_emoji.length, 1);
 | 
						|
 | 
						|
    const categories = $(html).find(".emoji-popover-tab-item");
 | 
						|
    assert.equal(categories.length, 2);
 | 
						|
 | 
						|
    const category_1 = $(html).find(".emoji-popover-tab-item[data-tab-name = 'Test']");
 | 
						|
    assert(category_1.hasClass("active"));
 | 
						|
});
 | 
						|
 | 
						|
run_test('emoji_popover_search_results', () => {
 | 
						|
    const args = {
 | 
						|
        message_id: 1,
 | 
						|
        search_results: [
 | 
						|
            {
 | 
						|
                has_reacted: false,
 | 
						|
                is_realm_emoji: false,
 | 
						|
                name: 'test-1',
 | 
						|
                emoji_code: 'test-1',
 | 
						|
            },
 | 
						|
            {
 | 
						|
                has_reacted: true,
 | 
						|
                is_realm_emoji: false,
 | 
						|
                name: 'test-2',
 | 
						|
                emoji_code: 'test-2',
 | 
						|
            },
 | 
						|
        ],
 | 
						|
    };
 | 
						|
    let html = "<div>";
 | 
						|
    html += render("emoji_popover_search_results", args);
 | 
						|
    html += "</div>";
 | 
						|
    const used_emoji = $(html).find(".emoji-test-2").parent();
 | 
						|
    assert(used_emoji.hasClass("reaction"));
 | 
						|
    assert(used_emoji.hasClass("reacted"));
 | 
						|
});
 | 
						|
 | 
						|
run_test('emoji_showcase', () => {
 | 
						|
    const args = {
 | 
						|
        emoji_dict: {
 | 
						|
            name: "thumbs_up",
 | 
						|
            is_realm_emoji: false,
 | 
						|
            emoji_code: "1f44d",
 | 
						|
            has_reacted: false,
 | 
						|
        },
 | 
						|
    };
 | 
						|
    const html = render("emoji_showcase", args);
 | 
						|
    const emoji_div = $(html).find(".emoji");
 | 
						|
    const canonical_name = $(html).find(".emoji-canonical-name");
 | 
						|
 | 
						|
    assert.equal(emoji_div.length, 1);
 | 
						|
    assert(emoji_div.hasClass("emoji-1f44d"));
 | 
						|
    assert.equal(canonical_name.text(), "thumbs_up");
 | 
						|
    assert.equal(canonical_name.attr("title"), "thumbs_up");
 | 
						|
});
 | 
						|
 | 
						|
run_test('group_pms', () => {
 | 
						|
    const args = {
 | 
						|
        group_pms: [
 | 
						|
            {
 | 
						|
                fraction_present: 0.1,
 | 
						|
                emails: "alice@zulip.com,bob@zulip.com",
 | 
						|
                short_name: "Alice and Bob",
 | 
						|
                name: "Alice and Bob",
 | 
						|
            },
 | 
						|
        ],
 | 
						|
    };
 | 
						|
    const html = render('group_pms', args);
 | 
						|
 | 
						|
    const a = $(html).find("a").first();
 | 
						|
    assert.equal(a.text(), 'Alice and Bob');
 | 
						|
});
 | 
						|
 | 
						|
run_test('hotspot_overlay', () => {
 | 
						|
    const args = {
 | 
						|
        title: 'Start a new conversation',
 | 
						|
        name: 'intro_compose',
 | 
						|
        description: 'Click the "New topic" button to start a new conversation.',
 | 
						|
    };
 | 
						|
 | 
						|
    const html = render('hotspot_overlay', args);
 | 
						|
 | 
						|
    assert.equal($(html).attr('id'), 'hotspot_intro_compose_overlay');
 | 
						|
    assert.equal($(html).find('.hotspot-title').text(), 'Start a new conversation');
 | 
						|
    assert.equal(
 | 
						|
        $(html).find('.hotspot-description').text(),
 | 
						|
        'Click the "New topic" button to start a new conversation.'
 | 
						|
    );
 | 
						|
});
 | 
						|
 | 
						|
run_test('input_pill', () => {
 | 
						|
    const args = {
 | 
						|
        id: 22,
 | 
						|
        display_value: 'King Hamlet',
 | 
						|
    };
 | 
						|
 | 
						|
    const html = render('input_pill', args);
 | 
						|
 | 
						|
    assert($(html).hasClass('pill'));
 | 
						|
});
 | 
						|
 | 
						|
run_test('intro_reply_hotspot', () => {
 | 
						|
    const html = render('intro_reply_hotspot', {});
 | 
						|
 | 
						|
    assert($(html).hasClass('hotspot-message'));
 | 
						|
});
 | 
						|
 | 
						|
run_test('invite_subscription', () => {
 | 
						|
    const args = {
 | 
						|
        streams: [
 | 
						|
            {
 | 
						|
                name: "devel",
 | 
						|
            },
 | 
						|
            {
 | 
						|
                name: "social",
 | 
						|
            },
 | 
						|
        ],
 | 
						|
    };
 | 
						|
    const html = render('invite_subscription', args);
 | 
						|
 | 
						|
    const input = $(html).find("label").first();
 | 
						|
    assert.equal(input.text().trim(), "#devel");
 | 
						|
});
 | 
						|
 | 
						|
run_test('single_message', () => {
 | 
						|
    const message =  {
 | 
						|
        msg: {
 | 
						|
            include_recipient: true,
 | 
						|
            display_recipient: 'devel',
 | 
						|
            is_stream: true,
 | 
						|
            content: 'This is message one.',
 | 
						|
            last_edit_timestr: '11:00',
 | 
						|
            starred: true,
 | 
						|
            starred_status: "Unstar",
 | 
						|
        },
 | 
						|
    };
 | 
						|
 | 
						|
    let html = render('single_message', message);
 | 
						|
    html = '<div class="message_table focused_table" id="zfilt">' + html + '</div>';
 | 
						|
 | 
						|
    const first_message = $(html).find("div.messagebox").first();
 | 
						|
 | 
						|
    const first_message_text = first_message.find(".message_content").text().trim();
 | 
						|
    assert.equal(first_message_text, "This is message one.");
 | 
						|
 | 
						|
    const starred_title = first_message.find(".star").attr("title");
 | 
						|
    assert.equal(starred_title, "translated: Unstar this message (*)");
 | 
						|
});
 | 
						|
 | 
						|
run_test('message_edit_form', () => {
 | 
						|
    const args = {
 | 
						|
        topic: "lunch",
 | 
						|
        content: "Let's go to lunch!",
 | 
						|
        is_stream: true,
 | 
						|
    };
 | 
						|
    const html = render('message_edit_form', args);
 | 
						|
 | 
						|
    const textarea = $(html).find("textarea.message_edit_content");
 | 
						|
    assert.equal(textarea.text(), "Let's go to lunch!");
 | 
						|
});
 | 
						|
 | 
						|
run_test('message_group', () => {
 | 
						|
    const messages = [
 | 
						|
        {
 | 
						|
            msg: {
 | 
						|
                id: 1,
 | 
						|
                match_content: 'This is message one.',
 | 
						|
                starred: true,
 | 
						|
                is_stream: true,
 | 
						|
                content: 'This is message one.',
 | 
						|
            },
 | 
						|
            include_recipient: true,
 | 
						|
            display_recipient: 'devel',
 | 
						|
            last_edit_timestr: '11:00',
 | 
						|
        },
 | 
						|
        {
 | 
						|
            msg: {
 | 
						|
                content: 'This is message two.',
 | 
						|
                match_content: 'This is message <span class="highlight">two</span>.\n<pre>code\nblock</pre>',
 | 
						|
                is_stream: true,
 | 
						|
                unread: true,
 | 
						|
                id: 2,
 | 
						|
            },
 | 
						|
        },
 | 
						|
    ];
 | 
						|
 | 
						|
    const groups = [
 | 
						|
        {
 | 
						|
            display_recipient: "support",
 | 
						|
            is_stream: true,
 | 
						|
            message_ids: [1, 2],
 | 
						|
            message_containers: messages,
 | 
						|
            group_date_divider_html: '"<span class="timerender82">Jan 07</span>"',
 | 
						|
            show_group_date_divider: true,
 | 
						|
            match_topic: '<span class="highlight">two</span> messages',
 | 
						|
        },
 | 
						|
    ];
 | 
						|
 | 
						|
    render('loader');
 | 
						|
    const html = render('message_group', {message_groups: groups, use_match_properties: true});
 | 
						|
 | 
						|
    const first_message_text = $(html).next('.recipient_row').find('div.messagebox').first().find('.message_content').text().trim();
 | 
						|
    assert.equal(first_message_text, "This is message one.");
 | 
						|
 | 
						|
    const last_message_html = $(html).next('.recipient_row').find('div.messagebox').last().find('.message_content').html().trim();
 | 
						|
    assert.equal(last_message_html, 'This is message <span class="highlight">two</span>.\n<pre>code\nblock</pre>');
 | 
						|
 | 
						|
    const highlighted_topic_word = $(html).find('a.narrows_by_topic .highlight').text();
 | 
						|
    assert.equal(highlighted_topic_word, 'two');
 | 
						|
});
 | 
						|
 | 
						|
run_test('message_edit_history', () => {
 | 
						|
    const message = {
 | 
						|
        content: "Let's go to lunch!",
 | 
						|
        edit_history: [
 | 
						|
            {
 | 
						|
                body_to_render: "<p>Let's go to " +
 | 
						|
                                    "<span class='highlight_text_deleted'>lunch</span>" +
 | 
						|
                                    "<span class='highlight_text_inserted'>dinner</span>" +
 | 
						|
                                "!</p>",
 | 
						|
                timestamp: 1468132659,
 | 
						|
                edited_by: 'Alice',
 | 
						|
                posted_or_edited: "Edited by",
 | 
						|
            },
 | 
						|
        ],
 | 
						|
    };
 | 
						|
    const html = "<div>" + render('message_edit_history', {
 | 
						|
        edited_messages: message.edit_history,
 | 
						|
    }) + "</div>";
 | 
						|
    const edited_message = $(html).find("div.messagebox-content");
 | 
						|
    assert.equal(edited_message.text().trim(),
 | 
						|
                 "1468132659\n        Let\'s go to lunchdinner!\n        Edited by Alice");
 | 
						|
});
 | 
						|
 | 
						|
run_test('message_and_topic_edit_history', () => {
 | 
						|
    const message = {
 | 
						|
        content: "Let's go to lunch!",
 | 
						|
        edit_history: [
 | 
						|
            {
 | 
						|
                body_to_render: "<p>Let's go to " +
 | 
						|
                                    "<span class='highlight_text_deleted'>lunch</span>" +
 | 
						|
                                    "<span class='highlight_text_inserted'>dinner</span>" +
 | 
						|
                                "!</p>",
 | 
						|
                new_topic: 'Lunch',
 | 
						|
                prev_topic: 'Dinner',
 | 
						|
                topic_edited: true,
 | 
						|
                timestamp: 1468132659,
 | 
						|
                edited_by: 'Alice',
 | 
						|
                posted_or_edited: "Edited by",
 | 
						|
            },
 | 
						|
        ],
 | 
						|
    };
 | 
						|
    const html = "<div>" + render('message_edit_history', {
 | 
						|
        edited_messages: message.edit_history,
 | 
						|
    }) + "</div>";
 | 
						|
    const edited_message = $(html).find("div.messagebox-content");
 | 
						|
    assert.equal(edited_message.text().trim(),
 | 
						|
                 "1468132659\n        Topic: Lunch Dinner\n        Let\'s go to lunchdinner!\n        Edited by Alice");
 | 
						|
});
 | 
						|
 | 
						|
run_test('topic_edit_history', () => {
 | 
						|
    const message = {
 | 
						|
        content: "Let's go to lunch!",
 | 
						|
        edit_history: [
 | 
						|
            {
 | 
						|
                prev_topic: 'Dinner',
 | 
						|
                new_topic: 'Lunch',
 | 
						|
                topic_edited: true,
 | 
						|
                timestamp: 1468132659,
 | 
						|
                edited_by: 'Alice',
 | 
						|
                posted_or_edited: "Topic edited by",
 | 
						|
            },
 | 
						|
        ],
 | 
						|
    };
 | 
						|
    const html = "<div>" + render('message_edit_history', {
 | 
						|
        edited_messages: message.edit_history,
 | 
						|
    }) + "</div>";
 | 
						|
    const edited_message = $(html).find("div.messagebox-content");
 | 
						|
    assert.equal(edited_message.text().trim(),
 | 
						|
                 "1468132659\n        Topic: Lunch Dinner\n        Topic edited by Alice");
 | 
						|
});
 | 
						|
 | 
						|
run_test('message_reaction', () => {
 | 
						|
    const args = {
 | 
						|
        class: 'message_reaction',
 | 
						|
        emoji_name: 'smile',
 | 
						|
        emoji_code: '1f604',
 | 
						|
        local_id: 'unicode_emoji,smile,1f604',
 | 
						|
        message_id: '1',
 | 
						|
    };
 | 
						|
 | 
						|
    let html = '';
 | 
						|
    html += '<div>';
 | 
						|
    html += render('message_reaction', args);
 | 
						|
    html += '</div>';
 | 
						|
 | 
						|
    const reaction = $(html).find(".message_reaction");
 | 
						|
    assert.equal(reaction.data("reaction-id"), "unicode_emoji,smile,1f604");
 | 
						|
    assert(reaction.find(".emoji").hasClass("emoji-1f604"));
 | 
						|
});
 | 
						|
 | 
						|
run_test('more_topics', () => {
 | 
						|
    const html = render('more_topics');
 | 
						|
 | 
						|
    assert($(html).hasClass('show-more-topics'));
 | 
						|
});
 | 
						|
 | 
						|
run_test('new_stream_users', () => {
 | 
						|
    const args = {
 | 
						|
        users: [
 | 
						|
            {
 | 
						|
                email: 'lear@zulip.com',
 | 
						|
                full_name: 'King Lear',
 | 
						|
            },
 | 
						|
            {
 | 
						|
                email: 'othello@zulip.com',
 | 
						|
                full_name: 'Othello the Moor',
 | 
						|
            },
 | 
						|
        ],
 | 
						|
    };
 | 
						|
 | 
						|
    const html = render('new_stream_users', args);
 | 
						|
 | 
						|
    const label = $(html).find("label").first();
 | 
						|
    assert.equal(label.text().trim(), 'King Lear (lear@zulip.com)');
 | 
						|
});
 | 
						|
 | 
						|
run_test('non_editable_user_group', () => {
 | 
						|
    const args = {
 | 
						|
        user_group: {
 | 
						|
            id: "9",
 | 
						|
            name: "uranohoshi",
 | 
						|
            description: "Students at Uranohoshi Academy",
 | 
						|
        },
 | 
						|
    };
 | 
						|
 | 
						|
    let html = '';
 | 
						|
    html += '<div id="user-groups">';
 | 
						|
    html += render('non_editable_user_group', args);
 | 
						|
    html += '</div>';
 | 
						|
 | 
						|
    const group_id = $(html).find('.user-group').first().prop('id');
 | 
						|
    const group_pills_id = $(html).find('.user-group').first().find('.pill-container').attr('data-group-pills');
 | 
						|
    const group_name_display = $(html).find('.user-group').first().find('.name').text().trim().replace(/\s+/g, ' ');
 | 
						|
    const group_description = $(html).find('.user-group').first().find('.description').text().trim().replace(/\s+/g, ' ');
 | 
						|
 | 
						|
    assert.equal(group_id, '9');
 | 
						|
    assert.equal(group_pills_id, '9');
 | 
						|
    assert.equal(group_name_display, 'uranohoshi');
 | 
						|
    assert.equal(group_description, 'Students at Uranohoshi Academy');
 | 
						|
});
 | 
						|
 | 
						|
run_test('notification', () => {
 | 
						|
    const args = {
 | 
						|
        content: "Hello",
 | 
						|
        gravatar_url: "/gravatar/url",
 | 
						|
        title: "You have a notification",
 | 
						|
    };
 | 
						|
 | 
						|
    const html = render('notification', args);
 | 
						|
 | 
						|
    const title = $(html).find(".title");
 | 
						|
    assert.equal(title.text().trim(), 'You have a notification');
 | 
						|
});
 | 
						|
 | 
						|
run_test('reminder_popover_content', () => {
 | 
						|
    const args = {
 | 
						|
        message: {
 | 
						|
            is_stream: true,
 | 
						|
            id: "420",
 | 
						|
            stream: "devel",
 | 
						|
            sender_full_name: "Iago",
 | 
						|
        },
 | 
						|
        can_edit_message: true,
 | 
						|
        can_mute_topic: true,
 | 
						|
        narrowed: true,
 | 
						|
    };
 | 
						|
 | 
						|
    let html = '<div style="height: 250px">';
 | 
						|
    html += render('remind_me_popover_content', args);
 | 
						|
    html += "</div>";
 | 
						|
    const link = $(html).find("a.remind.custom");
 | 
						|
    assert.equal(link.text().trim(), 'translated: Select date and time');
 | 
						|
});
 | 
						|
 | 
						|
run_test('revoke_invite_modal', () => {
 | 
						|
    const args = {
 | 
						|
        is_multiuse: false,
 | 
						|
        email: "iago@zulip.com",
 | 
						|
    };
 | 
						|
 | 
						|
    let html = "<div>";
 | 
						|
    html += render('settings/revoke_invite_modal', args);
 | 
						|
    html += "</div>";
 | 
						|
    assert.equal($(html).find("p strong").text(), "iago@zulip.com");
 | 
						|
});
 | 
						|
 | 
						|
run_test('settings_tab', () => {
 | 
						|
    const page_param_checkbox_options = {
 | 
						|
        enable_stream_desktop_notifications: true,
 | 
						|
        enable_stream_push_notifications: true,
 | 
						|
        enable_stream_audible_notifications: true,
 | 
						|
        enable_desktop_notifications: true,
 | 
						|
        enable_sounds: true,
 | 
						|
        enable_offline_email_notifications: true,
 | 
						|
        enable_offline_push_notifications: true,
 | 
						|
        enable_online_push_notifications: true,
 | 
						|
        enable_digest_emails: true,
 | 
						|
        realm_digest_emails_enabled: true,
 | 
						|
        realm_name_in_notifications: true,
 | 
						|
        realm_push_notifications_enabled: true,
 | 
						|
        wildcard_mentions_notify: true,
 | 
						|
    };
 | 
						|
    const page_params = $.extend(page_param_checkbox_options, {
 | 
						|
        full_name: "Alyssa P. Hacker", password_auth_enabled: true,
 | 
						|
        avatar_url: "https://google.com",
 | 
						|
    });
 | 
						|
 | 
						|
    const checkbox_ids = [
 | 
						|
        "enable_stream_desktop_notifications",
 | 
						|
        "enable_stream_push_notifications",
 | 
						|
        "enable_stream_audible_notifications",
 | 
						|
        "enable_desktop_notifications",
 | 
						|
        "enable_sounds",
 | 
						|
        "enable_offline_push_notifications",
 | 
						|
        "enable_online_push_notifications",
 | 
						|
        "enable_digest_emails",
 | 
						|
        "realm_name_in_notifications",
 | 
						|
        "wildcard_mentions_notify",
 | 
						|
    ];
 | 
						|
 | 
						|
    // Render with all booleans set to true.
 | 
						|
    let html = render('settings_tab', {
 | 
						|
        page_params: page_params,
 | 
						|
        notification_settings: settings_notifications.all_notifications.settings,
 | 
						|
    });
 | 
						|
 | 
						|
    // All checkboxes should be checked.
 | 
						|
    _.each(checkbox_ids, function (checkbox) {
 | 
						|
        assert.equal($(html).find("#" + checkbox).is(":checked"), true);
 | 
						|
    });
 | 
						|
 | 
						|
    // Re-render with checkbox booleans set to false.
 | 
						|
    _.each(page_param_checkbox_options, function (value, option) {
 | 
						|
        page_params[option] = false;
 | 
						|
    });
 | 
						|
 | 
						|
    html = render('settings_tab', {page_params: page_params});
 | 
						|
 | 
						|
    // All checkboxes should be unchecked.
 | 
						|
    _.each(checkbox_ids, function (checkbox) {
 | 
						|
        assert.equal($(html).find("#" + checkbox).is(":checked"), false);
 | 
						|
    });
 | 
						|
 | 
						|
    // Check if enable_desktop_notifications setting disables subsetting too.
 | 
						|
    const parent_elem = $('#pm_content_in_desktop_notifications_label').wrap("<div></div>");
 | 
						|
 | 
						|
    $('#enable_desktop_notifications').prop('checked', false).triggerHandler('change');
 | 
						|
    $('#enable_desktop_notifications').change(function () {
 | 
						|
        assert(parent_elem.hasClass('control-label-disabled'));
 | 
						|
        assert.equal($('#pm_content_in_desktop_notifications').attr('disabled'), 'disabled');
 | 
						|
    });
 | 
						|
 | 
						|
    $('#enable_desktop_notifications').prop('checked', true).triggerHandler('change');
 | 
						|
    $('#enable_desktop_notifications').change(function () {
 | 
						|
        assert(!parent_elem.hasClass('control-label-disabled'));
 | 
						|
        assert.equal($('#pm_content_in_desktop_notifications').attr('disabled'), undefined);
 | 
						|
    });
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
run_test('stream_member_list_entry', () => {
 | 
						|
    const everyone_items = ["subscriber-name", "subscriber-email"];
 | 
						|
    const admin_items = ["remove-subscriber-button"];
 | 
						|
 | 
						|
    // First, as non-admin.
 | 
						|
    let html = render('stream_member_list_entry',
 | 
						|
                      {name: "King Hamlet", email: "hamlet@zulip.com"});
 | 
						|
    _.each(everyone_items, function (item) {
 | 
						|
        assert.equal($(html).find("." + item).length, 1);
 | 
						|
    });
 | 
						|
    _.each(admin_items, function (item) {
 | 
						|
        assert.equal($(html).find("." + item).length, 0);
 | 
						|
    });
 | 
						|
 | 
						|
    // Now, as admin.
 | 
						|
    html = render('stream_member_list_entry',
 | 
						|
                  {name: "King Hamlet", email: "hamlet@zulip.com",
 | 
						|
                   displaying_for_admin: true});
 | 
						|
    _.each(everyone_items, function (item) {
 | 
						|
        assert.equal($(html).find("." + item).length, 1);
 | 
						|
    });
 | 
						|
    _.each(admin_items, function (item) {
 | 
						|
        assert.equal($(html).find("." + item).length, 1);
 | 
						|
    });
 | 
						|
});
 | 
						|
 | 
						|
run_test('stream_sidebar_actions', () => {
 | 
						|
    const args = {
 | 
						|
        stream: {
 | 
						|
            color: 'red',
 | 
						|
            name: 'devel',
 | 
						|
            is_muted: false,
 | 
						|
            id: 55,
 | 
						|
        },
 | 
						|
    };
 | 
						|
 | 
						|
    const html = render('stream_sidebar_actions', args);
 | 
						|
 | 
						|
    const li = $(html).find("li").first();
 | 
						|
    assert.equal(li.text().trim(), 'translated: Stream settings');
 | 
						|
});
 | 
						|
 | 
						|
run_test('stream_sidebar_row', () => {
 | 
						|
    const args = {
 | 
						|
        name: "devel",
 | 
						|
        color: "red",
 | 
						|
        dark_background: "maroon",
 | 
						|
        uri: "/devel/uri",
 | 
						|
        id: 999,
 | 
						|
    };
 | 
						|
 | 
						|
    let html = '<ul id="stream_filters">';
 | 
						|
    html += render('stream_sidebar_row', args);
 | 
						|
    html += '</ul>';
 | 
						|
 | 
						|
    const swatch = $(html).find(".stream-privacy");
 | 
						|
    assert.equal(swatch.attr('id'), 'stream_sidebar_privacy_swatch_999');
 | 
						|
 | 
						|
    // test to ensure that the hashtag element from stream_privacy exists.
 | 
						|
    assert.equal($(html).find(".stream-privacy").children("*").attr("class"), "hashtag");
 | 
						|
});
 | 
						|
 | 
						|
run_test('subscription_invites_warning_modal', () => {
 | 
						|
    const html = render('subscription_invites_warning_modal');
 | 
						|
 | 
						|
    const button = $(html).find(".close-invites-warning-modal").last();
 | 
						|
    assert.equal(button.text(), 'translated: Go back');
 | 
						|
});
 | 
						|
 | 
						|
run_test('subscription_settings', () => {
 | 
						|
    const sub = {
 | 
						|
        name: 'devel',
 | 
						|
        subscribed: true,
 | 
						|
        notifications: true,
 | 
						|
        is_admin: true,
 | 
						|
        render_subscribers: true,
 | 
						|
        color: 'purple',
 | 
						|
        invite_only: true,
 | 
						|
        can_change_stream_permissions: true,
 | 
						|
        email_address: 'xxxxxxxxxxxxxxx@zulip.com',
 | 
						|
        stream_id: 888,
 | 
						|
        is_muted: false,
 | 
						|
    };
 | 
						|
 | 
						|
    let html = '';
 | 
						|
    page_params.realm_push_notifications_enabled = false;
 | 
						|
    const check_realm_setting = {
 | 
						|
        push_notifications: !page_params.realm_push_notifications_enabled,
 | 
						|
    };
 | 
						|
 | 
						|
    html += render('subscription_settings', {
 | 
						|
        sub: sub,
 | 
						|
        settings: stream_edit.stream_settings(sub),
 | 
						|
        realm_settings: check_realm_setting,
 | 
						|
    });
 | 
						|
 | 
						|
    const div = $(html).find(".subscription-type");
 | 
						|
    assert(div.text().indexOf('private stream') > 0);
 | 
						|
 | 
						|
    const anchor = $(html).find(".change-stream-privacy").first();
 | 
						|
    assert.equal(anchor.text(), "[translated: Change]");
 | 
						|
});
 | 
						|
 | 
						|
 | 
						|
run_test('subscription_stream_privacy_modal', () => {
 | 
						|
    const args = {
 | 
						|
        stream_id: 999,
 | 
						|
        is_private: true,
 | 
						|
        is_admin: true,
 | 
						|
    };
 | 
						|
    const html = render('subscription_stream_privacy_modal', args);
 | 
						|
 | 
						|
    const other_options = $(html).find("input[name=privacy]");
 | 
						|
    assert.equal(other_options[0].value, 'public');
 | 
						|
    assert.equal(other_options[1].value, 'invite-only-public-history');
 | 
						|
    assert.equal(other_options[2].value, 'invite-only');
 | 
						|
 | 
						|
    const is_announcement_only = $(html).find("input[name=is-announcement-only]");
 | 
						|
    assert.equal(is_announcement_only.prop('checked'), false);
 | 
						|
 | 
						|
    const button = $(html).find("#change-stream-privacy-button");
 | 
						|
    assert(button.hasClass("btn-danger"));
 | 
						|
    assert.equal(button.text().trim(), "translated: Save changes");
 | 
						|
});
 | 
						|
 | 
						|
 | 
						|
run_test('subscription_table_body', () => {
 | 
						|
    // We are mostly deprecating template tests,
 | 
						|
    // but we try to make sure rendering does not
 | 
						|
    // crash.
 | 
						|
    render('subscription_table_body', {});
 | 
						|
});
 | 
						|
 | 
						|
 | 
						|
run_test('subscriptions', () => {
 | 
						|
    const args = {
 | 
						|
        subscriptions: [
 | 
						|
            {
 | 
						|
                name: 'devel',
 | 
						|
                subscribed: true,
 | 
						|
                notifications: true,
 | 
						|
                is_admin: true,
 | 
						|
                render_subscribers: true,
 | 
						|
                color: 'purple',
 | 
						|
                invite_only: true,
 | 
						|
                email_address: 'xxxxxxxxxxxxxxx@zulip.com',
 | 
						|
                stream_id: 888,
 | 
						|
                is_muted: false,
 | 
						|
            },
 | 
						|
            {
 | 
						|
                name: 'social',
 | 
						|
                color: 'green',
 | 
						|
                stream_id: 999,
 | 
						|
            },
 | 
						|
        ],
 | 
						|
    };
 | 
						|
 | 
						|
    let html = '';
 | 
						|
    html += '<div>';
 | 
						|
    html += render('subscriptions', args);
 | 
						|
    html += '</div>';
 | 
						|
 | 
						|
    const span = $(html).find(".stream-name").first();
 | 
						|
    assert.equal(span.text(), 'devel');
 | 
						|
});
 | 
						|
 | 
						|
 | 
						|
run_test('tab_bar', () => {
 | 
						|
    const args = {
 | 
						|
        tabs: [
 | 
						|
            {
 | 
						|
                cls: 'root',
 | 
						|
                title: 'Home',
 | 
						|
                hash: '#',
 | 
						|
                data: 'home',
 | 
						|
            },
 | 
						|
            {
 | 
						|
                cls: 'stream',
 | 
						|
                title: 'Devel',
 | 
						|
                hash: '/stream/uri',
 | 
						|
                data: 'devel',
 | 
						|
            },
 | 
						|
        ],
 | 
						|
    };
 | 
						|
 | 
						|
    const html = render('tab_bar', args);
 | 
						|
 | 
						|
    const a = $(html).find("li").first();
 | 
						|
    assert.equal(a.text().trim(), 'Home');
 | 
						|
});
 | 
						|
 | 
						|
run_test('topic_edit_form', () => {
 | 
						|
    const html = render('topic_edit_form');
 | 
						|
 | 
						|
    const button = $(html).find("button").first();
 | 
						|
    assert.equal(button.find("i").attr("class"), 'fa fa-check');
 | 
						|
});
 | 
						|
 | 
						|
run_test('topic_list_item', () => {
 | 
						|
    const args = {
 | 
						|
        is_muted: false,
 | 
						|
        topic_name: 'lunch',
 | 
						|
        url: '/lunch/url',
 | 
						|
        unread: 5,
 | 
						|
    };
 | 
						|
 | 
						|
    const html = render('topic_list_item', args);
 | 
						|
 | 
						|
    assert.equal($(html).attr('data-topic-name'), 'lunch');
 | 
						|
});
 | 
						|
 | 
						|
 | 
						|
run_test('topic_sidebar_actions', () => {
 | 
						|
    let args = {
 | 
						|
        stream_name: 'social',
 | 
						|
        topic_name: 'lunch',
 | 
						|
        can_mute_topic: true,
 | 
						|
        is_admin: false,
 | 
						|
    };
 | 
						|
    let html = render('topic_sidebar_actions', args);
 | 
						|
 | 
						|
    const a = $(html).find("a.narrow_to_topic");
 | 
						|
    assert.equal(a.text().trim(), 'translated: Narrow to topic lunch');
 | 
						|
 | 
						|
    let delete_topic_option = $(html).find("a.sidebar-popover-delete-topic-messages");
 | 
						|
    assert.equal(delete_topic_option.length, 0);
 | 
						|
 | 
						|
    args = {
 | 
						|
        is_admin: true,
 | 
						|
    };
 | 
						|
    html = render('topic_sidebar_actions', args);
 | 
						|
 | 
						|
    delete_topic_option = $(html).find("a.sidebar-popover-delete-topic-messages");
 | 
						|
    assert.equal(delete_topic_option.length, 1);
 | 
						|
});
 | 
						|
 | 
						|
run_test('delete_topic_modal', () => {
 | 
						|
    const args = {
 | 
						|
        topic_name: 'lunch',
 | 
						|
    };
 | 
						|
    const html = render('delete_topic_modal', args);
 | 
						|
 | 
						|
    const modal_body = $(html).find('.modal-body');
 | 
						|
    assert(modal_body.text().indexOf('delete all messages in lunch?') > 0);
 | 
						|
});
 | 
						|
 | 
						|
run_test('typeahead_list_item', () => {
 | 
						|
    const args = {
 | 
						|
        primary: 'primary-text',
 | 
						|
        secondary: 'secondary-text',
 | 
						|
        img_src: 'https://zulip.org',
 | 
						|
        is_emoji: true,
 | 
						|
        has_image: true,
 | 
						|
        has_secondary: true,
 | 
						|
    };
 | 
						|
 | 
						|
    const html = '<div>' + render('typeahead_list_item', args) + '</div>';
 | 
						|
 | 
						|
    assert.equal($(html).find('.emoji').attr('src'), 'https://zulip.org');
 | 
						|
    assert.equal($(html).find('strong').text().trim(), 'primary-text');
 | 
						|
    assert.equal($(html).find('small').text().trim(), 'secondary-text');
 | 
						|
});
 | 
						|
 | 
						|
run_test('typing_notifications', () => {
 | 
						|
    const args = {
 | 
						|
        users: [{
 | 
						|
            full_name: 'Hamlet',
 | 
						|
            email: 'hamlet@zulip.com',
 | 
						|
        }],
 | 
						|
    };
 | 
						|
 | 
						|
    let html = '';
 | 
						|
    html += '<ul>';
 | 
						|
    html += render('typing_notifications', args);
 | 
						|
    html += '</ul>';
 | 
						|
 | 
						|
    const li = $(html).find('li').first();
 | 
						|
    assert.equal(li.text(), 'Hamlet is typing...');
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
run_test('upload_space_stats', () => {
 | 
						|
    let args = {
 | 
						|
        show_upgrade_message: true,
 | 
						|
        percent_used: 50,
 | 
						|
        upload_quota: "1 GB",
 | 
						|
    };
 | 
						|
    let html = render('settings/upload_space_stats', args);
 | 
						|
    assert.equal($(html).text().trim(), "translated: Organization using 50% of 1 GB.\n    translated: Upgrade for more space.");
 | 
						|
 | 
						|
    args = {
 | 
						|
        show_upgrade_message: false,
 | 
						|
        percent_used: 10,
 | 
						|
        upload_quota: "5 GB",
 | 
						|
    };
 | 
						|
    html = render('settings/upload_space_stats', args);
 | 
						|
    assert.equal($(html).text().trim(), "translated: Organization using 10% of 5 GB.");
 | 
						|
});
 | 
						|
 | 
						|
run_test('user_group_info_popover', () => {
 | 
						|
    const html = render('user_group_info_popover');
 | 
						|
 | 
						|
    $(html).hasClass('popover message-info-popover group-info-popover');
 | 
						|
});
 | 
						|
 | 
						|
run_test('user_group_info_popover_content', () => {
 | 
						|
    const args = {
 | 
						|
        group_name: 'groupName',
 | 
						|
        group_description: 'groupDescription',
 | 
						|
        members: [
 | 
						|
            {
 | 
						|
                full_name: 'Active Alice',
 | 
						|
                user_last_seen_time_status: 'time',
 | 
						|
                is_bot: false,
 | 
						|
            },
 | 
						|
            {
 | 
						|
                full_name: 'Bot Bob',
 | 
						|
                user_last_seen_time_status: 'time',
 | 
						|
                is_bot: true,
 | 
						|
            },
 | 
						|
            {
 | 
						|
                full_name: 'Inactive Imogen',
 | 
						|
                user_last_seen_time_status: 'time',
 | 
						|
                is_bot: false,
 | 
						|
            },
 | 
						|
        ],
 | 
						|
    };
 | 
						|
 | 
						|
    const html = render('user_group_info_popover_content', args);
 | 
						|
 | 
						|
    const allUsers = $(html).find("li");
 | 
						|
    assert.equal($(allUsers[0]).text().trim(), 'Active Alice');
 | 
						|
    assert.equal($(allUsers[1]).text().trim(), 'Bot Bob');
 | 
						|
    assert.equal($(allUsers[2]).text().trim(), 'Inactive Imogen');
 | 
						|
 | 
						|
    assert.equal($(html).find('.group-name').text().trim(), 'groupName');
 | 
						|
    assert.equal($(html).find('.group-description').text().trim(), 'groupDescription');
 | 
						|
});
 | 
						|
 | 
						|
run_test('no_arrow_popover', () => {
 | 
						|
    const html = render('no_arrow_popover', {class: 'message-info-popover'});
 | 
						|
 | 
						|
    $(html).hasClass('popover message-info-popover');
 | 
						|
});
 | 
						|
 | 
						|
run_test('user_info_popover_content', () => {
 | 
						|
    const args = {
 | 
						|
        message: {
 | 
						|
            full_date_str: 'Monday',
 | 
						|
            full_time_str: '12:00',
 | 
						|
            user_full_name: 'Alice Smith',
 | 
						|
            user_email: 'alice@zulip.com',
 | 
						|
        },
 | 
						|
        sent_by_uri: '/sent_by/uri',
 | 
						|
        pm_with_uri: '/pm_with/uri',
 | 
						|
        private_message_class: 'compose_private_message',
 | 
						|
    };
 | 
						|
 | 
						|
    const html = render('user_info_popover_content', args);
 | 
						|
 | 
						|
    const a = $(html).find("a.narrow_to_private_messages");
 | 
						|
    assert.equal(a.text().trim(), 'translated: View private messages');
 | 
						|
});
 | 
						|
 | 
						|
run_test('user_info_popover_title', () => {
 | 
						|
    let html = render('user_info_popover_title', {user_avatar: 'avatar/hamlet@zulip.com'});
 | 
						|
 | 
						|
    html = '<div>' + html + '</div>';
 | 
						|
    assert.equal($(html).find('.popover-avatar').css('background-image'), "url(avatar/hamlet@zulip.com)");
 | 
						|
});
 | 
						|
 | 
						|
run_test('uploaded_files_list_popover', () => {
 | 
						|
    const args = {
 | 
						|
        attachment: {
 | 
						|
            name: "file_name.txt",
 | 
						|
            create_time: "Apr 12 04:18 AM",
 | 
						|
            messages: [
 | 
						|
                {
 | 
						|
                    id: "1",
 | 
						|
                },
 | 
						|
                {
 | 
						|
                    id: "2",
 | 
						|
                },
 | 
						|
            ],
 | 
						|
            size: 1234,
 | 
						|
            path_id: "2/65/6wITdgsd63hdskjuFqEeEy7_r/file_name.txt",
 | 
						|
        },
 | 
						|
    };
 | 
						|
 | 
						|
    const html = render('uploaded_files_list', args);
 | 
						|
    assert.equal($(html).find('.ind-message').attr("href"), "/#narrow/id/1");
 | 
						|
    assert.equal($(html).find('#download_attachment').attr("href"),
 | 
						|
                 "/user_uploads/2/65/6wITdgsd63hdskjuFqEeEy7_r/file_name.txt");
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
run_test('user_presence_rows', () => {
 | 
						|
    const args = {
 | 
						|
        users: [
 | 
						|
            {
 | 
						|
                type_desc: "Active",
 | 
						|
                type: "active",
 | 
						|
                num_unread: 0,
 | 
						|
                email: "lear@zulip.com",
 | 
						|
                name: "King Lear",
 | 
						|
            },
 | 
						|
            {
 | 
						|
                type_desc: "Away",
 | 
						|
                type: "away",
 | 
						|
                num_unread: 5,
 | 
						|
                email: "othello@zulip.com",
 | 
						|
                name: "Othello",
 | 
						|
            },
 | 
						|
        ],
 | 
						|
    };
 | 
						|
 | 
						|
    let html = '';
 | 
						|
    html += '<ul class="filters">';
 | 
						|
    html += render('user_presence_rows', args);
 | 
						|
    html += '</ul>';
 | 
						|
 | 
						|
    const a = $(html).find("a").first();
 | 
						|
    assert.equal(a.text().trim(), 'King Lear');
 | 
						|
});
 | 
						|
 | 
						|
run_test('buddy_list_tooltip_content', () => {
 | 
						|
    const args = {
 | 
						|
        first_line: 'Iago',
 | 
						|
        second_line: 'out to lunch',
 | 
						|
        third_line: 'Active now',
 | 
						|
    };
 | 
						|
 | 
						|
    const html = render('buddy_list_tooltip_content', args);
 | 
						|
    const tooltip_content = $(html).find(".tooltip_inner_content");
 | 
						|
    assert.equal(tooltip_content.text().trim(), 'Iagoout to lunchActive now');
 | 
						|
});
 | 
						|
 | 
						|
run_test('user_profile_modal', () => {
 | 
						|
    let args = {
 | 
						|
        full_name: "Iago",
 | 
						|
        email: "iago@zulip.com",
 | 
						|
        profile_data: {
 | 
						|
            author: "Shakespeare",
 | 
						|
            book: "Othello",
 | 
						|
        },
 | 
						|
        show_email: true,
 | 
						|
    };
 | 
						|
 | 
						|
    let html = render('user_profile_modal', args);
 | 
						|
    let div = $(html).find("#email .value");
 | 
						|
    assert.equal(div.text().trim(), 'iago@zulip.com');
 | 
						|
 | 
						|
    args = {
 | 
						|
        full_name: "Hamlet",
 | 
						|
        email: "hamlet@zulip.com",
 | 
						|
        profile_data: {
 | 
						|
            author: "Hamlet",
 | 
						|
            book: "Othello",
 | 
						|
        },
 | 
						|
        show_email: false,
 | 
						|
    };
 | 
						|
 | 
						|
    html = render('user_profile_modal', args);
 | 
						|
    div = $(html).find("#email .value");
 | 
						|
    assert.equal(div.text().trim(), '');
 | 
						|
});
 | 
						|
 | 
						|
run_test('muted_topic_ui_row', () => {
 | 
						|
    const args = {
 | 
						|
        stream: 'Verona',
 | 
						|
        stream_id: 99,
 | 
						|
        topic: 'pizza',
 | 
						|
    };
 | 
						|
 | 
						|
    let html = '<table id="muted-topics-table">';
 | 
						|
    html += '<tbody>';
 | 
						|
    html += render('muted_topic_ui_row', args);
 | 
						|
    html += '</tbody>';
 | 
						|
    html += '</table>';
 | 
						|
 | 
						|
    assert.equal($(html).find("tr").attr("data-stream-id"), 99);
 | 
						|
    assert.equal($(html).find("tr").attr("data-topic"), "pizza");
 | 
						|
});
 | 
						|
 | 
						|
run_test('embedded_bot_config_item', () => {
 | 
						|
    const args = {
 | 
						|
        botname: 'giphy',
 | 
						|
        key: 'api_key',
 | 
						|
        value: '12345678',
 | 
						|
    };
 | 
						|
    const html = render('embedded_bot_config_item', args);
 | 
						|
    assert.equal($(html).attr('name'), args.botname);
 | 
						|
    assert.equal($(html).attr('id'), args.botname + '_' + args.key);
 | 
						|
    assert.equal($(html).find('label').text(), args.key);
 | 
						|
    assert.equal($(html).find('input').attr('placeholder'), args.value);
 | 
						|
});
 | 
						|
 | 
						|
run_test('edit_bot', () => {
 | 
						|
    render('edit_bot');
 | 
						|
});
 | 
						|
 | 
						|
run_test('edit_outgoing_webhook_service', () => {
 | 
						|
    const args = {
 | 
						|
        service: {base_url: "http://www.foo.bar",
 | 
						|
                  interface: "1"},
 | 
						|
    };
 | 
						|
    const html = render('settings/edit_outgoing_webhook_service', args);
 | 
						|
    assert.equal($(html).find('#edit_service_base_url').val(), args.service.base_url);
 | 
						|
    assert.equal($(html).find('#edit_service_interface').val(), args.service.interface);
 | 
						|
});
 | 
						|
 | 
						|
run_test('edit_embedded_bot_service', () => {
 | 
						|
    const args = {
 | 
						|
        service: {service_name: "giphy",
 | 
						|
                  config_data: {key: "abcd1234"}},
 | 
						|
    };
 | 
						|
    const html = render('settings/edit_embedded_bot_service', args);
 | 
						|
    assert.equal($(html).find('#embedded_bot_key_edit').attr('name'), 'key');
 | 
						|
    assert.equal($(html).find('#embedded_bot_key_edit').val(), 'abcd1234');
 | 
						|
});
 | 
						|
 | 
						|
run_test('archive_message_group', () => {
 | 
						|
    // The messages list below doesn't represent the actual HTML which would be
 | 
						|
    // feed to these handlebar templates but since the actual one is a lot bigger
 | 
						|
    // to be included in a test case and really comes pre rendered from the backend
 | 
						|
    // we just kinda test out the template part which is rendered on frontend with
 | 
						|
    // some self made html for messages to insert into the handlebars.
 | 
						|
    const messages = [
 | 
						|
        '<p>This is message one.</p>',
 | 
						|
        '<p>This is message two.</p>',
 | 
						|
    ];
 | 
						|
 | 
						|
    const groups = [
 | 
						|
        {
 | 
						|
            display_recipient: "support",
 | 
						|
            message_containers: messages,
 | 
						|
            group_date_divider_html: '"<span class="timerender82">Jan 07</span>"',
 | 
						|
            show_group_date_divider: true,
 | 
						|
        },
 | 
						|
    ];
 | 
						|
 | 
						|
    const html = render('archive_message_group', {message_groups: groups});
 | 
						|
 | 
						|
    const first_message_text = $(html).next('.recipient_row').find('p').first().text().trim();
 | 
						|
    assert.equal(first_message_text, "This is message one.");
 | 
						|
 | 
						|
    const last_message_text = $(html).next('.recipient_row').find('p').last().text().trim();
 | 
						|
    assert.equal(last_message_text, 'This is message two.');
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
run_test('recipient_row', () => {
 | 
						|
    // Assert HTML escaping in topic links.
 | 
						|
    const data = {
 | 
						|
        is_stream: true,
 | 
						|
        topic_links: [
 | 
						|
            'https://google.com',
 | 
						|
            'https://<script>alert("Hello")</script>',
 | 
						|
        ],
 | 
						|
    };
 | 
						|
    const html = render('recipient_row', data);
 | 
						|
    assert(html.indexOf('<script>alert("Hello")</script>') === -1);
 | 
						|
    assert(html.indexOf('<script>alert("Hello")</script>') !== -1);
 | 
						|
});
 | 
						|
 | 
						|
run_test('invitation_failed_error', () => {
 | 
						|
    let err_list = [];
 | 
						|
    err_list.push("hamlet@zulip.com: Account has been deactivated.");
 | 
						|
    let data = {
 | 
						|
        error_message: "We weren't able to invite anyone.",
 | 
						|
        error_list: err_list,
 | 
						|
        is_invitee_deactivated: true,
 | 
						|
        is_admin: true,
 | 
						|
    };
 | 
						|
 | 
						|
    let html = '<div>';
 | 
						|
    html += render('invitation_failed_error', data);
 | 
						|
    html += '</div>';
 | 
						|
    let p = $(html).find('p#invitation_error_message');
 | 
						|
    assert.equal(p.text().trim(), "We weren't able to invite anyone.");
 | 
						|
    let li = $(html).find("li").first();
 | 
						|
    assert.equal(li.text().trim(), "hamlet@zulip.com: Account has been deactivated.");
 | 
						|
    let p_admin = $(html).find("p#invitation_admin_message");
 | 
						|
    assert.equal(p_admin.text().trim(), 'translated: You can reactivate deactivated users from organization settings.');
 | 
						|
 | 
						|
    err_list = [];
 | 
						|
    err_list.push("hamlet@zulip.com: Account has been deactivated.");
 | 
						|
    data = {
 | 
						|
        error_message: "We weren't able to invite anyone.",
 | 
						|
        error_list: err_list,
 | 
						|
        is_invitee_deactivated: true,
 | 
						|
        is_admin: false,
 | 
						|
    };
 | 
						|
 | 
						|
    html = '<div>';
 | 
						|
    html += render('invitation_failed_error', data);
 | 
						|
    html += '</div>';
 | 
						|
    p = $(html).find("p#invitation_error_message");
 | 
						|
    assert.equal(p.text().trim(), "We weren't able to invite anyone.");
 | 
						|
    li = $(html).find("li").first();
 | 
						|
    assert.equal(li.text().trim(), "hamlet@zulip.com: Account has been deactivated.");
 | 
						|
    const msg = $(html).find("p#invitation_non_admin_message");
 | 
						|
    assert.equal(msg.text().trim(), 'translated: Organization administrators can reactivate deactivated users.');
 | 
						|
    p_admin = $(html).find("p#invitation_admin_message");
 | 
						|
    assert.equal(p_admin.length, 0);
 | 
						|
 | 
						|
    err_list = [];
 | 
						|
    err_list.push("othello@zulip.com: Already has an account.");
 | 
						|
    data = {
 | 
						|
        error_message: "We weren't able to invite anyone.",
 | 
						|
        error_list: err_list,
 | 
						|
        is_invitee_deactivated: false,
 | 
						|
        is_admin: false,
 | 
						|
    };
 | 
						|
 | 
						|
    html = '<div>';
 | 
						|
    html += render('invitation_failed_error', data);
 | 
						|
    html += '</div>';
 | 
						|
    p = $(html).find("p#invitation_error_message");
 | 
						|
    assert.equal(p.text().trim(), "We weren't able to invite anyone.");
 | 
						|
    li = $(html).find("li").first();
 | 
						|
    assert.equal(li.text().trim(), "othello@zulip.com: Already has an account.");
 | 
						|
    p_admin = $(html).find("p#invitation_admin_message");
 | 
						|
    assert.equal(p_admin.length, 0);
 | 
						|
 | 
						|
});
 |