With chromedriver >= 80, the tests are freezing sometimes, particularly
when the same editor is loaded again.
We don't know whether it's a CKEditor issue or a chromedriver issue. In
the past we've had some errors related to CKEditor trying to load the
same instance twice and we aren't sure they have been fixed since we
could never reproduce them.
It could be a coincidence, though. If we modify the views so the only
content of the `<body>` tag is a textarea with the `html-area` class,
chromedriver freezes even if we only access the page once. So maybe
we're only detecting the problem on the second visit because the second
request is faster than the first one.
Since chromedriver no longer hangs after this change, we don't have to
force any chromedriver version anymore.
After upgrading to chromedriver 80, tests checking CKEditor's content
were causing chromedriver to hang. That's why we were configuring
webdrivers to use an older chromedriver.
Version 80 of chromedriver introduced several issues regarding frames.
Debugging shows in this case chromedriver froze when we used `setData`
and then `within_frame`. Since adding a `sleep` call made it work, we
think `within_frame` was being executed before `setData` had finished.
The fact that `setData` causes the browser to enter the frame having
CKEditor is probably the reason.
Even though the `setData` method provides a callback when it's finished,
configuring it so the rest of the Ruby code isn't executed until that
happens leads to complex code. Using Capybara's `set` to fill in the
editor is IMHO a bit easier to understand.
After this change, since we're using a method provided by Capybara
instead of executing asynchronous JavaScript code, we don't have to
check CKEditor has been filled anymore. The "Admin Active polls add"
test, which failed on my machine without that check, now passes.
If we don't use the `exact` option, tests will pass even if filling in
CKEditor adds the content twice or adds the new content to the existing
content, which has actually happened and has gone mostly unnoticed while
testing several ways to fill in CKEditor with Capybara (particularly,
when using Capybara's `send_keys` method). The problem was detected by
just one test, which checked the original content wasn't present anymore
after updating a record.
When a user replies to a comment whose responses was hidden at the
moment of reply form submission and although the reply were correctly
added to the DOM it was hidden because was added to a collapsed list.
This solution is about showing all responses of parent comment after adding
the new comment to the DOM so the user can see new reply into the screen.
(This is not applicable to root comments which cannot be collapsed)
Extract the needed portion of code to a new partial to be able to update
only the elements needed when a new comment is added keeping UI properly
updated.
In this case the confirmation dialog isn't really necessary since the
action to enable/disable the setting can easily be undone.
Furthermore, these tests were failing with Chrome 83, probably because
we use `confirm_dialog` and then we use `visit` without checking the
page in between.
In theory we shouldn't need to check the page in between because the
request generated by `confirm_dialog` is a synchronous one and so
`visit` isn't executed after the previous request has finished, but
apparently this behavior has changed in Chrome 83.
We could add an expectation before executing the `visit` method, but
that wouldn't improve the usability of the application.
The latest stable version is causing problems on some machines, hanging
forever in tests involving frames. So we're installing an old version
which works with the latest Chrome.
Note this means we're using an unsupported version. Officially, only the
latest chromedriver supports the latest Chrome.
We're using 2.38 instead of a more recent one (like 2.40) because it's
the one we specified in our Dockerfile.
See also:
https://bugs.chromium.org/p/chromedriver/issues/detail?id=3361
The number of errors in a form includes several errors for the same
field. For example, if a title is mandatory and has to have at least 5
characters, leaving the title blank will result in two errors. So users
will be invited to look for two errors, but they'll only find one field
with errors.
So it's a bit more intuitive to show as many errors as fields having
errors.
Note we're excluding errors on `:base`, which is a bit of a hack for
errors in association fields. For example, if the title of one
translation is not present, `resource.errors.messages` will contain two
elements: one for the translation's title, and one for the `base` field.
This resulted in the count of errors being 2 when there was only one.
Also note I haven't found a way to count errors on all `has_many`
relations. That is, if two translations have a missing title field, only
one error will be mentioned in the message (as it did before this
commit).
We were submitting the form without checking the AJAX request to attach
the image had finished, so sometimes two requests were executed at the
same time. Sometimes this made InvisibleCaptcha to go crazy and report
the form was submitted too quickly.
Checking the first AJAX request has finished before submitting the form
solves the problem.
We were checking `expect_document_has_title(0, "My Title")`, which was
already true before the AJAX request generated by `attach_file` had
finished.
That meant the AJAX request sometimes was handled after this test had
finished, affecting the following test and causing it to fail because
its cookie was overwritten and so `current_user` was set to `nil`.
In the test checking the filename is present, a similar scenario was
taking place: we were updating the `.file-name` element in the `change`
event of `fileupload` (using `App.Documentable.setFilename`); that is,
when the AJAX request started. And so the test passed before the request
was finished, causing the same issue.
We were using inline styles and passing local variables around, while
the rule we were following is very simple: it's only hidden if it's a
form to reply to a comment.
We were using a <ul> tag for a single comment, where the first element
of the list was the comment itself and the second element was the list
of replies.
IMHO it makes more sense to have a list of all comments, where every
element is a comment and inside it there's a list of replies.
We're also rendering the list even if it has no children so it's easier
to add comments through JavaScript. Then we use the :empty CSS selector
to hide the list if it's empty. However, since ERB adds whitespace if we
structure our code the usual way and current browsers don't recognize
elements with whitespace as empty, we have to use the `tag` helper so no
whitespace is added.
Tests are also a bit easier to read, even though we need to use the
`text:` option to find links because otherwise the text in the hidden
`<span>` tags will cause `click_link` to miss the link we want to click.
Here's an explanation by one of Capybara's authors:
https://github.com/teamcapybara/capybara/issues/2347#issuecomment-626373440
We were passing around many variables to condition the way we display
the comment. However, in the end we only had one place where these
variables were used: valuation. So we can make everything depend on the
valuation variable.
It was created in commit 83d254ad, but it was never used, since the
commit creating it removed the code rendering the
`budgets/investments/comments` partial, which this partial was supposed
to replace.
When a page with ckeditor is restored from browser cache by using browser
history back feature application was trying to re-initialize it but this was
throwing some javascript errors that left ckeditor useless. The ckeditor user
interface seemed to be loaded correctly but editor contents was not shown
and ckeditor locked.
This solution is about destroying all ckeditor instances on page before
leaving it and force the reinitialization after Turbolinks restored the cache.
Inspiration here [1].
There is a similar patch to make it work with Turbolinks 5.x versions [2].
[1] https://github.com/galetahub/ckeditor/issues/575#issuecomment-132757961
[2] https://github.com/galetahub/ckeditor/issues/575#issuecomment-241185136
We were calling initialize_modules after every ajax call only to apply the
application javascript to new elements added though ajax calls, this is not
always needed because some ajax calls do not add new elements to the user
interface so there is no new elements to initialize. This technique was working
fine in most cases but was causing different kind of problems at some pages where
some elements where being unnecessarily reinitiliazed causing the execution of the
element associated scripts as many times as the element was initialized. This fix
should relief a little bit of work to users browsers.
After this change we had to fix some pieces of javascript:
Regarding LegistationAnnotatable module, since we are not re-initializing Annotator
app we cannot destroy it so now we only need to insert the new annotation into annotator
interface to keep it properly updated.
Regarding Comments module, we do not need anymore the initialization check because this
code only will be fired once now we do not launch application initialization after ajax
calls. Also, when a new comment is created its added to the DOM through AJAX and include
some elements that needs javascript initialization to work. By using
"Delegated event handlers" [1] new comments will be initialized automatically when added.
[1] https://api.jquery.com/on/#direct-and-delegated-events
The method `available_locales` in
`RemoteTranslations::Microsoft::AvailableLocales` needs to execute a
request to an external server in order to work, meaning it will fail if
the machine its executed on doesn't have an internet connection.
So we're stubbing the method in the tests using it.
We were evaluating its value when the server starts. On production
enviroments, that could mean weeks or months before the available
locales are checked again, and so it would be possible to use a list
which is no longer in sync with the list provided by microsoft.