mirror of
https://github.com/zulip/zulip.git
synced 2025-11-10 00:46:03 +00:00
This is a recent regression where we I refactored the toggle component. For some reason the old code was waiting until after the callback to set some of its state, and I did the same thing when I simplified how the state was stored. Under the old code, this didn't manifest as a bug, although the old code was problematic for other reasons. This "fix" doesn't actually change anything user facing, as the follow up commit fixes the proximal problem more directly. And the toggle component is still prone to people writing code that tries to inspect the state of the widget as it's being built.
202 lines
5.7 KiB
JavaScript
202 lines
5.7 KiB
JavaScript
set_global('i18n', global.stub_i18n);
|
|
|
|
zrequire('keydown_util');
|
|
zrequire('components');
|
|
|
|
var noop = function () {};
|
|
|
|
var LEFT_KEY = { which: 37, preventDefault: noop };
|
|
var RIGHT_KEY = { which: 39, preventDefault: noop };
|
|
|
|
(function test_basics() {
|
|
var keydown_f;
|
|
var click_f;
|
|
var tabs = [];
|
|
var focused_tab;
|
|
var callback_args;
|
|
|
|
function make_tab(i) {
|
|
var self = {};
|
|
|
|
assert.equal(tabs.length, i);
|
|
|
|
self.stub = true;
|
|
self.class = [];
|
|
|
|
self.addClass = function (c) {
|
|
self.class += ' ' + c;
|
|
var tokens = self.class.trim().split(/ +/);
|
|
self.class = _.uniq(tokens).join(' ');
|
|
};
|
|
|
|
self.removeClass = function (c) {
|
|
var tokens = self.class.trim().split(/ +/);
|
|
self.class = _.without(tokens, c).join(' ');
|
|
};
|
|
|
|
self.click = function () {
|
|
click_f.call(this);
|
|
};
|
|
|
|
self.data = function (name) {
|
|
assert.equal(name, 'tab-id');
|
|
return i;
|
|
};
|
|
|
|
self.focus = function () {
|
|
focused_tab = i;
|
|
};
|
|
|
|
tabs.push(self);
|
|
|
|
return self;
|
|
}
|
|
|
|
var ind_tab = (function () {
|
|
var self = {};
|
|
|
|
self.stub = true;
|
|
|
|
self.click = function (f) {
|
|
click_f = f;
|
|
};
|
|
|
|
self.keydown = function (f) {
|
|
keydown_f = f;
|
|
};
|
|
|
|
self.removeClass = function (c) {
|
|
_.each(tabs, function (tab) {
|
|
tab.removeClass(c);
|
|
});
|
|
};
|
|
|
|
self.eq = function (idx) {
|
|
return tabs[idx];
|
|
};
|
|
|
|
return self;
|
|
}());
|
|
|
|
var switcher = (function () {
|
|
var self = {};
|
|
|
|
self.stub = true;
|
|
|
|
self.children = [];
|
|
|
|
self.append = function (child) {
|
|
self.children.push(child);
|
|
};
|
|
|
|
self.find = function (sel) {
|
|
switch (sel) {
|
|
case ".ind-tab":
|
|
return ind_tab;
|
|
default:
|
|
throw Error('unknown selector: ' + sel);
|
|
}
|
|
};
|
|
|
|
return self;
|
|
}());
|
|
|
|
set_global('$', function (sel) {
|
|
if (sel.stub) {
|
|
// The component often redundantly re-wraps objects.
|
|
return sel;
|
|
}
|
|
|
|
switch (sel) {
|
|
case "<div class='tab-switcher'></div>":
|
|
return switcher;
|
|
case "<div class='ind-tab' data-tab-key='keyboard-shortcuts' data-tab-id='0' tabindex='0'>translated: Keyboard shortcuts</div>":
|
|
return make_tab(0);
|
|
case "<div class='ind-tab' data-tab-key='markdown-help' data-tab-id='1' tabindex='0'>translated: Message formatting</div>":
|
|
return make_tab(1);
|
|
case "<div class='ind-tab' data-tab-key='search-operators' data-tab-id='2' tabindex='0'>translated: Search operators</div>":
|
|
return make_tab(2);
|
|
default:
|
|
throw Error('unknown selector: ' + sel);
|
|
}
|
|
});
|
|
|
|
var callback_value;
|
|
|
|
var widget;
|
|
widget = components.toggle({
|
|
name: "info-overlay-toggle",
|
|
selected: 0,
|
|
values: [
|
|
{ label: i18n.t("Keyboard shortcuts"), key: "keyboard-shortcuts" },
|
|
{ label: i18n.t("Message formatting"), key: "markdown-help" },
|
|
{ label: i18n.t("Search operators"), key: "search-operators" },
|
|
],
|
|
callback: function (name, key) {
|
|
assert.equal(callback_args, undefined);
|
|
callback_args = [name, key];
|
|
|
|
// The subs code tries to get a widget value in the middle of a
|
|
// callback, which can lead to obscure bugs.
|
|
if (widget) {
|
|
callback_value = widget.value();
|
|
}
|
|
},
|
|
});
|
|
|
|
assert.equal(widget.get(), switcher);
|
|
|
|
assert.deepEqual(switcher.children, tabs);
|
|
|
|
assert.equal(focused_tab, 0);
|
|
assert.equal(tabs[0].class, 'first selected');
|
|
assert.equal(tabs[1].class, 'middle');
|
|
assert.equal(tabs[2].class, 'last');
|
|
assert.deepEqual(callback_args, ['translated: Keyboard shortcuts', 'keyboard-shortcuts']);
|
|
assert.equal(widget.value(), 'translated: Keyboard shortcuts');
|
|
|
|
callback_args = undefined;
|
|
|
|
components.toggle.lookup("info-overlay-toggle").goto('markdown-help');
|
|
assert.equal(focused_tab, 1);
|
|
assert.equal(tabs[0].class, 'first');
|
|
assert.equal(tabs[1].class, 'middle selected');
|
|
assert.equal(tabs[2].class, 'last');
|
|
assert.deepEqual(callback_args, ['translated: Message formatting', 'markdown-help']);
|
|
assert.equal(widget.value(), 'translated: Message formatting');
|
|
|
|
callback_args = undefined;
|
|
|
|
keydown_f.call(tabs[focused_tab], RIGHT_KEY);
|
|
assert.equal(focused_tab, 2);
|
|
assert.equal(tabs[0].class, 'first');
|
|
assert.equal(tabs[1].class, 'middle');
|
|
assert.equal(tabs[2].class, 'last selected');
|
|
assert.deepEqual(callback_args, ['translated: Search operators', 'search-operators']);
|
|
assert.equal(widget.value(), 'translated: Search operators');
|
|
assert.equal(widget.value(), callback_value);
|
|
|
|
// try to crash the key handler
|
|
keydown_f.call(tabs[focused_tab], RIGHT_KEY);
|
|
assert.equal(widget.value(), 'translated: Search operators');
|
|
|
|
callback_args = undefined;
|
|
|
|
keydown_f.call(tabs[focused_tab], LEFT_KEY);
|
|
assert.equal(widget.value(), 'translated: Message formatting');
|
|
|
|
callback_args = undefined;
|
|
|
|
keydown_f.call(tabs[focused_tab], LEFT_KEY);
|
|
assert.equal(widget.value(), 'translated: Keyboard shortcuts');
|
|
|
|
// try to crash the key handler
|
|
keydown_f.call(tabs[focused_tab], LEFT_KEY);
|
|
assert.equal(widget.value(), 'translated: Keyboard shortcuts');
|
|
|
|
callback_args = undefined;
|
|
|
|
click_f.call(tabs[1]);
|
|
assert.equal(widget.value(), 'translated: Message formatting');
|
|
}());
|