Previously, exact matches could be pushed off the typeahead list in the
case where there were more prefix matches that happened to rank first,
which is confusing to the user: if an emoji, for instance, falls into
this category, it will never show up in typeahead, which is easy to
confuse with the emoji not existing.
This isn't a perfect fix — there are still cases where it's hard to find
emojis because the prefix-space is very crowded, but it does fix a
category of surprising and frustrating behaviour.
This doesn't come completely without downside - it means that the exact
match emoji will jump to the front of the list, which changes what is
currently conceptually a "filtering" operation to a "filtering and
sorting" operation, but it seems on the whole to be a more ideal
experience. This is particularly notable in the non-typeahead emoji
picker, which uses the same codepath, but this change seems somewhat
desirable even there, since it allows the user to type the name of an
emoji and press enter and have that emoji show up, without having to
visually confirm that they aren't inadvertently selecting a
prefix-matching emoji.
A better solution to this in the long term might be ordering emoji
results by shortest-first as a tiebreaker for alphabetical ordering,
since that should provide the same behaviour while keeping the mental
model as "filtering" (since the sort order won't change as the user
types), but this seems like a reasonable first pass, and changing to
shortest-first ordering after making this change won't break any muscle
memory for existing users.
We don't need to handle user clicking on Zulip logo since
changing the hash via the `a` tag takes care of it automatically.
Also, cleanup the narrow.restore_home_state function since
it is no longer being used.
We manually trigger a re-render of RT after a stream is muted
to update the list of topic in RT for the active filter.
This fixes the bug that RT doesn't update correctly
after a stream is muted.
If user is in private message narrow, we reduce height of stream
list to allow height for pm list in the left sidebar. We need
to recalculate it when moving out of pm narrow and moving in
rt narrow.
When idle, we try to backfill messages and in the end reselect
the closest message in the list, which can be a unread message
if present.
When recent topics is open, we can backfill messages; but
shouldn't select the message_id otherwise it will mark the
message as read if the message is unread while triggering
`message_selected.zulip`.
User can go from recent topics to stream / topic narrow via various
means, but all go through narrow.activate, hence we make sure all the
state changes we do in recent_topics.hide are actually applied when we
hide recent topics and go to another narrow.
This fixes the bug that narrowing from left sidebar to a stream
takes user to the top of the narrow.
We land user on the first row of the table instead of the search
box because here user can access hotkeys like `w`, `q`, `/`, etc,
which will not be directly available if user is focused in
recent topics search box.
For tests:
We set focus to search by default to avoid mocking a lot of
table html for getting the tests passing.
Previously the filter would be reset every time the page was
refreshed. This commit adds persistence via localstorage, the tests
follow the pattern used in tests for drafts.
Fixes: #15676.
Go to Recent Topics on "#", no hash and "#recent_topics".
Go to Recent Topics as the last destination for escape key.
Map `a` key to All messages and change its hash to
`#all_messages`.
Recent Topics is no longer an overlay now, but note that it is
also not a typical messages narrow. It can reside between
an overlay and a Filter in the sense that it is dispalyed as
a typical Filter narrow but has properties of an Overlay.
Compose box is not visible in this view as it will be confusing
to many users and hence compose shortcuts have also been disabled.
Keyboard shortcuts that apply on messages have also been disabled.
The remaining shortcuts that apply to a narrow are still accessible
here.
We now only assign target once, rather than
assigning it then overwriting it for the
not-sent-by-me use case.
I tried to extract a "selector" here but the linter
complained.
Splitting up the tests here ensures we don't
needlessly do extra work here. (In the prior
clumsy implementation, the second test that
I split out here would fail due to the lack
of us setting up the jquery stub.)
This warning was added in #6551. It’s not for any version of the
current Electron app, which we warn about on the server side with
DESKTOP_WARNING_VERSION, but rather some pre-Electron app so ancient I
don’t even know what it is. Apparently it communicated using the
window.bridge global, so eradicate that too.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
Note that at this point, it's not possible to create moderator users;
this just will make it easier to write tests for logic involving them
as we develop the feature.
‘function’ and ‘=>’ are not equivalent because they bind ‘this’
differently. For these functions, the ‘function’ semantics are
intentional.
This reverts part of commit 1a241cef88
(#17388).
Signed-off-by: Anders Kaseorg <anders@zulip.com>
We now call $.clear_all_elements at the top
of run_test.
We have to exempt two modules from the new regime:
compose
settings_user_groups
Also, if modules do set_global("$", ...) we don't
try to call the non-existent function.
It's possible we'll want to move to something like
this, but we might want to clean up the two
sloppy_$ modules first:
// AVOID THIS:
// const $ = require("zjquery")
run_test("test widget", ({override, $}) => {
override(foo, "bar", ...);
$.create(...);
// do stuff
});
It was checking whether the selector string is itself null, not
whether it selects anything!
Use page.waitForSelector(…, {hidden: true}) instead.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
Move clear_zulip_refs into restore, and rewrite it without lodash. We
no longer need the requires array, and zrequire is now nothing more
than a wrapper around require.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
We weren't exercising this method in any
meaningful way during the tests, and when
do add coverage, we probably want to just
test it directly.
We also kill off stub_selector(), which was
never well-documented.
Callers can either explicitly pass in children,
stub out $(...)[0] as needed, or just
circumvent jQuery complications with override.
Note the reactions test was broken before,
since $(...)[0] was always returning the same
stub.
Like using $('input') is too broad a selector and shouldn't
be used in the codebase. With this error messages, contributors
can easily understand that now.
We no longer export make_zjquery().
We now instead have a singleton zjquery instance
that we attach to global.$ in index.js.
We call $.clear_all_elements() before each module.
(We will soon get even more aggressive about doing
it in run_test.)
Test functions can still override $ with set_global.
A good example of this is copy_and_paste using the
real jquery module.
We no longer exempt $ as a global variable, so
test modules that use the zjquery $ need to do:
const $ = require("../zjsunit/zjquery");
The "silent" option was kind of evil, as it had
$(...).find(...) passing back "self" instead of a stub.
Now we just use $(...).set_find_results(...) or
override(...) to simulate/bypass drawing code.
(It turns out hash_util didn't even need this option.)
The tooltips for the left panel of stream settings
have been broken since November 2018 due to my
commit 8f915da2ca.
The code prior to 2018 was restoring tooltips
right inside the loop where we were detaching
the row from the DOM to put it back into the
DOM at another place. And then I tried to
just add them in bulk, forgetting that I was
in the middle of all the DOM manipulation (and
hence my selector for the loop was a noop).
Also, I don't think we've ever had them for live
events that add streams. (I fixed that too.)
It's not clear to me that this code is actually
necessary, as we get hover help without
calling $(...).tooltip(...) properly.
This is probably why we didn't notice any
breakage when we merged my 2018 commit.
We generally want to avoid clicking on DOM elements
that may not actually be visible due to the prior
operation. Instead, we can just find the visible
element after each step.
I also introduce a couple helper functions to
de-clutter the click/unclick/click steps, and I do
a couple extra clicks for good measure.
You can verify that the test will fail if you
add an early return to update_check_button_for_sub.
We just want to reset the scrollbar here, which
we still do via ui.reset_scrollbar.
You don't want to preserve scroll position if
you are filtering or re-sorting.
We have long had this annoying two-pass way of building the
DOM that I am trying to eliminate.
The function names that I introduce here describe the current
situation more accurately.
In passing I make it so that we only throttle redraws when
users are actually typing. Using a throttled redraw when
you click on the sort icons is at best unnecessary, and it
may actually aggravate double clicks.
This splits the one big `run_test` block into
add/edit/delete parts, for better clarity.
This also removes the `with_field` calls and
uses the override style instead, which is
preferred because it makes sure the stubbed
function is actually called.
This commit replaces `with_field` calls to
use the override style instead.
`override` is preferred since it makes sure
the stubbed function is actually called,
while `with_field` doesn't, which makes it
hard to spot dead code.
This commit replaces all `with_stub` calls and
explicitly calls `make_stub` instead.
The `with_stub` helper does not add much clarity
hence we now use scoped stub objects instead.
This de-indents some blocks where scoping isn't
required, for example when there is a single
stub object inside a `run_test` function.
With this change, we also need to explicitly
assert `num_calls`.
We can relax this restriction in the future, but
basically every time it came up for me, the test
code was just disorganized, or it had an easy
workaround.
These are still kind of a mess.
The old code combined the worst of both worlds:
- we had one monolithic test
- we called the events multiple times,
verifying a different stub each time
Now I make the tests more granular.
We could actually re-combine the tests, but
in a nicer way, so that we just set
up multiple stubs and verify that all stubs
get correctly invoked.
There is also some code cleanup here--in dispatch_subs,
we don't stub stream_data, so it's easier to write
deeper tests that actually validate the data changes.
This prevents a bug where we interpret "2something"
as a modern slug instead of a legacy stream name.
The bug was probably somewhat unlikely to happen in
practice, since it only manifests if 2 is an actual
stream_id.
We still need to write to these globals with set_global because the
code being tested reads from them, but the tests themselves should
never need to read from them.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit makes it so that MessageListData
methods always attempt to filter muted messages.
We later, in a new function
(`messages_filtered_for_topic_mutes`)
check if `excludes_muted_topics` is true or not,
and skip the filtering work if it isn't.
This new function consistently returns a new list.
This refactor will later allow us to write clean
and concise code as part of mute users.
This commit also refactors the muting tests
for MessageListData, which were earlier
spread across two `run_test` functions.
These tests should remain organized,
since similar tests will be added as part of
user mutes in future commits.
Previously, the `muting_enabled` property of
MessageListData class was used to indicate whether
some messages in the message list need to be
filtered due to topic muting, depending on the
narrow. For example, we exclude messages belonging
to muted topics from stream narrows, but not from
search narrows.
The name `muting_enabled` is a bit confusing, and hence is
changed to `excludes_muted_topics`.
It is also important that the name be specific, since
a similar new property will be added for user mutes
in future commits.
I have a local branch with a hacked up version of
zjquery that lets you basically detect when zjquery
stubs are never actually invoked by real code.
There are some nuances to that kind of audit, so
I haven't pushed the auditing code, but these
are low hanging fruit.
I now just use inline the code to create stubs
for the line items in the markdown_content
container, and I don't add methods to the
zjquery stubs.
And then I use the new "children" feature in
zjquery's `$.create(sel, opts)` to set up
$(".markdown_content"), which means I don't
have to stub `each` any more.
It's actually pretty rare in our codebase to
call methods like `$(...).map` or `$(...).each`,
but we now support them better in zjquery.
You can pass a list of child elements now to
`$.create(...)`.
Fixes the sorting button labels in stream settings, which were
regressed by commit f8fbae4d8e (because
the HTML was not marked as being HTML).
Signed-off-by: Anders Kaseorg <anders@zulip.com>
This is a minor refactor in the muting test in
`message_list`.
The `unmuted_messages` function filters out messages only
considering topic mutes, and not stream mutes.
The test previously made it look like we were testing
stream muting, by stubbing the `is_topic_muted` on the
basis of `stream_id`.
This also replaces the stub and uses real data instead.
This is a prep commit, which renames some variables
and functions involved in topic muting to include
the word "topic" in them.
This is done to have clarity when similar code
will be added as a part of the mute-user in
future commits.
Replaced methods/functions of moment.js with date-fns library.
The motive was to replace it with a smaller frontend timezone library.
Date-fns ~ 11.51 kb
moment.js ~ 217.87 kb
Some of the format strings change because date-fns encodes them
differently from how moment did.
Fixes#16373.
It's not clear to me why this code was necessary,
and I assume it was either originally written
with a bit of misunderstanding of how zjquery
works or it became unnecessary with some refactoring
of the "real" code.
We move some of the data setup to the top of the file.
We also remove some get_sub() calls that aren't really
necessary now that peer_data and stream_data are more
independent.
The maybe_clear_subscribers() function was an artifact of
when we used to attach subscribers to the "sub" records in
stream_data.js. I think it was basically a refactoring
shim, and due to some other recent cleanup, it was only
used in test code.
We also change how we validate stream ids.
Going forward, peer_data just looks up stream_ids with the
normal stream_data API when it's trying to warn about
rogue stream_ids coming in. As I alluded to in an earlier
commit, some of the warning code here might be overly
defensive, but at least it's pretty self-contained.
Now when we want to measure how long a block
of code takes to execute, we just wrap it with
`blueslip.measure_time`, instead of the awkward
idiom from my original commit of getting a callback
function.
My rationale for the original scheme was that I
wanted to minimize diffs and avoid changing
`const` to `let` in a few cases, but I believe
now that the function wrapper is nicer.
In a few cases I just removed the blueslip timing
code, since I was able to confirm on czo that
the times were pretty minimal.
We now use the same code in all places to
get the bucket of user_ids that correspond
to a stream, and we consistently treat
a stream as having zero subscribers, not
an undefined number of subscribers, in
the hypothetical case of us asking about
a stream that we're not tracking.
The behavior for untracked streams has
always been problematic, since if a
stream is untracked, all bets are off.
So now if we don't "track" the stream,
the subscriber count is zero. None of
our callers distinguish between undefined
and zero.
And we just consider the stream to be subscribed
by a user when add_subscriber is called,
even if we haven't been told by stream_data
to track the stream. (We also stop
returning true/false from add_subscriber,
since only test code was looking at it.)
We protect against the most likely source
of internal-to-the-frontend bugs by adding
the assert_number() call.
We generally have to assume that the server
is sending us sensible data at page load
time, or all bets are off.
And we have good protections in place
for unknown ids in our dispatch code
for peer_add/peer_remove events.
We also streamline some of the error handling code
by doing everything up front. This will prevent
scenarios where a single bad stream_id/user_id causes a
bunch of the same warnings in an inner loop.
This removes a bit of complexity. If a piece of
settings code needs to render a stream with
subscribers, it just asks for it.
We no longer have the brittle, action-at-a-distance
mechanism of mutating the subscriber count on to
the stream_data version of a sub.
Stream subs are pretty small, so making copies of
them is cheap, and the blueslip timings from the
previous commit can help confirm that.
There is some discussion of putting `subscriber_count`
on the Stream model, which may eventually get us
away from tracking it in `peer_data.js`, but we will
cross that bridge when we get there. See
https://github.com/zulip/zulip/issues/17101 for
more details.
The weekly stream traffic is a better tiebreaker
for stream typeaheads than subscriber count, as
it's more directly a measure of a stream's current
relevance.
Normally stream traffic and subscriber counts are
closely correlated, but a good example for me is
the #twitter feed on czo, which only has 80 subscribers,
but which gets more traffic than our #integrations
stream (with 16k subscribers). I would rather
see #twitter win the tiebreaker (if it even got
to the tiebreaker).
The main motivation behind this fix, though, is
to break our dependency on peer_data, which has
some upcoming changes that will introduce some
performance tradeoffs, and I want one less place
to audit.
Also, it will be easier long term to share this
code with mobile if we don't require mobile
to pull in our peer_data dependency. (The webapp
has different forces than mobile that dicate
our data structures.)
We use day_old calculated based on day instead of hours to
render last seen values. This fixes us incorrectly quoting
anything 24 - 48 hours ago as Yesterday and
incorrectly quoting `time` that are Yesterday
but < 24 hours ago in 'x hours ago' format.
We were adding `expanded` class to left-sidebar when searching
for streams even if the left-sidebar was not in the popover state.
This cased confusion with popovers.any_active returning true,
when actually it is not.
topic_generator previously included an entire lazy generator
combinator library that was used four times. These straightforward
equivalent loops might not be as fun but they are way simpler.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
Commit f0f6138f01 deleted the
translation for "Saved. Please <a class='reload_link'>reload</a> for
the change to take effect." which caused puppeteer test 16-settings to
fail as it had hardcoded the translation for German. This commit
changes the expected text and hence fixes the failing puppeteer test.
After this change all peer_data functions consistently
use stream_id rather than some "sub" object whose
data type is complicated by all sort of fields that
don't really concern how we track subscribers.
The goal here is to make all our peer_data functions
basically work in id space. Passing a full `sub`
to these functions is a legacy of when subscriber
info was attached to a full stream "sub" object,
but we don't care about anything sub-related
(color, description, name, etc.) when we are
dealing with subscriptions.
When callers pass in stream_id, you can be more
confident in a quick skim of the code that we're
not mutating anything in the "sub".
This de-clutters stream_data a bit. Since our
peer data is our biggest performance concern,
I want to contain any optimizations to a fairly
well-focused module.
The name `peer_data` is a bit of a compromise,
since we already have `subs.js` and we use
`sub` as a variable name for stream records
throughout our code, but it's consistent with
our event nomenclature (peer/add, peer/remove)
and it's short while still being fairly easy
to find with grep.
This sets us up to use better system-wide data structures
for tracking subscribers.
Basically, instead of storing subscriber data on the
"sub" objects in stream_data.js, we instead have a
parallel data structure called stream_subscribers.
We also have stream_create, stream_edit, and friends
use helper functions rather than accessing
sub.subscribers directly.
In commit ebea17b9a6,
we added an extra fetch to get accurate data for the top
items in recent topics table.
But the `narrow` parameter wasn't passed to the endpoint,
this resulted in fetching the user's overall message
history including the muted streams/topics which aren't
required by the recent topics table.
`operators` can be replaced as we set the same value for
the `narrow_state` module and the narrowed message list's
filter, when activating the narrow.
The changes made in this commit are as follows:
* The `remove_messages` is moved to the `message_events.js`
file from `ui.js`.
* We refactor `MessageListData.change_message_id` to no
longer require an `opts` parameter as this function
just returns whether we need to rerender or not.
The blueslip error block can be removed since we made
the change to no long defer the data updates in
commit 3b5ba6b2c1,
this case can no longer occur.
The changes made in this commit are as follows:
* We remove the now unused `ui.find_message` which was added
in commit 1666403850.
* We change the function paramter to now accept message ids
instead of messages to eliminate redundant message ids to
message convertion as only the id is required.
* The remove method in MessageListData did not remove the
messages from the hash, it removed only from the items,
this fixes it.
* This commit also fixes a bug where messages are not added
to the current message list if an event is recieved where
messages are moved to this current narrow.
Only the message removal logic was present, which has been
refactored in this commit.
Steve asked me to remove this, since the tictactoe game was always
intended as a proof of concept. Now that we have poll and todo
widgets, the sample code for tictactoe has much less value.
We replace the content and type in test_widgets.py to maintain
coverage.
This fixes a bug where the autocomplete for topics
deleted all the text content, if the topic jump is used
without entering any text.
The topic typeahead is automatically set up, on entering
the ">" key for stream completions. Therefore there is a
case where the user can select a typeahead item without
entering any text.
Thus the token length will be 0 and `beginning.slice(0, -0)` returns
"" instead of the `beginning` string. The case is only relevant for
"topic_list" completion as we don't set up the typeahead for empty
strings.
Fix this by reverting a hunk of
48f5e5179a, adding a test.
Fixes#16599.
Co-authored-by: Rohitt Vashishtha <aero31aero@gmail.com>
Refactor test_video_link_compose_clicked into seperate tests for:
No video provider.
Jitsi as the provider.
Zoom as the provider.
BigBlueButton as the provider.
While working on shifting toward native browser time zone APIs
(#16451), it was found that all but very recent Chrome and Node
versions reject certain legacy timezone aliases like US/Pacific
(https://crbug.com/364374).
For now, we only canonicalize the timezone property returned in user
objects and not the timezone setting itself.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
We now can send an implied matrix of user/stream tuples
for peer_add and peer_remove events.
The client code basically does this:
for stream_id in event['stream_ids']:
for user_id in event['user_ids']:
update_sub(stream_id, user_id)
We used to send individual events, which gets real
expensive when you are creating new streams. For
the case of copy-to-stream case, we should see
events go from U to 1, where U is the number of users
added.
Note that we don't yet fully optimize the potential
of this schema. For adding a new user with lots
of default streams, we still send S peer_add events.
And if you subscribe a bunch of users to a bunch of
private streams, we only go from U * S to S; we can't
optimize it down to one event easily.
Upstream has slightly changed the whitespace around stashes. Take
this opportunity to clean up the extra blank lines we were outputting.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
Using web_public_guest for anonymous users is confusing since
'guest' is actually a logged-in user compared to
web_public_guest which is not logged-in and has only
read access to messages. So, we rename it to
web_public_visitor.
Since the denmark stream stub has subscribed = true,
we include the current user id in it's subscribers set.
We remove user `me` from sweden stream stub for the same
reason.
For streams in which only full members are allowed to post,
we block guest users from posting there.
Guests users were blocked from posting to admin only streams
already. So now, guest users can only post to
STREAM_POST_POLICY_EVERYONE streams.
This is not a new feature but a bugfix which should have
happened when implementing full member stream policy / guest users.
For the lines of code that I changed here, we were
getting field reports that the below code
was getting `undefined`:
emoji.all_realm_emojis.get(r.emoji_code)
It's not really clear to me how this could happen,
but we definitely should fail softly here. We
still report it as an error, but we let the function
return and don't trigger a TypeError.
If there's a legitimate reason for realms to delete
realm emojis, we should either downgrade this to a
warning or consider a strategy of back-fixing messages
when realm emojis get deleted.
We rename all_everyone_warn_threshold to
wildcard_mention_large_stream_threshold as we would
be adding wildcard_mention_policy and this
constant will also be used to show error
in case when wildcard_mention_policy is set
to admins only.
In c563cdba61 we imported the generated
pygments data from outside `/shared` folder. This had a couple of
problems:
* Using `require` was the wrong way to do the import in ES6 modules.
* Since we get the data from outside `/shared`, clients like
zulip-mobile would not receive it - this case had to be handeled.
Here, we fix the above problems by receiving the data when initializing
through fenced_code.initialize, and when the pygments data structure is
empty (for zulip-mobile) we fallback to the old header structure without
the data-code-language tag.
Also, this commit does a small refactor to improve the way we fetch
canonicalized_alias from pygments_data.
Tests amended.
The failures saying incorrect password were caused due to
change in focus. Some actual code of ours calls focus on
the modal when opened but puppeteer was starting to type
before this occurs due to which the test was only able
to enter a part of string.
This was happening with change full name too but less
frequently as it's a relatively shorter string.
As a fix, we wait till the focus is on the modal and then
start typing.
Instead of prohibiting ‘return undefined’ (#8669), we require that a
function must return an explicit value always or never. This prevents
you from forgetting to return a value in some cases. It will also be
important for TypeScript, which distinguishes between undefined and
void.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
It would conflict with the stream_id variable after migration to an
ES6 module, and adds no real convenience over stream_sub().
Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit enables keyboard support for user info popovers for
navigating through popover options using up/down keys.
We add get_user_info_popover_items function, whose implementation
is different from other similar functions. Instead of using
popover_data.$tip we directly use $("div.user-info-popover")
because when we open the popover of bot owner from the bot
popover, the element which opens the popover is removed from
DOM and popover_data is undefined.
This commit replaces the "Reply mentioning user" option with "Copy mention
syntax" for user info popovers that are not opened from a message.
Clicking on "Copy mention syntax" will copy the mention syntax of user to
clipboard.
This change is done because user popovers not opened from message are not
linked to any message.
Previously, compose_ui.autosize_textarea didn't work while editing
messages in many cases (uploading files, typeaheads, keydown handling,
etc.).
Refactored the autosize_textarea function in compose_ui to work
while editing messages too and added appropriate argument for the
introduced function parameter at all occurences of the function
use.
Also, updated the corresponding test cases.
On uploading a few files from markdown_preview mode of compose box and
then switching back to edit mode, the compose box doesn't get resized.
It even doesn't allow to scroll through the content.
Fixed this by switching back to the edit mode everytime user uploads
some file in markdown_preview mode as there's no use of staying in
markdown_preview mode anyways after uploading a file as the preview
doesn't get updated.
Also, updated the corresponding test cases.
Fixes: #16296.
This mimics the backend logic for adding the data-attribute -
to know what Pygments language was used to highlight the code
block - in locally echoed messages.
New test added checks our logic for canonicalizing pygments alias
(for both frontend and backend).
Other fixtures and tests amended.
We need this information in the frontend to:
* Display the 'view in playground' option for locally echoed messages.
* When we add a UI settings for realm admins to configure their
playground choices, we'll need to use these canonicalized aliases
for displaying the option.
Hence, this tweaks the tool which generates pygments_data.json to contain
the data we need.
Bumping major PROVISION_VERSION since folks need to provision in both
directions.
Tests amended.
Wait for disable_stream_notifications selector to be visible
before clicking as it could cause flakes if the test tries
to click without it being visible.
Added a stronger validation of waiting for text "Verona" to
appear but that didn't really seem to have worked though it
seemed like fixing the flake by passing ~600 runs.
So, change the puppeteer click to a click through evaluate
as we had experiences where page.click() didn't work sometimes.
Though this has passed 1000 runs on CI, I'm not very certain
if this fixed it as this test passed 1000 times with my previous
PR fixing the same flake.
Currently, compose box placeholder text for PMs only gets updated
when the focus shifts to it.
With this change, the text is now also updated if recipients are
added or removed.
Fixes#15897.
The code to run single files was added
in c15695e514,
and it's just kinda strange code.
We already do a lot of file logic in Python
to check for line-coverage, so it's easier
to just have all the logic in Python.
This adds a new feature--you can now specify
the actual file:
./tools/test-js-with-node frontend_tests/node_tests/people.js
(This is helpful if you just want to use
shell autocomplete.)
Another minor change is that if you specify
individual files, we won't sort them. This is
important when you're trying to hunt down test
leaks.
Finally, we have a nicer message if we can't find
the file.
Because `util` is so late in the alphabet, this
leak never surfaced in practice, but I tried running
the node tests in reverse, and this leak came
up if you ran `util` before `stream_list`. I guess
it's nice that `stream_list` actually exercises
the difference between a dumb sort and an
Intl-aware sort.
It's possible that we should just assume that
Intl.Collator is always available at this point,
which would eliminate the need for this test.
There is good reason to do this (explanation is bit long!). With the
TypeScript migration, and the require and ES6 migrations that come
with it, we use require instead of set_global which loads the entire
module. Suppose we have a util module, which is used by some other
module, say message_store, and util is being required in message_store
since it is removed from window. Then, if a test zrequires
message_store first, and then zrequires the util module qand mocks one
of its methods, it will not be mocked for the message_store
module. The reason is:
1. zrequire('message_store') leads to require('util').
2. zrequire('util') removes the util module from cache and it is
reloaded. Now the util module in message_store and the one in
the test will be different and any updates to it in tests won't
be reflected in the actual code.
Which can lead to confusion for folks writing tests. I'll mention this
can be avoided doing zrequire('util') first but...that is not ideal.
And, since there was one outlier test that relied on this behavior,
we add the namespace.reset_module function.
Fixes#16252.
icon* classes are used by bootstrap for displaying glyphicons.
We removed these classes in our custom version of bootstrap 2.1.1;
but since our reset to v2.3.2, they have been added again and hence
any classes starting with icon* in zulip will have to be renamed.
We were not updating the trailing bookend on deactivation of stream
if the user was narrowed to deactivated stream and this commit fixes
this.
For subscribed streams, we just show the trailing bookend with
content as 'This stream has been deactivated' and hide the
Unsubscribe button.
For unsubscribed streams, we change the content of trailing bookend
to 'This stream has been deactivated' and hide the Subscribe button.
Fixes#15999.
This completes the remaining work required to support
addition of all members of another stream.
This allows the creation of stream pills on pasting
the #streamname and copying it from the stream pill.
The user pills uses email ids instead.
And also allows creating stream pills when the user
hides the typeahead.
Tested by commenting out the "set_up_typeahead_on_pills"
line in `stream_edit.js`.
A `node_tests/stream_pill.js` file has been created
for the node tests and the other half of the coverage
check takes place in `node_tests/stream_edit.js`.
This fixes the regression introduced in the pervious
commit to regain the 100% line coverage in `user_pill.js`
as well as `stream_pill.js`.
The new `stream_edit.js` mainly tests for:
* The stream related queries of the typeahead in `user_pill.js`
* The "Add subscribers" event handlers.
* The event handler which displays the settings for a stream.
Now that all casper tests have been migrated to
puppeteer, there's no need for having casper
related things.
Removed the casperjs package and removed/replaced
casper in few places with puppeteer.
Only removed few of them which I'm confident
about. Also didn't make any changes in docs
as it would be easier to remove them while
adding puppeteer docs.
check_messages_test should have table set to zfilt
instead of zhome as we are narrowed to only starred
messages.
Found it as the test failed with the previous commit.
The next adds a few tests which heavily rely on
check_messages_sent. There were some weird errors,
this fixed those. I think the errors were due to
us navigating multiple times and this function
not waiting for the messages to become visible.
These `waitForSelector`s appear just after page loads.
Though they worked most of the times, a few clicks weren't
getting registered because of these selectors not appearing
and thus causing flakes as the modal takes time to appear.
Adding visible: true asserts that it's visible and not just present.
In few rare cases the click on display settings section wasn't working
which was causing the test to stay in the "your account" settings section.
This lead to a waitFor fail.
The screenshot in this failed CircleCI build suggests the above.
https://app.circleci.com/pipelines/github/chdinesh1089/zulip/525/workflows/cd77e269-6a3e-4283-b765-d1c4584ccf35/jobs/1807/artifacts
This and the previous commit along with the changes to prevent
logging out of user on changing password were tested by running
this test 1200 times and all of them passed!
This handles a rare race condition that occurs when the session hash
is not updated by the backend during the password change process.
This mostly occurs in puppeteer tests, but could occur to a user.
In list_render.js, [...list] requires list to be an array, and
widget.set_sorting_function(...opts.init_sort) requires init_sort to
be an array.
This allows the Node tests to pass in Babel strict mode. We currently
use loose mode for performance, and so we should test in loose mode as
well; but we must never depend on loose mode for correctness, since
individual Babel transformations may stop being applied as our browser
support baseline improves.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
Clicking on the copy-to-clipboard button triggers the clipboard.js
API to dynamically set the text to be copied. This text is the
actual code content from the sibling <code> element (extracted
though jQuery text() method).
The html structure would now look like:
<div class="codehilite">
<pre>
<button> The copy button </button>
<span></span>
<code>......</code>
</pre>
</div>
Additionally, this preserves the original code formatting of
the codeblock during copy-paste.
Tests amended.
Fixes: #15208
`update_message_flags` events used `operation` instead of `op`, the
latter being the standard field used in other events. So add `op`
field to `update_message_flags` and mark `operation` as deprecated,
so that it can be removed later.
This flake occurs because the Verona dropdown menu couldn't be
clicked, in rare cases, because puppeteer would click it too
quickly before it appears and then fails. To fix this we wait for it
to fully appear and then click it. Around 1000 runs
passed without a failure.
The error the flake caused was:
TimeoutError: waiting for selector
"#org-submit-notifications[data-status="unsaved"]" ...
The dispatch test here really only cares that values
get passed on.
Note that the dispatch code ignores the email field, because
we only send subscription/update events to the user
whose subscription has changed.
This fixes a bug with the original frontend-side implementation for
has: filters, where it would incorrectly not match content in cases
where the message's nesting structure did not have an outer tag.
Bug was introduced in 02ea52fc18.
Fixes#16118.
This commit adds "role" field to the Subscription objects passed to
clients. This is important preparation for being able to work on the
frontend for this feature.
The dispatch for presence is a trivial one-liner,
so the test just makes sure three important parameters
get passed along.
We will eventually want to use the fixtures data in
other presence-related tests, but for now the only
goal is to make it pass the schema checks.
Also add helper functions needed.
`select_item_via_typeahead` has been ported from casper
and is exactly same in puppeteer to as I couldn't find
any better way for that purpose.
I had a misconception with hidden and visible options
and thought `hidden: false` was same as `visible: true`
and other way too.
But `hidden: false` or `visible: false` does nothing
more than checking if the selector exists.
Also, to mention, `visible: false`'s were fixed in
33e19fa7d1
We need to replace 'visible: false' with 'hidden: true' to wait
for elements to get hidden. Using 'visible: false' just checks
whether the selector exists or not and does not check whether
the element is hidden or not.
Since our Webpack config passes pre-minified JS files to
script-loader, they can’t be used as modules. Use the normal
unminified version, letting Webpack minify it and give us source maps.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
We also just make the test express what's actually
happening in the code; we just pass the entire
"exports" section of the event to the settings code
and let it do its thing.
We follow the naming convention.
I also arbitrarily assign the "op" of
"add" to the attachment event, even
though we don't meaningfully test it.
The situation with attachment from the
dispatch test point of view is that
we just want to test that the one line
of code that calls into attachments_ui
(for all three ops) does get dispatched
correctly. We eventually want to get
deeper coverage there, but attachments_ui
wasn't written in the most test-friendly
way. I think it might actually be easy
to fix up attachments_ui to make it a
bit easier to test, but it's out of the
scope of my current PR.
The benefit here is check-node-fixtures
now gives a more concrete plan for
moving schemas to event_schema.py.
We extract test_realm_emojis, and we make
the name of the event more explicit (adding
the __update suffix).
We also add the "op" of "update" here, which
is sort of a quirk of the api, since we don't
actually have alternatives like add/remove,
and therefore the current frontend code doesn't
look at the "op", and thus the original tests
never had to provide a correct value for it.
We move this function from `user_pill.js` to `pill_typeahead.js`.
The function has also been renamed to `set_up`.
The move was made because there are plans to update the pills
typeahead (i.e. to include user-groups/streams in the results).
Thus this function should not belong in `user_pill.js`.
This commit allows skipping over any disabled tabs
that are in the middle when using the left or right
arrow keys.
We also add `enable_tab` to the `components` API.
After the latest message in a stream is deleted, we should update
the max_message_id in the stream.
Removed false comment in message_util.get_messages_in_topic
this method only takes 2ms for 10,000 messages loaded locally.
Fixes#15992.
If the last message of the topic was deleted, we update the stored
message_id in the topic history so that the topic order in topic_list
is updated correctly.
When messages weren't locally echoed,
`wait_for_full_processes_message` fails to assert that the message
is being sent. It was figured out by tabbott that the messages
aren't locally echoed because of content no loading completely.
So, this commit changes the `log_in` function to wait till
the selector '#zhome .message_row' is visible which indicates
that the cotent is loaded.
Removed the waitForNavigation in the `log_in` as it would
become a redundant check after this change.
Also removed this same check present in 03-compose.js which
becomes reduntant as we already do it in `log_in`.
This test changes user password causing all subsequent
tests to fail. Since rechanging the password would be
a redudant test/task or having manually entering the
new password in every test after this aren't good ideas,
this commit makes the settings test run in the end by
renaming it be numbered 16. It is assumed that we'll
end up having 16 tests seeing the number of tests in
casper and considering 03 and 02 from casper are being
combined as 02-message-basics.js. Though 03 of casper
has not yet been added in message-basics test it will
soon be added.
ES and TypeScript modules are strict by default and don’t need this
directive. ESLint will remind us to add it to new CommonJS files and
remove it from ES and TypeScript modules.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
Commit 114cc1ec25 (#15949) introduced a
subtle bug because sortablejs provides both a CJS module and an ES
module that expose different interfaces to CJS require() under
Webpack. This difference will disappear when we convert
settings_profile_fields to an ES module.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
Prior to commit eb4a2b9d4e the center
area of the navbar was based on a structure that appended crumbs or
"tabs" as <li>s, forming a tab_bar and a tab_list.
However, in eb4a2b9d4e we apply a new
style and structure to the navbar which lets go of the convention of
tabs. Hence, we'd like to purge the tab_bar and tab_list labels from
our code base.
We purged tab_list in 1267caf5009118875f47fdafe312880af08024e1.
This commit purges tab_bar, it includes:
- A blanket search and replace of tab_bar with message_view_header.
- Splitting a single line comment in
tab_bar.js / message_view_header.js.
- The renaming of tab_bar.js to message_view_header.js.
- The renaming of tab_bar.hbs to message_view_header.hbs.
- A blanket search and replace of tab_data with
message_view_header_data.
- Replacing the single occurrence of tabbar with message_view_header
(it was within a comment.)
There were a lots of flakes in CI recently because typeahead didn't
appear when Enter was pressed and real emails are not accepted as
valid inputs. To fix this we wait for typeahead to appear and then
click that instead of Enter. We also use delay option to type the
email (100ms delay between keypresses) since without we'd also get
flakes.
Re-enable puppeteer test in CI after this fix too.
Improved markup of help-text.
Showing Email as plain-text instead of disabled input.
Changed page heading to 'Create your organization' in realm creation form
and 'Create your account' in normal signup form.
Grouped org settings and user settings with fieldsets.
Reduced space between Password field and Password strength bar.
Also, updated the corresponding test cases.
Partially Fixes: #15750.
We will store list of stream ids to sort streams instead of names.
We have added a compare_function for sorting the list of stream_ids
by comparing stream names.
This change helps us to remove a couple of get_sub calls and using
stream ids instead of name also helps in avoiding bugs caused due
to live update on renaming of stream.
We add a function subscribed_stream_ids which returns an array
of stream ids of all subscribed streams.
This is a prep commit for changing the logic for sorting streams
to store stream ids instead of names.
We should not allow every function who wants to narrow to All
messages to come up with their own method to do so. This
commit makes existing such functions use hashchange library to
do so.
Remove click event on All message button, it already contains
an <a> tag which navigates correctly.
We always use hashchange.go_to_location method now to open the
info_overlay, this makes sure that the url hash are reliable and
hotkeys don't get confused if an overlay is open or not.
We don't want to change hash to "" (this also doesn't navigates
us to 'All messages' view, hence the bug was not noticed.) on
exit of info_overlay.
Note that require("moment") and require("moment-timezone") resolve to
the same thing, but the latter adds timezone support as a side effect.
So I went with the latter in every file where .tz is used.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
This is done to decouple our message view related update events
from MessageListData as there are plans to create multiple
MessageListData objects. Instead we update the `stored_messages`
which tracks the complete data for all messages.
This just lets us temporarily assign a value
to a field.
Differences with the "override" scheme:
* override only works on globals
* override (when passed in via run_test) will
just automatically clean up at the end of
the function
This is a pretty straightforward conversion.
The bulk of the diff is just changing emoji.js
to ES6 syntax.
There is one little todo that can be deferred
to the next commit--we are now set up to have
markdown.js require emoji.js directly, since
it is no longer on `window`.
We now show before/after, and we don't complicate
our other test that runs in a big loop.
And we take advantage of function injection to
not have to hack into the "real" emoji_codes
structure.
Note that we're simulating the missing emojis
at a slightly higher level, but we already had test
coverage that emoji.get_emoji_name returns
undefined for unknown codepoints.
The main thing here is that we check that the
actual data got put into our data structures.
(In general we want to move away from stubbing
data modules; any place where we stub data modules
is a relic of earlier days, where we were just
trying to set the bar for 100% line coverage,
even though some of the original coverage was
quite shallow.)
I also use real stubs instead of noops for
the calls out to UI-oriented modules.
In passing I tweak some comments in the actual
dispatch code.
With update_emojis, it is pretty easy
to set up the tests with reasonable
data without reaching into internal
data structures.
Also, we can begin the process of
sharing the same data with our
dispatch tests (upcoming).
We want to undo overrides in reverse order,
which is important if you override the
same name more than once in the same
function.
Until today the code basically prevented
us from ever using the original implementation
of a name we stubbed, and most of them start
as undefined due to their parent modules
starting with `set_global`.
But I do want this proper, and I introduced
a tiny pitfall today.
There was only one place where we weren't
overriding a function, and the use case there
was fairly unique.
Knowing that we're dealing with only functions
will simplify override and allow us to add
features like detecting spurious stubs.
This forces us to more explicitly document at the
top of the file what dependencies we are stubbing,
plus it's less magical.
Also, we may want to do occasional audits of
set_global to clean up places where we mock
things like stream_data, which are probably just
easier to use the real version of now that we
have cleaner APIs to set up stream data.
The modules most affected by this change are our
dispatch-oriented tests--basically, all the
modules that test handling of Zulip events
plus hotkey.js.
Before we were making it impossible to reuse
the function again (so we were preventing
leaks), but it's fine to just restore the
original function, especially now that some
of our tests have grown bigger.
As of commit 87e72ac8e2 (#15267), we
need to be an owner for some of the tested functionality, not just an
administrator.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
Commit 7c0fa3aefc (#15734) added sample
alert words to the test database, so the Casper test can no longer
assume its alert word is the only one.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
$.fn.typeahead, on the other hand, returns the jQuery object back (not
the Typeahead object, which also happens to have a select method), so
this should be converted.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
Zulip converts :) to the 1F642 Unicode emoji and promotes the same emoji
in the popular section of the emoji picker.
Previously Zulip has labeled 1F642 as "slight smile". While that name
conforms to the Unicode standard (which describes the code point as
SLIGHTLY SMILING FACE), it didn't match our use case of the emoji.
If a user types :) or selects the first smile in the emoji picker they
probably mean to express a regular "smile" and not a "slight smile",
which raises the question why they are only smiling slightly.
This commit relabels 1F642 as 😄 and our previous 😄 263A as
:smiling_face:. Note that 263A looks different in our three supported
emoji sets, so it is not suited to be our "default smile".
This change does not require a migration since our emoji system stores
both unicode points and names and handles name changes transparently.
Previously, image upload widget delete button CSS class name was
`settings-page-delete-button`.
We can change the CSS class name to `image-delete-button`
so that the name can be more generic.
Prettier would do this anyway, but it’s separated out for a more
reviewable diff. Generated by ESLint.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
Prettier would do this anyway, but it’s separated out for a more
reviewable diff. Generated by ESLint.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
Prettier would do this anyway, but it’s separated out for a more
reviewable diff. Generated by ESLint.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
Prettier would do this anyway, but it’s separated out for a more
reviewable diff. Generated by ESLint.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
We hide the spoiler content in browser/desktop notifications.
Note: its not worth adding zjquery tests for this bit of code because
the tests do not operate on the actual data and are likely to get stale
if we change the syntax for spoilers.
This handler adds a neat little effect whereby hovering over the
clickable region to open the navbar triggers the search_icon hover
effect and is a neat little visual cue about what happens onClick.
The previous implementation was slightly messy because it fetched the
color and applied it via ".css(". This commit cleans it up by creating
and using the class "search_icon_hover_highlight" instead. We also
make the selectors more specific, ensuring they target children of
"#tab_bar", this was so because it was reasonable to expect someone to
define eg `search_closed` elsewhere and we wanted to prevent bugs when
that happened.
We were not passing any arguments to needs_subscribe_warning
when testing the function itself.
This commit changes the code to pass the user-id and stream-id
to needs_subscribe_warning. We also remove the stubs for
get_by_user_id and is_user_subscribed and do these tests by
calling the original functions, because passing the arguments
(user-id and stream-id) only makes sense if we use original
functions for them rather than stubs.
Delete stored topic data in `recent_senders` and `recent_topics`
about the message's topics and re-render them. The process is similar
to topic editing. See `recent_senders.process_topic_edit` for
logical details.
We have changed our all instances of list_render to use
simplebar and thus, we will now use simplebar container
to track scroll event for all the lists created by
list_render.
This fixes the bug of new subscribers not rendering on
scrolling at the end of subscriber list in stream settings
and similar bug in some other lists also.
This commit also removes scroll_util.get_list_scrolling_container
function as this is no longer used.
Fixes#15637.
This reverts commit 63643c9d9d.
As the commit mentions, it makes a UI change for legacy search which
has largely been considered a regression. We've been running with
this reverted in zulip.com essentially since it was first merged.
Prior to commit eb4a2b9d4e the center
area of the navbar was based on a structure that appended crumbs or
"tabs" as <li>s, forming a tab_bar and a tab_list.
However, in eb4a2b9d4e we apply a new
style and structure to the navbar which lets go of the convention of
tabs. Hence, we'd like to purge the tab_bar and tab_list labels from
our code base.
It would have been nicer if we could simply purge tab_bar from the
codebase and rename "#tab_list" so that we have an anchor and wrapper
structure in the html, but dropping the float: left on tab_bar causes
some confusing problems such as causing the horizontal border to
disappear and the search_box to shift out of its intended position and
so its simpler to get rid of tab_list from our code base first.
This commit:
- Removes the #tab_list wrapper div from tab_bar.hbs.
- Removes any #tab_list selectors from night_mode.scss so that they
simply target based on "#tab_bar" instead of "#tab_bar #tab_list".
- Removes tab_list selectors from zulip.scss, so that #tab_list
attributes now apply to the #tab_bar, in the process we drop the
duplicated width property and reorder the attributes.
- Replaces all mention of #tab_list with #tab_bar in JS files.
Previously, the navbar sub count would not live update as users
subscribed or unsubscribed, this commit adds the relevant calls in
stream events.
It would have been better to just have a single call within
server_events_dispatch but it seems difficult due to the way of
mark_subscribed and mark_unsubscribed are structured.
stream_events.mark_unsubscribed conditionally calls
subs.update_settings_for_unsubscribed which calls
subs.rerender_subscriptions_settings and as such handles the update
for the subscriptions modal on its own. Hence, we simply rely on the
stream_data.update_calculated_fields to ensure the subscriber counts
are updated and make a call to
tab_bar.maybe_rerender_title_area_for_stream(sub).
stream_events.mark_subscribed is similar.
Previously, we were overriding narrow_state.is_for_stream_id() to make
sure we test the functions we intend to.
In this commit we:
* zrequire('narrow_state')
* set the filter to "stream:frontend" before the test cases which
were overriding is_for_stream_id to return true (and remove the
overrides).
* reset_current_filter() at the end of the above cases (and remove
the lines overriding is_for_stream_id to return false)
This is a prep commit to adding live update for sub_count in the
navbar.
This commit changes stream_data.is_user_subscribed to use stream id
instead of stream name.
We are using stream ids so that we can avoid bugs related to live
update after stream rename.
We have logic in place to update the ui for re-sending messages
on recieving the acknowledgement from the server on that API call.
However, if the acknowledgement is recieved through the get events
request before the `on_success` of `resend_message`, the message
gets re-rendered allowing the failed message actions to be clickable.
Now, we update the ".message_failed" ui for both cases. This helps
in preventing the "Trying to get local_id from row that has reified
message id" exception.
Fixes#15351.
As we add more features where rendered_markdown.update_elements does
something useful, it'll become important to run this code everywhere
we render markdown in the DOM.
One can see in this case that we had actually copied one hunk of
rendered_markdown.update_elements years ago, before we extracted it as
an independent function; we get to delete that copy.
Fixes#15500.
This particular commit has been a long time coming. For reference,
!avatar(email) was an undocumented syntax that simply rendered an
inline 50px avatar for a user in a message, essentially allowing
you to create a user pill like:
`!avatar(alice@example.com) Alice: hey!`
---
Reimplementation
If we decide to reimplement this or a similar feature in the future,
we could use something like `<avatar:userid>` syntax which is more
in line with creating links in markdown. Even then, it would not be
a good idea to add this instead of supporting inline images directly.
Since any usecases of such a syntax are in automation, we do not need
to make it userfriendly and something like the following is a better
implementation that doesn't need a custom syntax:
`![avatar for Alice](/avatar/1234?s=50) Alice: hey!`
---
History
We initially added this syntax back in 2012 and it was 'deprecated'
from the get go. Here's what the original commit had to say about
the new syntax:
> We'll use this internally for the commit bot. We might eventually
> disable it for external users.
We eventually did start using this for our github integrations in 2013
but since then, those integrations have been neglected in favor of
our GitHub webhooks which do not use this syntax.
When we copied `!gravatar` to add the `!avatar` syntax, we also noted
that we want to deprecate the `!gravatar` syntax entirely - in 2013!
Since then, we haven't advertised either of these syntaxes anywhere
in our docs, and the only two places where this syntax remains is
our game bots that could easily do without these, and the git commit
integration that we have deprecated anyway.
We do not have any evidence of someone asking about this syntax on
chat.zulip.org when developing an integration and rightfully so- only
the people who work on Zulip (and specifically, markdown) are likely
to stumble upon it and try it out.
This is also the only peice of code due to which we had to look up
emails -> userid mapping in our backend markdown. By removing this,
we entirely remove the backend markdown's dependency on user emails
to render messages.
---
Relevant commits:
- Oct 2012, Initial commit c31462c278
- Nov 2013, Update commit bot 968c393826
- Nov 2013, Add avatar syntax 761c0a0266
- Sep 2017, Avoid email use c3032a7fe8
- Apr 2019, Remove from webhook 674fcfcce1