Why does this commit do?
This commit adds support for sub-subcategories in the new edit sidebar
categories modal added in fc296b9a81. Note
that sub-subcategories are enabled when `max_category_nesting` is set to
`3`.
What this change?
We are currently not fully satisfied with the current way to edit the
categories and tags that appears in the sidebar where the user is
redirected to the tracking preferences tab in the user's profile causing
the user to lose context of the current page. In addition, the dropdown
to select categories or tags limits the amount of information we can
display.
Since editing or adding a custom categories section is already using a
modal, we have decided to switch editing the categories and tags that
appear in the sidebar to use a modal as well.
This commit ships a first pass of the edit categories modal such that we
can keep the commit small and reviewable. The incomplete nature of the
feature is also reflected in the fact that the feature is hidden behind
a new `new_edit_sidebar_categories_tags_interface_groups` site setting.
One user can create a post or chat message with a hashtag they
have permission to use, but then when other users look at that
post they will see an empty space next to the hashtag because they
do not have the permission to load the colors in CSS classes for
the related category.
This fixes the issue by adding a default color with a special
CSS class if the user doesn't have permission to see the linked
channel/category on the hashtag.
Display modal for combined new and unread view with options:
- [x] Dismiss new topics
- [x] Dismiss new posts
- [ ] Stop tracking these topics so they stop appearing in my new list
What is this change required?
We were seeing this error on CI
```
1) Fast edit when editing text that has strange characters saves when paragraph contains apostrophe
Failure/Error:
expect(find("#{topic_page.post_by_number_selector(2)} .cooked p")).to have_content(
"It ‘twas a great’ “day”!",
)
Selenium::WebDriver::Error::StaleElementReferenceError:
stale element reference: stale element not found
(Session info: chrome=114.0.5735.90)
```
I believe this is because the element that is "found" using `find` is
eventually re-rendered before the `have_content` matcher is called on
it.
Failure message on CI
```
Failures:
1) Network Disconnected Doesn't show the offline indicator when the site setting isn't present
Failure/Error: expect(page).to have_css("html.message-bus-offline")
expected to find css "html.message-bus-offline" but there were no matches
[Screenshot Image]: /__w/discourse/discourse/tmp/capybara/failures_r_spec_example_groups_network_disconnected_doesn_t_show_the_offline_indicator_when_the_site_setting_isn_t_present_764.png
~~~~~~~ JS LOGS ~~~~~~~
http://localhost:31338/uploads/default/test_0/optimized/1X/_129430568242d1b7f853bb13ebea28b3f6af4e7_2_512x512.png - Failed to load resource: net::ERR_INTERNET_DISCONNECTED
http://localhost:31338/categories - Error while trying to use the following icon from the Manifest: http://localhost:31338/uploads/default/test_0/optimized/1X/_129430568242d1b7f853bb13ebea28b3f6af4e7_2_512x512.png (Download error or resource isn't a valid image)
~~~~~ END JS LOGS ~~~~~
# ./spec/system/network_disconnected_spec.rb:35:in `block (3 levels) in <main>'
# ./spec/system/network_disconnected_spec.rb:8:in `with_network_disconnected'
# ./spec/system/network_disconnected_spec.rb:34:in `block (2 levels) in <main>'
# ./spec/rails_helper.rb:380:in `block (3 levels) in <top (required)>'
# ./spec/rails_helper.rb:380:in `block (2 levels) in <top (required)>'
# ./spec/rails_helper.rb:372:in `block (3 levels) in <top (required)>'
# ./vendor/bundle/ruby/3.2.0/gems/timeout-0.3.2/lib/timeout.rb:189:in `block in timeout'
# ./vendor/bundle/ruby/3.2.0/gems/timeout-0.3.2/lib/timeout.rb:196:in `timeout'
# ./spec/rails_helper.rb:367:in `block (2 levels) in <top (required)>'
# ./spec/rails_helper.rb:356:in `block (2 levels) in <top (required)>'
# ./vendor/bundle/ruby/3.2.0/gems/webmock-3.18.1/lib/webmock/rspec.rb:37:in `block (2 levels) in <top (required)>'
```
Before, the review button was shown in `primary section` when there were items to review. Otherwise, it was hidden in `more section`.
Because we are allowing admins to customize community section and reorder link, it makes sense to simplify that logic and review link should follow admin's decision.
## What is the problem?
MessageBus by default uses long polling which keeps a connection
open for 25 seconds by default. The problem here is that Capybara does not know about these
connections being kept opened by MessageBus and hence does not know how
to stop these connections at the end of each test. As a result, the long polling MessageBus connections are kept opened by the browser and we hit chrome's limit of 6 concurrent requests per host, new request made in the browser is marked as "pending" until a request is freed up. Since we keep a MessageBus long polling connection opened for 25 seconds, our finders in Capybara end up hitting Capybara's wait time out causing the tests to fail.
## What is the fix?
Since we can't rely on Capybara to close all the existing Capybara
connections, we manually execute a script to stop all MessageBus
connections after each system test.
```
for i in {1..10}; do
echo "Running iteration $i"
PARALLEL_TEST_PROCESSORS=8 CAPYBARA_DEFAULT_MAX_WAIT_TIME=10 bin/turbo_rspec --seed=34908 --profile --verbose --format documentation spec/system
if [ $? -ne 0 ]; then
echo "Error encountered on iteration $i"
exit 1
fi
done
echo "All 10 iterations completed successfully"
```
Without the fix, the script fails consistently in the first few iterations. Running in non-headless mode with the "network" tab opened will reveal the requests that are marked as pending.
What is the problem?
There are two problems being fixed here:
1. When opening the composer, we are seeing multiple requests made to
the `/composer_messages` endpoint. This is due to our use of the
`transitionend` event on the `#reply-control` element. The event is
fired once for each transition event and the `#reply-control` element
has multiple transition events.
2. System tests have animations disabled so the `transitionend` event
does not fire at all.
What is the solution?
Instead of relying on the `transitionend` event, we can instead just
observer the `composerState` property of the `ComposerBody` component
and trigger the `composer:opened` appEvent with a delay that is similar
to the transition duration used for the `ComposerBody` component.
What is the problem?
Prior to this change, we had a `has_css?(context + ":not(.is-expanded)"`
check when using the select-kit component page object. The problem here
is that this check will end up waiting the full capybara default wait
time if the select-kit has already been expanded. It turns out that we
were calling this check alot of times when the select-kit has already
been expanded resulting in many tests waiting the full default wait
time.
What is the fix?
The fix here is to specify the `wait: 0` option such that we do not wait
and fundamentally, there is no need for us to wait at all here.
Allow admins to edit Community section. This includes drag and drop reorder, change names, delete and reset to default.
Visual improvements introduced in edit community section modal are available in edit custom section form as well. For example:
- drag and drop links to change their position;
- smaller icon picker.
Why is this change required?
The flaky system test was due to the fact that we had to poll for the
user preferences interface page to reload after saving. However, this
turns out to be a bug on the user perferences interface page because the
page should only reload if the user has selected a new theme that is
different from the site's default but we were reloading the page for
users that did not have any user theme selected. Therefore there was an
unnecessary reload happening when saving other fields on the user
preferences interface page.
The fix use the SelectKit component in the spec and improves reliability of SelectKit component to ensure expanded/collapsed state effectively set/present.
Multiple lines have also been removed as they are not necessary, eg:
- check button is present
- click button
The check is un-necesssary as we won't find the button on click if not present. This kind of checks are only necessary when another element has to be present before the button is show, eg:
- check modal is displayed
- click button
FInally this commit changes the way the SelectKit component initializes component and now uses a css selector instead of a finder, it ensures we are always getting fresh object and allows to build complete selectors: ".context[data-id=1].foo:enabled"
Watching screenshots of failures it appears that sometimes the reply was stuck at: `this is a #` due to the autocomplete showing, given we only need to send a reply here? I think fill_in + click send should be more reliable here.
Note I also tweaked `send_reply` to accept a content param and use it to fill composer when given.
Page settings can be very long and the setting can be way out of viewport. This should ensure the setting can be found and clicked.
This was for example causing this flakey in discourse-topic-voting:
```
Failures:
1) Voting enables voting in category topics and votes
Failure/Error: find(".edit-category-tab .#{setting} label.checkbox-label", text: text).click
Capybara::ElementNotFound:
Unable to find css ".edit-category-tab .enable-topic-voting label.checkbox-label"
[Screenshot Image]: /__w/discourse/discourse/tmp/capybara/failures_r_spec_example_groups_voting_enables_voting_in_category_topics_and_votes_873.png
~~~~~~~ JS LOGS ~~~~~~~
http://localhost:31341/favicon.ico - Failed to load resource: the server responded with a status of 404 (Not Found)
~~~~~ END JS LOGS ~~~~~
# ./vendor/bundle/ruby/3.2.0/gems/capybara-3.39.1/lib/capybara/node/finders.rb:312:in `block in synced_resolve'
# ./vendor/bundle/ruby/3.2.0/gems/capybara-3.39.1/lib/capybara/node/base.rb:84:in `synchronize'
# ./vendor/bundle/ruby/3.2.0/gems/capybara-3.39.1/lib/capybara/node/finders.rb:301:in `synced_resolve'
# ./vendor/bundle/ruby/3.2.0/gems/capybara-3.39.1/lib/capybara/node/finders.rb:60:in `find'
# ./vendor/bundle/ruby/3.2.0/gems/capybara-3.39.1/lib/capybara/session.rb:773:in `find'
# ./vendor/bundle/ruby/3.2.0/gems/capybara-3.39.1/lib/capybara/dsl.rb:52:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/capybara-3.39.1/lib/capybara/dsl.rb:52:in `find'
# ./spec/system/page_objects/pages/category.rb:34:in `toggle_setting'
# ./plugins/discourse-topic-voting/spec/system/voting_spec.rb:39:in `block (2 levels) in <main>'
# ./spec/rails_helper.rb:368:in `block (3 levels) in <top (required)>'
# ./vendor/bundle/ruby/3.2.0/gems/timeout-0.3.2/lib/timeout.rb:189:in `block in timeout'
# ./vendor/bundle/ruby/3.2.0/gems/timeout-0.3.2/lib/timeout.rb:36:in `block in catch'
# ./vendor/bundle/ruby/3.2.0/gems/timeout-0.3.2/lib/timeout.rb:36:in `catch'
# ./vendor/bundle/ruby/3.2.0/gems/timeout-0.3.2/lib/timeout.rb:36:in `catch'
# ./vendor/bundle/ruby/3.2.0/gems/timeout-0.3.2/lib/timeout.rb:198:in `timeout'
# ./spec/rails_helper.rb:364:in `block (2 levels) in <top (required)>'
# ./spec/rails_helper.rb:356:in `block (2 levels) in <top (required)>'
# ./vendor/bundle/ruby/3.2.0/gems/webmock-3.18.1/lib/webmock/rspec.rb:37:in `block (2 levels) in <top (required)>'
Finished in 7 minutes 18 seconds (files took 0 seconds to load)
445 examples, 1 failure, 17 pending, 1 error occurred outside of examples
Failed examples:
rspec ./plugins/discourse-topic-voting/spec/system/voting_spec.rb:27 # Voting enables voting in category topics and votes
```
In tests we could attempt to click the preview button when it was not enabled yet, causing a noop and a future failure. This fix ensures we try to find (and wait) for an enabled button.
This should help with this specific flakey:
```
Failures:
1) Admin Customize Form Templates when visiting the page to create a new form template should render all the input field types in the preview
Failure/Error: find(".form-template-field__#{type}").present?
Capybara::ElementNotFound:
Unable to find css ".form-template-field__input"
[Screenshot Image]: /__w/discourse/discourse/tmp/capybara/failures_r_spec_example_groups_admin_customize_form_templates_when_visiting_the_page_to_create_a_new_form_template_should_render_all_the_input_field_types_in_the_preview_277.png
~~~~~~~ JS LOGS ~~~~~~~
http://localhost:31339/favicon.ico - Failed to load resource: the server responded with a status of 404 (Not Found)
~~~~~ END JS LOGS ~~~~~
# ./vendor/bundle/ruby/3.2.0/gems/capybara-3.39.1/lib/capybara/node/finders.rb:312:in `block in synced_resolve'
# ./vendor/bundle/ruby/3.2.0/gems/capybara-3.39.1/lib/capybara/node/base.rb:84:in `synchronize'
# ./vendor/bundle/ruby/3.2.0/gems/capybara-3.39.1/lib/capybara/node/finders.rb:301:in `synced_resolve'
# ./vendor/bundle/ruby/3.2.0/gems/capybara-3.39.1/lib/capybara/node/finders.rb:60:in `find'
# ./vendor/bundle/ruby/3.2.0/gems/capybara-3.39.1/lib/capybara/session.rb:773:in `find'
# ./vendor/bundle/ruby/3.2.0/gems/capybara-3.39.1/lib/capybara/dsl.rb:52:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/capybara-3.39.1/lib/capybara/dsl.rb:52:in `find'
# ./spec/system/page_objects/pages/form_template.rb:55:in `has_input_field?'
# ./spec/system/admin_customize_form_templates_spec.rb:114:in `block (3 levels) in <main>'
# ./spec/rails_helper.rb:368:in `block (3 levels) in <top (required)>'
# ./vendor/bundle/ruby/3.2.0/gems/timeout-0.3.2/lib/timeout.rb:189:in `block in timeout'
# ./vendor/bundle/ruby/3.2.0/gems/timeout-0.3.2/lib/timeout.rb:36:in `block in catch'
# ./vendor/bundle/ruby/3.2.0/gems/timeout-0.3.2/lib/timeout.rb:36:in `catch'
# ./vendor/bundle/ruby/3.2.0/gems/timeout-0.3.2/lib/timeout.rb:36:in `catch'
# ./vendor/bundle/ruby/3.2.0/gems/timeout-0.3.2/lib/timeout.rb:198:in `timeout'
# ./spec/rails_helper.rb:364:in `block (2 levels) in <top (required)>'
# ./spec/rails_helper.rb:356:in `block (2 levels) in <top (required)>'
# ./vendor/bundle/ruby/3.2.0/gems/webmock-3.18.1/lib/webmock/rspec.rb:37:in `block (2 levels) in <top (required)>'
```
In tests we could attempt to click the button before it was enabled and it could also not be visible on screen. This commits attempts to solve both problems.
It should solve this failure:
```
Failures:
1) Admin Customize Form Templates when visiting the page to create a new form template should allow admin to create a new form template
Failure/Error: find(".form-templates__table tbody tr td", text: name).present?
Capybara::ElementNotFound:
Unable to find css ".form-templates__table tbody tr td"
[Screenshot Image]: /__w/discourse/discourse/tmp/capybara/failures_r_spec_example_groups_admin_customize_form_templates_when_visiting_the_page_to_create_a_new_form_template_should_allow_admin_to_create_a_new_form_template_911.png
~~~~~~~ JS LOGS ~~~~~~~
http://localhost:31339/favicon.ico - Failed to load resource: the server responded with a status of 404 (Not Found)
~~~~~ END JS LOGS ~~~~~
# ./vendor/bundle/ruby/3.2.0/gems/capybara-3.39.1/lib/capybara/node/finders.rb:312:in `block in synced_resolve'
# ./vendor/bundle/ruby/3.2.0/gems/capybara-3.39.1/lib/capybara/node/base.rb:84:in `synchronize'
# ./vendor/bundle/ruby/3.2.0/gems/capybara-3.39.1/lib/capybara/node/finders.rb:301:in `synced_resolve'
# ./vendor/bundle/ruby/3.2.0/gems/capybara-3.39.1/lib/capybara/node/finders.rb:60:in `find'
# ./vendor/bundle/ruby/3.2.0/gems/capybara-3.39.1/lib/capybara/session.rb:773:in `find'
# ./vendor/bundle/ruby/3.2.0/gems/capybara-3.39.1/lib/capybara/dsl.rb:52:in `call'
# ./vendor/bundle/ruby/3.2.0/gems/capybara-3.39.1/lib/capybara/dsl.rb:52:in `find'
# ./spec/system/page_objects/pages/form_template.rb:21:in `has_form_template?'
# ./spec/system/admin_customize_form_templates_spec.rb:71:in `block (3 levels) in <main>'
# ./spec/rails_helper.rb:381:in `block (3 levels) in <top (required)>'
# ./vendor/bundle/ruby/3.2.0/gems/timeout-0.3.2/lib/timeout.rb:189:in `block in timeout'
# ./vendor/bundle/ruby/3.2.0/gems/timeout-0.3.2/lib/timeout.rb:36:in `block in catch'
# ./vendor/bundle/ruby/3.2.0/gems/timeout-0.3.2/lib/timeout.rb:36:in `catch'
# ./vendor/bundle/ruby/3.2.0/gems/timeout-0.3.2/lib/timeout.rb:36:in `catch'
# ./vendor/bundle/ruby/3.2.0/gems/timeout-0.3.2/lib/timeout.rb:198:in `timeout'
# ./spec/rails_helper.rb:377:in `block (2 levels) in <top (required)>'
# ./spec/rails_helper.rb:369:in `block (2 levels) in <top (required)>'
# ./vendor/bundle/ruby/3.2.0/gems/webmock-3.18.1/lib/webmock/rspec.rb:37:in `block (2 levels) in <top (required)>'
```
This commit also includes two changes to the rails helper which make tests more consistent on different devices. With this change the failure was reproducible locally and not only on CI:
```
options.add_argument("--force-device-scale-factor=1")
```
The fix itself is quite simple and attempts to find safe click coordinates, the previous solution could fail depending on the size of the sidebar.
This commit makes some fundamental changes to how hashtag cooking and
icon generation works in the new experimental hashtag autocomplete mode.
Previously we cooked the appropriate SVG icon with the cooked hashtag,
though this has proved inflexible especially for theming purposes.
Instead, we now cook a data-ID attribute with the hashtag and add a new
span as an icon placeholder. This is replaced on the client side with an
icon (or a square span in the case of categories) on the client side via
the decorateCooked API for posts and chat messages.
This client side logic uses the generated hashtag, category, and channel
CSS classes added in a previous commit.
This is missing changes to the sidebar to use the new generated CSS
classes and also colors and the split square for categories in the
hashtag autocomplete menu -- I will tackle this in a separate PR so it
is clearer.
* FIX: Fix for Default to subcategory when parent category does not allow posting
* added tests for edge case scenario
* implemented correct behaviour when parent category doesn't have subcategories
* implemented new fabricator for categories and suggested changes
added site toggle functionality through site settings
added tests to implemented feature
Introduced suggested correction
renamed find_new_topic method and deleted click_new_topic_button method
What is the problem?
We are relying on RSpec custom matchers in system tests by defining
predicates in page objects. The problem is that this can result in a
system test unnecessarily waiting up till the full duration of
Capybara's default wait time when the RSpec custom matcher is used with
`not_to`. Considering this topic page object where we have a `has_post?`
predicate defined.
```
class Topic < PageObject
def has_post?
has_css?('something')
end
end
```
The assertion `expect(Topic.new).not_to have_post` will end up waiting
the full Capybara's default wait time since the RSpec custom matcher is
calling Capybara's `has_css?` method which will wait until the selector
appear. If the selector has already disappeared by the time the
assertion is called, we end up waiting for something that will never
exists.
This commit fixes such cases by introducing new predicates that uses
the `has_no_*` versions of Capybara's node matchers.
For future reference, `to have_css` and `not_to have_css` is safe to sue
because the RSpec matcher defined by Capbyara is smart enough to call
`has_css?` or `has_no_css?` based on the expectation of the assertion.
* DEV: move sidebar community section to database
Before, community section was hard-coded. In the future, we are planning to allow admins to edit it. Therefore, it has to be moved to database to `custom_sections` table.
Few steps and simplifications has to be made:
- custom section was hidden behind `enable_custom_sidebar_sections` feature flag. It has to be deleted so all forums, see community section;
- migration to add `section_type` column to sidebar section to show it is a special type;
- migration to add `segment` column to sidebar links to determine if link should be displayed in primary section or in more section;
- simplify more section to have one level only (secondary section links are merged);
- ensure that links like `everything` are correctly tracking state;
- make user an anonymous links position consistence. For example, from now on `faq` link for user and anonymous is visible in more tab;
- delete old community-section template.
SearchIndexer is only automatically disabled in `before_all` and `before` blocks which means at the start
of test runs. Enabling the SearchIndexer in one `fab!` block will affect
all other `fab!` blocks which is not ideal as we may be indexing stuff
for search when we don't need to.
What is the problem?
The system tests incorrectly assumes that the discobot user which is
seeded by a core plugin will always be present. This is not true as the
discobot user will only be seeded when the test databases are migrated
with plugins enabled. If we migrate test databases without plugins being
enabled, the core system tests should still pass.
The value field of ThemeField is only used when viewing a diff in the staff action logs and local theme editing. value is being serialized into the theme index as well, which is not used. It's a huge amount of JSON that we can cut by removing it.
This also breaks up the various theme serializers into separate classes so they autoload properly (or at least restart the server on edit)