mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 14:03:30 +00:00 
			
		
		
		
	We now set locally_echoed to true for messages that are locally echoed, and we change some of our code to look for this flag.
		
			
				
	
	
		
			365 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			365 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
var util = require("util");
 | 
						|
var REALMS_HAVE_SUBDOMAINS = casper.cli.get('subdomains');
 | 
						|
var common = (function () {
 | 
						|
 | 
						|
var exports = {};
 | 
						|
 | 
						|
var test_credentials = require('../../var/casper/test_credentials.js').test_credentials;
 | 
						|
 | 
						|
function timestamp() {
 | 
						|
    return new Date().getTime();
 | 
						|
}
 | 
						|
 | 
						|
// The timestamp of the last message send or get_events result.
 | 
						|
var last_send_or_update = -1;
 | 
						|
 | 
						|
function log_in(credentials) {
 | 
						|
    if (credentials === undefined) {
 | 
						|
        credentials = test_credentials.default_user;
 | 
						|
    }
 | 
						|
 | 
						|
    casper.test.info('Logging in');
 | 
						|
    casper.fill('form[action^="/accounts/login"]', {
 | 
						|
        username: credentials.username,
 | 
						|
        password: credentials.password,
 | 
						|
    }, true /* submit form */);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
exports.init_viewport = function () {
 | 
						|
    casper.options.viewportSize = {width: 1280, height: 1024};
 | 
						|
};
 | 
						|
 | 
						|
exports.initialize_casper = function () {
 | 
						|
    if (casper.zulip_initialized !== undefined) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    casper.zulip_initialized = true;
 | 
						|
    // These initialization steps will fail if they run before
 | 
						|
    // casper.start has been called.
 | 
						|
 | 
						|
    // Fail if we get a JavaScript error in the page's context.
 | 
						|
    // Based on the example at http://phantomjs.org/release-1.5.html
 | 
						|
    //
 | 
						|
    // casper.on('error') doesn't work (it never gets called) so we
 | 
						|
    // set this at the PhantomJS level.
 | 
						|
    casper.page.onError = function (msg, trace) {
 | 
						|
        casper.test.error(msg);
 | 
						|
        casper.echo('Traceback:');
 | 
						|
        trace.forEach(function (item) {
 | 
						|
            casper.echo('  ' + item.file + ':' + item.line);
 | 
						|
        });
 | 
						|
        casper.exit(1);
 | 
						|
    };
 | 
						|
 | 
						|
    // Capture screens from all failures
 | 
						|
    var casper_failure_count = 1;
 | 
						|
    casper.test.on('fail', function failure() {
 | 
						|
        if (casper_failure_count <= 10) {
 | 
						|
            casper.capture("var/casper/casper-failure" + casper_failure_count + ".png");
 | 
						|
            casper_failure_count += 1;
 | 
						|
        }
 | 
						|
    });
 | 
						|
 | 
						|
    // Update last_send_or_update whenever get_events returns.
 | 
						|
    casper.on('resource.received', function (resource) {
 | 
						|
        if (/\/json\/get_events/.test(resource.url)) {
 | 
						|
            last_send_or_update = timestamp();
 | 
						|
        }
 | 
						|
    });
 | 
						|
 | 
						|
    casper.on('load.finished', function () {
 | 
						|
        casper.evaluateOrDie(function () {
 | 
						|
            $(document).trigger($.Event('phantom_page_loaded'));
 | 
						|
            return true;
 | 
						|
        });
 | 
						|
    });
 | 
						|
 | 
						|
    // This function should always be enclosed within a then() otherwise
 | 
						|
    // it might not exist on casper object.
 | 
						|
    casper.waitForSelectorText = function (selector, text, then, onTimeout, timeout) {
 | 
						|
        this.waitForSelector(selector, function _then() {
 | 
						|
            this.waitFor(function _check() {
 | 
						|
                var content = this.fetchText(selector);
 | 
						|
                if (util.isRegExp(text)) {
 | 
						|
                    return text.test(content);
 | 
						|
                }
 | 
						|
                return content.indexOf(text) !== -1;
 | 
						|
            }, then, onTimeout, timeout);
 | 
						|
        }, onTimeout, timeout);
 | 
						|
        return this;
 | 
						|
    };
 | 
						|
 | 
						|
    casper.evaluate(function () {
 | 
						|
        window.localStorage.clear();
 | 
						|
    });
 | 
						|
};
 | 
						|
 | 
						|
exports.then_log_in = function (credentials) {
 | 
						|
    casper.then(function () {
 | 
						|
        log_in(credentials);
 | 
						|
    });
 | 
						|
};
 | 
						|
 | 
						|
exports.start_and_log_in = function (credentials, viewport) {
 | 
						|
    var log_in_url = "";
 | 
						|
    if (REALMS_HAVE_SUBDOMAINS) {
 | 
						|
        log_in_url = "http://zulip.zulipdev.com:9981/accounts/login";
 | 
						|
    } else {
 | 
						|
        log_in_url = "http://zulipdev.com:9981/accounts/login";
 | 
						|
    }
 | 
						|
    exports.init_viewport();
 | 
						|
    casper.start(log_in_url, function () {
 | 
						|
        exports.initialize_casper(viewport);
 | 
						|
        log_in(credentials);
 | 
						|
    });
 | 
						|
};
 | 
						|
 | 
						|
exports.then_log_out = function () {
 | 
						|
    var menu_selector = '#settings-dropdown';
 | 
						|
    var logout_selector = 'li[title="Log out"] a';
 | 
						|
 | 
						|
    casper.waitUntilVisible(menu_selector, function () {
 | 
						|
        casper.click(menu_selector);
 | 
						|
 | 
						|
        casper.waitUntilVisible(logout_selector, function () {
 | 
						|
            casper.test.info('Logging out');
 | 
						|
            casper.click(logout_selector);
 | 
						|
 | 
						|
        });
 | 
						|
 | 
						|
    });
 | 
						|
    casper.waitUntilVisible(".login-page-container", function () {
 | 
						|
        casper.test.assertUrlMatch(/accounts\/login\/$/);
 | 
						|
        casper.test.info("Logged out");
 | 
						|
    });
 | 
						|
};
 | 
						|
 | 
						|
// Put the specified string into the field_selector, then
 | 
						|
// select the menu item matching item by typing str.
 | 
						|
exports.select_item_via_typeahead = function (field_selector, str, item) {
 | 
						|
    casper.then(function () {
 | 
						|
        casper.test.info('Looking in ' + field_selector + ' to select ' + str + ', ' + item);
 | 
						|
 | 
						|
        casper.evaluate(function (field_selector, str, item) {
 | 
						|
            // Set the value and then send a bogus keyup event to trigger
 | 
						|
            // the typeahead.
 | 
						|
            $(field_selector)
 | 
						|
                .focus()
 | 
						|
                .val(str)
 | 
						|
                .trigger($.Event('keyup', { which: 0 }));
 | 
						|
 | 
						|
            // You might think these steps should be split by casper.then,
 | 
						|
            // but apparently that's enough to make the typeahead close (??),
 | 
						|
            // but not the first time.
 | 
						|
 | 
						|
            // Trigger the typeahead.
 | 
						|
            // Reaching into the guts of Bootstrap Typeahead like this is not
 | 
						|
            // great, but I found it very hard to do it any other way.
 | 
						|
 | 
						|
            var tah = $(field_selector).data().typeahead;
 | 
						|
            tah.mouseenter({
 | 
						|
                currentTarget: $('.typeahead:visible li:contains("'+item+'")')[0],
 | 
						|
            });
 | 
						|
            tah.select();
 | 
						|
        }, {field_selector:field_selector, str: str, item: item});
 | 
						|
    });
 | 
						|
};
 | 
						|
 | 
						|
exports.enable_page_console = function () {
 | 
						|
    // Call this (after casper.start) to enable printing page-context
 | 
						|
    // console.log (plus some CasperJS-specific messages) to the
 | 
						|
    // terminal.
 | 
						|
    casper.on('remote.message', function (msg) {
 | 
						|
        casper.echo(msg);
 | 
						|
    });
 | 
						|
};
 | 
						|
 | 
						|
exports.check_form = function (form_selector, expected, test_name) {
 | 
						|
    var values = casper.getFormValues(form_selector);
 | 
						|
    var k;
 | 
						|
    for (k in expected) {
 | 
						|
        if (expected.hasOwnProperty(k)) {
 | 
						|
            casper.test.assertEqual(values[k], expected[k],
 | 
						|
                                    test_name ? (test_name + ": " + k) : undefined);
 | 
						|
        }
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
exports.wait_for_message_actually_sent = function () {
 | 
						|
    casper.waitFor(function () {
 | 
						|
        return casper.evaluate(function () {
 | 
						|
            return !current_msg_list.last().locally_echoed;
 | 
						|
        });
 | 
						|
    });
 | 
						|
};
 | 
						|
 | 
						|
exports.turn_off_press_enter_to_send = function () {
 | 
						|
    var enter_send_selector = '#enter_sends';
 | 
						|
    casper.waitForSelector(enter_send_selector);
 | 
						|
 | 
						|
    var is_checked = casper.evaluate(function (enter_send_selector) {
 | 
						|
        return document.querySelector(enter_send_selector).checked;
 | 
						|
    }, enter_send_selector);
 | 
						|
 | 
						|
    if (is_checked) {
 | 
						|
        casper.click(enter_send_selector);
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
// Wait for any previous send to finish, then send a message.
 | 
						|
exports.then_send_message = function (type, params) {
 | 
						|
    casper.then(function () {
 | 
						|
        casper.waitForSelector('#compose-send-button:enabled');
 | 
						|
        casper.waitForSelector('#new_message_content');
 | 
						|
    });
 | 
						|
 | 
						|
    casper.then(function () {
 | 
						|
        if (type === "stream") {
 | 
						|
            casper.page.sendEvent('keypress', "c");
 | 
						|
        } else if (type === "private") {
 | 
						|
            casper.page.sendEvent('keypress', "C");
 | 
						|
        } else {
 | 
						|
            casper.test.assertTrue(false, "send_message got valid message type");
 | 
						|
        }
 | 
						|
        casper.fill('form[action^="/json/messages"]', params);
 | 
						|
 | 
						|
        exports.turn_off_press_enter_to_send();
 | 
						|
 | 
						|
        casper.then(function () {
 | 
						|
            casper.click('#compose-send-button');
 | 
						|
        });
 | 
						|
    });
 | 
						|
 | 
						|
    casper.then(function () {
 | 
						|
        casper.waitFor(function emptyComposeBox() {
 | 
						|
            return casper.getFormValues('form[action^="/json/messages"]').content === '';
 | 
						|
        });
 | 
						|
        exports.wait_for_message_actually_sent();
 | 
						|
    });
 | 
						|
 | 
						|
    casper.then(function () {
 | 
						|
        last_send_or_update = timestamp();
 | 
						|
    });
 | 
						|
};
 | 
						|
 | 
						|
// Get message headings (recipient rows) and bodies out of the DOM.
 | 
						|
// casper.evaluate plays weird tricks with a closure, evaluating
 | 
						|
// it in the web page's context.  Passing arguments from the test
 | 
						|
// script's context is awkward (c.f. the various appearances of
 | 
						|
// 'table' here).
 | 
						|
exports.get_rendered_messages = function (table) {
 | 
						|
    return casper.evaluate(function (table) {
 | 
						|
        var tbl = $('#'+table);
 | 
						|
        return {
 | 
						|
            headings: $.map(tbl.find('.recipient_row .message-header-contents'), function (elem) {
 | 
						|
                var $clone = $(elem).clone(true);
 | 
						|
                $clone.find(".recipient_row_date").remove();
 | 
						|
 | 
						|
                return $clone.text();
 | 
						|
            }),
 | 
						|
 | 
						|
            bodies: $.map(tbl.find('.message_content'), function (elem) {
 | 
						|
                return elem.innerHTML;
 | 
						|
            }),
 | 
						|
        };
 | 
						|
    }, {
 | 
						|
        table: table,
 | 
						|
    });
 | 
						|
};
 | 
						|
 | 
						|
exports.get_form_field_value = function (selector) {
 | 
						|
    return casper.evaluate(function (selector) {
 | 
						|
        return $(selector).val();
 | 
						|
    }, selector);
 | 
						|
};
 | 
						|
 | 
						|
// Inject key presses by running some jQuery code in page context.
 | 
						|
// PhantomJS and CasperJS don't provide a clean way to insert key
 | 
						|
// presses by code, only strings of printable characters.
 | 
						|
exports.keypress = function (code) {
 | 
						|
    casper.evaluate(function (code) {
 | 
						|
        $('body').trigger($.Event('keydown', { which: code }));
 | 
						|
    }, {
 | 
						|
        code: code,
 | 
						|
    });
 | 
						|
};
 | 
						|
 | 
						|
// Send a whole list of messages using then_send_message.
 | 
						|
exports.then_send_many = function (msgs) {
 | 
						|
    msgs.forEach(function (msg) {
 | 
						|
        exports.then_send_message(
 | 
						|
            (msg.stream !== undefined) ? 'stream' : 'private',
 | 
						|
            msg);
 | 
						|
    });
 | 
						|
};
 | 
						|
 | 
						|
// Wait to receive queued messages.
 | 
						|
exports.wait_for_receive = function (step) {
 | 
						|
    // Wait until the last send or get_events result was more than 1000 ms ago.
 | 
						|
    casper.waitFor(function () {
 | 
						|
        return (timestamp() - last_send_or_update) > 1000;
 | 
						|
    }, step);
 | 
						|
};
 | 
						|
 | 
						|
// Wait until the loading spinner goes away (helpful just after logging in).
 | 
						|
exports.wait_for_load = function (step) {
 | 
						|
    casper.waitWhileVisible('#page_loading_indicator', step);
 | 
						|
};
 | 
						|
 | 
						|
// innerText sometimes gives us non-breaking space characters, and occasionally
 | 
						|
// a different number of spaces than we expect.
 | 
						|
exports.normalize_spaces = function (str) {
 | 
						|
    return str.replace(/\s+/g, ' ');
 | 
						|
};
 | 
						|
 | 
						|
exports.ltrim = function (str) {
 | 
						|
    return str.replace(/^\s+/g, '');
 | 
						|
};
 | 
						|
 | 
						|
exports.rtrim = function (str) {
 | 
						|
    return str.replace(/\s+$/g, '');
 | 
						|
};
 | 
						|
 | 
						|
exports.trim = function (str) {
 | 
						|
    return exports.rtrim(exports.ltrim(str));
 | 
						|
};
 | 
						|
 | 
						|
// Call get_rendered_messages and then check that the last few headings and
 | 
						|
// bodies match the specified arrays.
 | 
						|
exports.expected_messages = function (table, headings, bodies) {
 | 
						|
    casper.test.assertVisible('#'+table, table + ' is visible');
 | 
						|
 | 
						|
    var msg = exports.get_rendered_messages(table);
 | 
						|
 | 
						|
    casper.test.assertEquals(
 | 
						|
        msg.headings.slice(-headings.length).map(exports.normalize_spaces).map(exports.trim),
 | 
						|
        headings.map(exports.trim),
 | 
						|
        'Got expected message headings');
 | 
						|
 | 
						|
    casper.test.assertEquals(
 | 
						|
        msg.bodies.slice(-bodies.length),
 | 
						|
        bodies,
 | 
						|
        'Got expected message bodies');
 | 
						|
};
 | 
						|
 | 
						|
exports.un_narrow = function () {
 | 
						|
    casper.test.info('Un-narrowing');
 | 
						|
    if (casper.visible('.message_comp')) {
 | 
						|
        // close the compose box
 | 
						|
        common.keypress(27); // Esc
 | 
						|
    }
 | 
						|
    common.keypress(27); // Esc
 | 
						|
};
 | 
						|
 | 
						|
return exports;
 | 
						|
 | 
						|
}());
 | 
						|
 | 
						|
// For inclusion with CasperJS
 | 
						|
try {
 | 
						|
    exports.common = common;
 | 
						|
} catch (e) {
 | 
						|
    // continue regardless of error
 | 
						|
}
 |