Skip to content

Migrate Turbolinks to Turbo #2998

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Jul 9, 2025
Merged

Migrate Turbolinks to Turbo #2998

merged 19 commits into from
Jul 9, 2025

Conversation

MrSerth
Copy link
Member

@MrSerth MrSerth commented Jul 2, 2025

This is the first part of using Turbo for this code base. Some solutions, like the temporary events triggered, should be temporary and removed once we fully migrated from Sprockets to another JavaScript distribution (such as Shakapacker, importmaps, etc.). More details are available in the dedicated commit messages. Related to #2952 and #2953. While I have tested the changes extensively, handling of the JavaScript events is not that easy and might require additional refinements later (based on Sentry events or other observations).

@MrSerth MrSerth self-assigned this Jul 2, 2025
@MrSerth MrSerth added dependencies Pull requests that update a dependency file javascript Pull requests that update Javascript code labels Jul 2, 2025
Copy link

codecov bot commented Jul 2, 2025

Codecov Report

Attention: Patch coverage is 50.00000% with 36 lines in your changes missing coverage. Please review.

Project coverage is 70.05%. Comparing base (d592c45) to head (88b2010).
Report is 20 commits behind head on main.

Files with missing lines Patch % Lines
app/controllers/file_templates_controller.rb 0.00% 5 Missing ⚠️
app/controllers/error_templates_controller.rb 42.85% 4 Missing ⚠️
app/controllers/exercises_controller.rb 60.00% 4 Missing ⚠️
...p/controllers/execution_environments_controller.rb 25.00% 3 Missing ⚠️
app/controllers/subscriptions_controller.rb 0.00% 3 Missing ⚠️
...pp/controllers/concerns/webauthn/authentication.rb 0.00% 2 Missing ⚠️
...ontrollers/error_template_attributes_controller.rb 60.00% 2 Missing ⚠️
app/controllers/live_streams_controller.rb 0.00% 2 Missing ⚠️
app/controllers/proxy_exercises_controller.rb 0.00% 2 Missing ⚠️
.../controllers/user_exercise_feedbacks_controller.rb 0.00% 2 Missing ⚠️
... and 7 more
Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2998   +/-   ##
=======================================
  Coverage   70.05%   70.05%           
=======================================
  Files         215      215           
  Lines        6846     6846           
=======================================
  Hits         4796     4796           
  Misses       2050     2050           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@MrSerth MrSerth force-pushed the ss/turbo-migration branch from 8713b17 to d647dab Compare July 4, 2025 09:08
@arkirchner
Copy link
Contributor

This PR is huge, but is works. I tested it locally and it works well. I added my first comment about the sign out of the session controller. Now I am considering to just approve the PR and changing individual things with later PRs. This way we can get this merged once.

@MrSerth Should we proceed like this?

MrSerth added 19 commits July 9, 2025 09:34
This is the first part of using Turbo for this code base. Some solutions, like the temporary events triggered, should be temporary and removed once we fully migrated from Sprockets to another JavaScript distribution (such as Shakapacker, importmaps, etc.)
These links work correctly with Turbo, so that we don't need to opt out here. Also, we can cache the /implement route normally, since the LTI handling has changed in 2024.
Turbo imposes a new requirement for form submissions: Whenever a form is submitted, a redirect is expected. When no redirect is performed (i.e., when the data submitted didn't pass validation and the same form is rendered again), a 4xx or 5xx error code is required.

Therefore, this commit changes those occurrences to return a 422 error code. Without this change, an error would be logged by Turbo in the JavaScript console and the response (including the flash message) wouldn't be shown.

See https://turbo.hotwired.dev/handbook/drive#redirecting-after-a-form-submission
Turbo requires us to use the HTTP status code 303 for successful form submissions after stateful server changes.

See https://turbo.hotwired.dev/handbook/drive#redirecting-after-a-form-submission
For these actions, we use use a non-default `method` (other than `GET`). To indicate that within the DOM (i.e., for Screenreaders) but also enhance compatibility with Turbo, we switch to proper `button_to` forms here.

As part of this migration, some buttons should still be shown inline (hence, we add the `form_class: 'd-inline-block'`) or with the same height as regular links (hence, we add `class: 'h-100'`).

See https://github.com/heartcombo/devise/wiki/How-To:-Upgrade-to-Devise-4.9.0-%5BHotwire-Turbo-integration%5D#data-method-vs-data-turbo-method
See https://gorails.com/episodes/link_to-vs-button_to-in-rails
After migrating `link_to` to `button_to` (in the previous commit), we need to adjust the styles for a uniform and correct appearance.
In some cases, the left sidebar was too high, causing an undesired scroll bar to be displayed. We increase the magic number to hide the scrollbar in more cases.

This is not a proper solution, but enough to hide the scroll bars once again. We need to investigate further.
When the form is submitted but no redirect occurs (e.g., after a validation error), the page content is simply replaced. However, for this occasion Turbo misses to emit a new `turbo:load` event, which makes it more difficult to use custom JavaScript waiting on page initializations. Potentially, we could use `turbo:render` here to fix these occasions.
With Turbo enabled on forms with custom JavaScript / CSS packs and a successful change (no validation error), the final page would be rendered twice: The first rendering would occur after sending the form data using Turbo. This response is fetched and potentially displayed (first visit). The resulting DOM contains a change in the packs (with `'data-turbo-track': 'reload'`), causing a page reload (without Turbo). This is the second visit. Through this "double-visit", any flash message intended to be shown after the form submission is simply "lost". Therefore, we disable Turbo in these cases.

This holds also true for signout actions, that can (potentially) originate from any page, including those with custom packs, and redirect to the root page (without any additional packs).
With this new method, Tooltips are correctly initialized and potential loading errors/race conditions are prevented.
Without this change, the new path might be visited before the login succeeded. This causes authorization issues, where another login screen was shown.
The Score system specs are very tricky and error-prone with the current setup. Mostly, this is due to the headless Chrome browser used on CI, which has a weird race condition preventing a regular use. Until said issue is resolved, we add additional expectations (to wait for the page to finish loading).

See SeleniumHQ/selenium#15273
With this change, each editor instance is destroyed upon changing the page. This prevents memory leaks and thus optimizes the performance. The editor will be restored upon visiting the sites again.
Similar to the ACE editor, we unload the JsTree to prevent memory leaks. Also, we use this opportunity to reduce code duplication.
In general, we can run system specs with four browser combinations:

1. Chrome
2. Chrome Headless
3. Firefox
4. Firefox Headless

Unfortunately, Selenium has an ongoing issue with Chrome Headless and Alerts, making the test execution slow or causing random failures. Non-headless Chrome works fine.

To overcome these issues, we switch to Firefox for the time being in GitHub Actions. Non-headless browsers are not supported in GitHub Actions.
While loading the multilang support only on the respective pages sounds like a good idea, it complicates matters with the Turbo migration (since we would need to opt-out of some cases). Also, the exercise description is used in too many places, whereas the snippet is rather short and the performance impact to load it globally is rather minimal.

Amends 7d4dd2c
@MrSerth MrSerth force-pushed the ss/turbo-migration branch from d647dab to 88b2010 Compare July 9, 2025 07:36
@MrSerth
Copy link
Member Author

MrSerth commented Jul 9, 2025

This PR is huge, but is works. I tested it locally and it works well. I added my first comment about the sign out of the session controller. Now I am considering to just approve the PR and changing individual things with later PRs. This way we can get this merged once.

@MrSerth Should we proceed like this?

Thanks for taking a look. I am glad you tested the changes locally and found them working; that's a huge win.

Usually, I'm not in favor of merging something that's likely to be revised right away due to "pending" review comments. That said, I agree the PR already covers a lot, and keeping it up-to-date could become time-consuming for both of us. To make things easier, I've kept all changes in smaller commits -- grouping repetitive patterns and adding explanations where needed.

I also took your comment about the Turbo cache on logout actions as a cue to perform some additional adjustments (e.g., HTTP status changes, converting link_to to button_to, and updating the Devise configuration for CodeHarbor), which is probably nice to have before merging the changes.

With that in mind, do you have a sense of how many remarks you're planning to make, or how "severe" they might be?

Copy link
Contributor

@arkirchner arkirchner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With that in mind, do you have a sense of how many remarks you're planning to make, or how "severe" they might be?

In an ideal situation I would split this PR into multiple steps and release them over time. I would do this to see the impact of individual changes for a day or two.

For example, the link to the button and the status code changes could be released independently.
I think the theme changing and editor unloading should also work woth Trubolinks.

However, everything is reviewed and tested now. To move fast, I approved this for a single merge/release.

@MrSerth
Copy link
Member Author

MrSerth commented Jul 9, 2025

With that in mind, do you have a sense of how many remarks you're planning to make, or how "severe" they might be?

In an ideal situation I would split this PR into multiple steps and release them over time. I would do this to see the impact of individual changes for a day or two.

For example, the link to the button and the status code changes could be released independently. I think the theme changing and editor unloading should also work woth Trubolinks.

However, everything is reviewed and tested now. To move fast, I approved this for a single merge/release.

Alright, thank you. I agree that some changes could have been extracted, but am also fine to merge it now and make further adjustments when needed 👍.

@MrSerth MrSerth merged commit 5acecc4 into main Jul 9, 2025
14 of 15 checks passed
@MrSerth MrSerth deleted the ss/turbo-migration branch July 9, 2025 11:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dependencies Pull requests that update a dependency file javascript Pull requests that update Javascript code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants