<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Christian Sedlmair</title>
    <description>The latest articles on DEV Community by Christian Sedlmair (@chmich).</description>
    <link>https://dev.to/chmich</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F917205%2Fcb6852d0-96a1-4526-b909-f303b0b802b4.jpeg</url>
      <title>DEV Community: Christian Sedlmair</title>
      <link>https://dev.to/chmich</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chmich"/>
    <language>en</language>
    <item>
      <title>System Testing</title>
      <dc:creator>Christian Sedlmair</dc:creator>
      <pubDate>Wed, 20 Nov 2024 13:44:10 +0000</pubDate>
      <link>https://dev.to/chmich/system-testing-1014</link>
      <guid>https://dev.to/chmich/system-testing-1014</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1"&gt;Overview&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Do you know Playwright?
&lt;/h2&gt;

&lt;p&gt;Playwright is emerging as the new standard for front-end testing.&lt;/p&gt;

&lt;p&gt;It is faster and more recent than Selenium, and its syntax is clearer than that of Capybara-DSL. See the discussion here: &lt;a href="https://discuss.rubyonrails.org/t/rails-7-capybara-speedup-possible/87571" rel="noopener noreferrer"&gt;https://discuss.rubyonrails.org/t/rails-7-capybara-speedup-possible/87571&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Notice&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Playwright can be used with the Capybara DSL, which is ideal for existing projects. Initially, I tried to keep the old tests in the Capybara DSL while writing the new ones in the Playwright DSL within the same project. However, this approach was unsuccessful, so ultimately I rewrote everything in Playwright.&lt;/p&gt;

&lt;p&gt;As recommended, it is configured to create a new instance at the start of each run and open/close a new window for every test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Playwright within Rails/RSpec
&lt;/h2&gt;

&lt;p&gt;Derived from &lt;a href="https://playwright-ruby-client.vercel.app/docs/article/guides/rails_integration_with_null_driver" rel="noopener noreferrer"&gt;Docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setup&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Remove all Selenium-gems&lt;/li&gt;
&lt;li&gt;add gem &lt;code&gt;capybara&lt;/code&gt; within group &lt;code&gt;test&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;add gem &lt;code&gt;playwright-ruby-client&lt;/code&gt;  within group &lt;code&gt;test&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm i --save-dev playwright&lt;/code&gt; (unlike docs do not install it by &lt;code&gt;npx i ...&lt;/code&gt; because it would not install dependencies)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;a File like &lt;code&gt;playwright_helper.rb&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;(for a project with devise authentication, it uses the &lt;em&gt;log_in&lt;/em&gt; method)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'rails_helper'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'capybara'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'playwright'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'support/playwright_utils'&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Devise&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Test&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;IntegrationHelpers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :system&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include&lt;/span&gt; &lt;span class="no"&gt;PlaywrightUtils&lt;/span&gt;

  &lt;span class="c1"&gt;# Docs https://playwright-ruby-client.vercel.app/docs/article/guides/rails_integration_with_null_driver&lt;/span&gt;

  &lt;span class="n"&gt;video_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'/tmp/rails-system-test-videos'&lt;/span&gt;

  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :system&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;FileUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rm_rf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;video_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;FileUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdir_p&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;video_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Launch Playwright and browser once for all tests&lt;/span&gt;
    &lt;span class="vi"&gt;@playwright&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Playwright&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;playwright_cli_executable_path: &lt;/span&gt;&lt;span class="s1"&gt;'./node_modules/.bin/playwright'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Use browser_type to access Firefox&lt;/span&gt;
    &lt;span class="vi"&gt;@browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@playwright&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;playwright&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;firefox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;headless: &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;playwright_debug_mode?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;after&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :system&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Close browser and Playwright after all tests&lt;/span&gt;
    &lt;span class="vi"&gt;@browser&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;
    &lt;span class="vi"&gt;@playwright&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CapybaraNullDriver&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Driver&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;needs_server?&lt;/span&gt;
      &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;PlaywrightPageScreenshotFix&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_screenshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;_args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="c1"&gt;# No-op: videos already saved on failure.&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="no"&gt;Playwright&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PlaywrightPageScreenshotFix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register_driver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;CapybaraNullDriver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;around&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;driver: :null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;driven_by&lt;/span&gt; &lt;span class="ss"&gt;:null&lt;/span&gt;

    &lt;span class="c1"&gt;# Create a new context and page for each test&lt;/span&gt;
    &lt;span class="vi"&gt;@browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;record_video_dir: &lt;/span&gt;&lt;span class="n"&gt;video_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;baseURL: &lt;/span&gt;&lt;span class="no"&gt;Capybara&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current_session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;base_url&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enable_debug_console!&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;playwright_debug_mode?&lt;/span&gt;
      &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_default_timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2000.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_page&lt;/span&gt;

      &lt;span class="c1"&gt;# Capture JavaScript console logs&lt;/span&gt;
      &lt;span class="vi"&gt;@browser_console_logs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
      &lt;span class="vi"&gt;@page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'console'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="vi"&gt;@browser_console_logs&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;text: &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'pageerror'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="vi"&gt;@browser_console_logs&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'exception'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;text: &lt;/span&gt;&lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="c1"&gt;# LOGIN PROCESS&lt;/span&gt;
      &lt;span class="n"&gt;sign_in&lt;/span&gt; &lt;span class="vi"&gt;@your_desired_user&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; set your user there&lt;/span&gt;

      &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;

      &lt;span class="c1"&gt;# Save video only if the test fails&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exception&lt;/span&gt;
        &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"file://&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;video&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="no"&gt;FileUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;video&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;video&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;support/playwright_helpers.rb&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Playwright&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_eval&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;path&lt;/span&gt;
    &lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Helper methods for Playwright in system-ssr-tests specs.&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;PlaywrightHelpers&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;playwright_debug_mode?&lt;/span&gt;
    &lt;span class="c1"&gt;# detects if a test is running in debug mode&lt;/span&gt;
    &lt;span class="c1"&gt;# works for rubymine&lt;/span&gt;
    &lt;span class="vi"&gt;@playwright_debug&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="vg"&gt;$LOADED_FEATURES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ruby-debug-ide'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;playwright_breakpoint&lt;/span&gt;
    &lt;span class="vi"&gt;@page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pause&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;playwright_debug?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;clear_js_logs&lt;/span&gt;
    &lt;span class="vi"&gt;@browser_console_logs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wait_for_js_log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;timeout: &lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;wait_for_js_log_condition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;"browser log to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Regexp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'match'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'equal'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; «&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;»"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;timeout: &lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;js_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;matcher&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wait_for_js_debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;timeout: &lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;wait_for_js_log_condition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;"browser debug to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Regexp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'match'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'equal'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; «&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;»"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;timeout: &lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;js_debugs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;matcher&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;matcher&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wait_for_js_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;timeout: &lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;wait_for_js_log_condition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;"browser info to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Regexp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'match'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'equal'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; «&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;»"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;timeout: &lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;js_infos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;matcher&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;matcher&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wait_for_js_warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;timeout: &lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;wait_for_js_log_condition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;"browser warning to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Regexp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'match'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'equal'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; «&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;»"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;timeout: &lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;js_warnings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;matcher&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wait_for_js_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;timeout: &lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;wait_for_js_log_condition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;"browser error to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Regexp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'match'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'equal'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; «&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;»"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;timeout: &lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;js_errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;matcher&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wait_for_js_exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;timeout: &lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;wait_for_js_log_condition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;"browser exception to &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Regexp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'match'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'equal'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; «&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;»"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;timeout: &lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;js_exceptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;matcher&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;js_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;logs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

             &lt;span class="n"&gt;invalid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="no"&gt;ALLOWED_CONSOLE_TYPES&lt;/span&gt;
             &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;invalid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt;
               &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ArgumentError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Invalid console type(s) in array: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;invalid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:inspect&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;', '&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. "&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;
                 &lt;span class="s2"&gt;"Allowed: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;ALLOWED_CONSOLE_TYPES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:inspect&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;', '&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
             &lt;span class="k"&gt;end&lt;/span&gt;

             &lt;span class="vi"&gt;@browser_console_logs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:type&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to_sym&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
           &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt;

             &lt;span class="n"&gt;sym&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_sym&lt;/span&gt;
             &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="no"&gt;ALLOWED_CONSOLE_TYPES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
               &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ArgumentError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Invalid console type: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. "&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;
                 &lt;span class="s2"&gt;"Allowed: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;ALLOWED_CONSOLE_TYPES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:inspect&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;', '&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; or nil"&lt;/span&gt;
             &lt;span class="k"&gt;end&lt;/span&gt;

             &lt;span class="vi"&gt;@browser_console_logs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
           &lt;span class="k"&gt;else&lt;/span&gt;
             &lt;span class="vi"&gt;@browser_console_logs&lt;/span&gt;
           &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:text&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;js_debugs&lt;/span&gt;
    &lt;span class="n"&gt;js_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;type: :debug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;js_infos&lt;/span&gt;
    &lt;span class="n"&gt;js_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;type: :info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;js_errors&lt;/span&gt;
    &lt;span class="n"&gt;js_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;type: :error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;js_warnings&lt;/span&gt;
    &lt;span class="n"&gt;js_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;type: :warning&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Some implementations use "exception" type – keep if needed&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;js_exceptions&lt;/span&gt;
    &lt;span class="n"&gt;js_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;type: :exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;js_issues&lt;/span&gt;
    &lt;span class="n"&gt;js_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="sx"&gt;%i[error warning exception]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wait_for_js_log_condition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;timeout: &lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;start_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_f&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;

    &lt;span class="kp"&gt;loop&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;

      &lt;span class="n"&gt;elapsed_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_f&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start_ms&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;elapsed_ms&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"Timeout: waited &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;elapsed_ms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; ms for &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;notice&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="no"&gt;ALLOWED_CONSOLE_TYPES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%i[log debug info error warning exception]&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and a test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'playwright_helper'&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"pw"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :system&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'test'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="vi"&gt;@page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'body'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;wait_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;state: :visible&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="c1"&gt;# =&amp;gt; without the .wait_for the test would not fail without the body-tag&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is a &lt;a href="https://playwright-ruby-client.vercel.app/docs/article/getting_started#simple-scraping" rel="noopener noreferrer"&gt;Code Example&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Mainly importand docs, for writing tests, may be the &lt;a href="https://playwright-ruby-client.vercel.app/docs/article/api_coverage" rel="noopener noreferrer"&gt;API Coverage&lt;/a&gt; and &lt;a href="https://playwright.dev/docs/api/class-page" rel="noopener noreferrer"&gt;Page class&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1"&gt;Overview&lt;/a&gt;&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>rails</category>
      <category>javascript</category>
    </item>
    <item>
      <title>A new kind of working</title>
      <dc:creator>Christian Sedlmair</dc:creator>
      <pubDate>Sun, 16 Apr 2023 10:16:17 +0000</pubDate>
      <link>https://dev.to/chmich/rails-7-vite-wrapping-up-1pia</link>
      <guid>https://dev.to/chmich/rails-7-vite-wrapping-up-1pia</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1"&gt;https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Streamlining, guidelines such as "convention over configuration" and a concise naming convention were the beginning of the success of the rails.&lt;/p&gt;

&lt;p&gt;2021 DHH published &lt;a href="https://rubyonrails.org/2021/12/15/Rails-7-fulfilling-a-vision"&gt;Rails-7 fulfilling a vision&lt;/a&gt; and there is an expectation that we can now build React-like applications, but as a true full-stack development with much less effort than a front-end separated application.&lt;/p&gt;

&lt;p&gt;Along the way I found some details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Streamline Stimulus naming &lt;a href="https://www.npmjs.com/package/vite-stimulus-initializer"&gt;npm vite-stimulus-initializer&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Working consistently with turbo means that part of the page hasn't changed between page changes. So they need to be refreshed in some other way: Popups often have content that needs to be refreshed when the popup or menu opens. I don't use lazy loading frames, but set a javascript trigger when the menu opens and reloads that part, and then set a trigger on `turbo:render' that makes sure all menus and popups are closed when a link is clicked.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rubygems.org/gems/render_turbo_stream"&gt;Streamlining rendering of turbo frames&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With all this and Vite as the frontend engine, full stack development feels like it should. Building such an app is much easier than separating frontend and backend, is minimally more effort than a classic Rails app bild React-like frontend apps, but with one huge advantage: having a consistent testing (rspec / capybara) over the whole app.&lt;/p&gt;

&lt;p&gt;Rails was the most agile framework from the start. Then others caught up. These are added values that can make Rails an interesting framework for years to come.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1"&gt;https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;P.S.:&lt;br&gt;
As a alternative to the default &lt;a href="https://trix-editor.org/"&gt;trix&lt;/a&gt; editor i found &lt;a href="https://www.npmjs.com/package/jodit"&gt;Jodit&lt;/a&gt; because of &lt;a href="https://stackoverflow.com/questions/75836943/summernote-on-vite-rails-production-not-working"&gt;issue on summernote on vite&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Setup Stimulus on Vite</title>
      <dc:creator>Christian Sedlmair</dc:creator>
      <pubDate>Fri, 21 Oct 2022 20:55:34 +0000</pubDate>
      <link>https://dev.to/chmich/setup-stimulus-on-vite-2eg9</link>
      <guid>https://dev.to/chmich/setup-stimulus-on-vite-2eg9</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1"&gt;Overview&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Why?
&lt;/h1&gt;

&lt;p&gt;Stimulus is essentially an initialiser. In this sense, it is an essential and necessary part of the setup, together with Hotwired.&lt;/p&gt;

&lt;p&gt;However, when it comes to building reactive components, we believe it is the wrong tool for the job.&lt;/p&gt;

&lt;h1&gt;
  
  
  Setup
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm i @hotwired/stimulus vite-stimulus-initializer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;application.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { initStimulus } from "vite-stimulus-initializer";

const controllers = import.meta.glob('../javascript/**/*-controller.js', { eager: true })
initStimulus(controllers, 2, { debug: true, exclude: ['components', 'views'], folderSeparator: '-'  })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See naming options on &lt;a href="https://gitlab.com/sedl/vite-stimulus-initializer" rel="noopener noreferrer"&gt;vite-stimulus-initializer&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Test Code
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;javascript/controllers/hello-controller.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;By the filename, the controller will be hooking on any element with &lt;code&gt;data-controller="admin-hello"&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Controller } from "@hotwired/stimulus"

export default class extends Controller {

    connect() {
        // connect fires after the controller has found a corresponding element.
        console.log("Stimulus is connected", this.element)
    }

    static targets = [ "name" ]

    greet() {
        const element = this.nameTarget
        const name = element.value
        console.log(`Hello, ${name}!`)
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;any view in .haml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%div{ 'data-controller' =&amp;gt; 'admin-hello' }
  %input{:type =&amp;gt; "text", 'data-admin-hello-target' =&amp;gt; 'name'}/
  %button{ 'data-action' =&amp;gt; "click-&amp;gt;admin-hello#greet" } Greet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;same in &lt;code&gt;html.erb&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div data-controller="admin-hello"&amp;gt;
  &amp;lt;input data-admin-hello-target="name" type="text"&amp;gt;/&amp;lt;/input&amp;gt;
  &amp;lt;button data-action="click-&amp;gt;admin-hello#greet"&amp;gt;Greet&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;by loading this you should see a «Stimulus is connected» in the console. Writing «John» in the input and clicking the button should print out a «Hello John!» on the console.&lt;/p&gt;

&lt;p&gt;⚠️ Together with turbo you may see a double «Stimulus is connected» on the console (connect fires 2 times). Based on my Tests it is first firing on the old, element then turbo replaces this and then its firing on the new element, so it should't matter and the new element is not a double-initialized.&lt;/p&gt;

&lt;p&gt;Continue with &lt;a href="https://stimulus.hotwired.dev/handbook/introduction" rel="noopener noreferrer"&gt;Stimulus Handbook&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Good luck!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1"&gt;Overview&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tooling</category>
      <category>tutorial</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Rails UJS on Vite (⚠️ together with Turbo?)</title>
      <dc:creator>Christian Sedlmair</dc:creator>
      <pubDate>Sat, 24 Sep 2022 15:45:10 +0000</pubDate>
      <link>https://dev.to/chmich/rails-ujs-on-vite-b6m</link>
      <guid>https://dev.to/chmich/rails-ujs-on-vite-b6m</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1"&gt;Overview&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attention ⚠️&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Unobstrosive Javascript (UJS) is widely applied in many rails apps. In &lt;a href="https://github.com/rails/rails-ujs"&gt;5.1&lt;/a&gt; the Rails team made it indepent from jQuery and moved it to the core of rails. &lt;/p&gt;

&lt;p&gt;Can UJS live alongside Turbo?&lt;/p&gt;

&lt;p&gt;Yes, but not on the easy way, &lt;a href="https://github.com/hotwired/turbo-rails#compatibility-with-rails-ujs"&gt;see github/turbo-rails&lt;/a&gt; and see &lt;a href="https://guides.rubyonrails.org/working_with_javascript_in_rails.html#replacements-for-rails-ujs-functionality"&gt;recommendations&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ujs</category>
      <category>rails</category>
      <category>vite</category>
    </item>
    <item>
      <title>Setup Turbo on Vite</title>
      <dc:creator>Christian Sedlmair</dc:creator>
      <pubDate>Wed, 21 Sep 2022 12:17:26 +0000</pubDate>
      <link>https://dev.to/chmich/setup-turbo-on-vite-4j8i</link>
      <guid>https://dev.to/chmich/setup-turbo-on-vite-4j8i</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1"&gt;Overview&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/chmich/setup-vite-on-rails-7-f1i"&gt;Vite&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Turbo&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;See also: &lt;a href="https://www.hotrails.dev/"&gt;Hotrails Tutorial&lt;/a&gt;&lt;a href="https://turbo.hotwired.dev/"&gt;Docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gemfile&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gem 'turbo-rails'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I prefere not using the &lt;a href="https://github.com/hotwired/turbo-rails#installation"&gt;Gem installer&lt;/a&gt;, just:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm i @hotwired/turbo-rails
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;see: &lt;a href="https://github.com/hotwired/turbo-rails#installation"&gt;GitHub Turbo/Installation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;frontend/entrypoints/application.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import '@hotwired/turbo-rails'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Test&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;How to check if Turbo works? see &lt;a href="https://www.hotrails.dev/turbo-rails/turbo-drive"&gt;Turbo/TurboDrive&lt;/a&gt; aproximately on the middle of the page&lt;/p&gt;

&lt;p&gt;You have to insert at least two links and correspondending views. Check the network tab on browser. Page Reload loads all the Javascript and Stylesheets while Turbo, by clicking a link, by default, just replaces the body.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;=&amp;gt;&lt;/strong&gt; Turbo should work. Check it by browser &amp;gt; developer tools &amp;gt; Network Tab&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1"&gt;Overview&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S.:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Page Initialization Event&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Like described on &lt;a href="https://dev.to/chmich/setup-foundation-sites-on-rails-7-and-vite-23mm"&gt;Foundation&lt;/a&gt; Frontend setup needs a Initialization Event.&lt;/p&gt;

&lt;p&gt;Full List of Turbo Events are on &lt;a href="https://turbo.hotwired.dev/reference/events"&gt;Turbo/reference/events&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Page Initialization is done with &lt;code&gt;turbo:render&lt;/code&gt; or &lt;code&gt;turbo:load&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Attention: If you work inside a frame, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;= turbo_frame_tag 'mainFrame' do
  %h1 TurboFrame#first_content
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;=&amp;gt; none of that events will work! And it also not updates the Browser History&lt;/p&gt;

&lt;p&gt;If you need an initialization Event, check &lt;a href="https://turbo.hotwired.dev/handbook/building#attaching-behavior-with-stimulus"&gt;Handbook/Stimulus, MutationObserver&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Changing the same frame to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;= turbo_frame_tag 'mainFrame', 'data-turbo-action': :advance do
  %h1 TurboFrame#second_content
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;=&amp;gt; mentioned events are firing and Browser History is changed&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1"&gt;Overview&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>vite</category>
      <category>hotwire</category>
    </item>
    <item>
      <title>Create Rails-7 app with Vite</title>
      <dc:creator>Christian Sedlmair</dc:creator>
      <pubDate>Tue, 20 Sep 2022 13:51:56 +0000</pubDate>
      <link>https://dev.to/chmich/setup-vite-on-rails-7-f1i</link>
      <guid>https://dev.to/chmich/setup-vite-on-rails-7-f1i</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1"&gt;Overview&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Vite relies on &lt;code&gt;Node&lt;/code&gt; and its included &lt;code&gt;npm&lt;/code&gt; which comes with node installation. So, check that Node is up to date on development machine and production server.&lt;/p&gt;

&lt;p&gt;⚠️ Since 2.x, &lt;code&gt;yarn&lt;/code&gt;, by default, deletes the node_modules folder, see &lt;a href="https://stackoverflow.com/questions/68735025/yarn-erased-my-node-modules-folder-every-time" rel="noopener noreferrer"&gt;Stackoverflow&lt;/a&gt;. For Vite it means that Vite breaks. So i'm going completely without yarn. If Yarn is referenced you can delete all yarn files in project, delete node_modules folder and rebuild it by &lt;code&gt;npm i&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Create Rails App
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ rails new vite-app --skip-javascript &amp;amp;&amp;amp; cd vite-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--skip-javascript&lt;/code&gt; is necessary for avoiding &lt;a href="https://github.com/ElMassimo/vite_ruby/issues/250" rel="noopener noreferrer"&gt;conflicts&lt;/a&gt; on the next steps. In case of bootstrap/foundation-sites the asset pipeline is helpful so &lt;code&gt;--skip-asset-pipeline&lt;/code&gt; is not applied.&lt;/p&gt;




&lt;h2&gt;
  
  
  Add the packages
&lt;/h2&gt;

&lt;p&gt;see: &lt;a href="https://github.com/sergii/vite_rails" rel="noopener noreferrer"&gt;GitHub/vite_rails&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ bundle add vite_rails
$ bundle exec vite install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Vite Stylesheets&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add entrypoint file &lt;code&gt;application.css&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ touch app/frontend/entrypoints/application.css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the vite_stylesheet_tag («+») to &lt;strong&gt;layout&lt;/strong&gt; so that its loaded alongside the asset pipeline&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    = stylesheet_link_tag "application" 
+   = vite_stylesheet_tag 'application.css', media: :all // =&amp;gt; extension .scss is necessary here
    = vite_client_tag
    = vite_javascript_tag 'application' 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Foreman Runner&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ bundle add foreman --group=development
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;within the development group.&lt;/p&gt;




&lt;h2&gt;
  
  
  Run the app
&lt;/h2&gt;

&lt;p&gt;On the console, run the app by &lt;code&gt;foreman start -f Procfile.dev&lt;/code&gt;, on RubyMine by the Runner.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pitfall:&lt;/strong&gt; Call your app by &lt;code&gt;localhost&lt;/code&gt; and not by 0.0.0.0 / 127.0.0.0 e.g.. Otherwise the browser cannot access the vite dev server and HMR would not work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;=&amp;gt;&lt;/strong&gt; You should see your app at &lt;a href="http://localhost:5100" rel="noopener noreferrer"&gt;http://localhost:5100&lt;/a&gt; and Hot Module Reloading (HMR) should work, but only for files inside frontend folder. Check: Modify a file and check console output.&lt;/p&gt;




&lt;h2&gt;
  
  
  Full Hot Module Reloading (HMR)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm i --save-dev vite-plugin-full-reload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;on &lt;code&gt;vite.config.ts&lt;/code&gt; add the lines with the «+»&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  import {defineConfig} from 'vite'
  import RubyPlugin from 'vite-plugin-ruby'
+ import FullReload from 'vite-plugin-full-reload'

  export default defineConfig({
      plugins: [
          RubyPlugin(),
+         FullReload([
+             'app/views/**/*',
+             // =&amp;gt; add paths you want to watch
+         ], {delay: 0}),
      ],
  })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart the server&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;create a scaffold or two views and connecting them by link&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;=&amp;gt;&lt;/strong&gt; HMR should work even for views, regarding configuration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1"&gt;Overview&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;P.S.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Deploy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm working on Capistrano, so some notes:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;deploy.rb&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;append :linked_dirs, 'public/vite'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;do not append &lt;code&gt;tmp/cache/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;see &lt;a href="https://github.com/ElMassimo/vite_ruby/discussions/198" rel="noopener noreferrer"&gt;Vite/Discussions/potential issues when deploy with capistrano&lt;/a&gt;&lt;br&gt;
do not append &lt;code&gt;tmp/cache/&lt;/code&gt;&lt;br&gt;
see &lt;a href="https://github.com/ElMassimo/vite_ruby/discussions/198" rel="noopener noreferrer"&gt;Potential issues when deploy with capistrano&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>vite</category>
    </item>
    <item>
      <title>Setup Svelte as Custom Element on Vite</title>
      <dc:creator>Christian Sedlmair</dc:creator>
      <pubDate>Tue, 20 Sep 2022 13:51:52 +0000</pubDate>
      <link>https://dev.to/chmich/setup-inertia-and-svelte-on-rails-7-3glk</link>
      <guid>https://dev.to/chmich/setup-inertia-and-svelte-on-rails-7-3glk</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1"&gt;Overview&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Why Svelte?
&lt;/h1&gt;

&lt;p&gt;In every app, we have at least one feature that is special and requires complex front-end logic. This is where Svelte gives us and our customers so much pleasure. &lt;/p&gt;

&lt;p&gt;Compared to React, which clearly has a larger ecosystem, Svelte is leaner, faster, better integrated, and in my opinion, simply the better technology. To understand the idea of Svelte in comparison to React, please take a look at this entertaining video from Rich Harris, the founder of Svelte, starting at minute 4:13 &lt;a href="https://svelte.dev/blog/svelte-3-rethinking-reactivity" rel="noopener noreferrer"&gt;rethinking reactivity&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Combined with solid system testing (we use Playwright), this is a powerful and reliable framework.&lt;/p&gt;

&lt;h1&gt;
  
  
  Setup Svelte
&lt;/h1&gt;

&lt;p&gt;For make work svelte fluently we built the gem &lt;a href="https://gitlab.com/sedl/svelte-on-rails" rel="noopener noreferrer"&gt;svelte-on-rails&lt;/a&gt; with the related node-package. Please follow the instructions there.&lt;/p&gt;

&lt;p&gt;But, first we must make svelte running:&lt;/p&gt;

&lt;h1&gt;
  
  
  Installation
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i svelte@latest @sveltejs/vite-plugin-svelte@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;vite.config.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  import { defineConfig } from 'vite'
  import RubyPlugin from 'vite-plugin-ruby'
+ import { svelte } from '@sveltejs/vite-plugin-svelte';

  export default defineConfig({
    plugins: [
      RubyPlugin(),
+     svelte({})
    ]
  })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;package.json&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  ...
  "type": "module" // =&amp;gt; otherwise @sveltejs/vite-plugin-svelte would break
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart server.&lt;/p&gt;

&lt;p&gt;example component &lt;code&gt;frontend/javascript/HelloWorld.svelte&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;h1&amp;gt;Hello World from Svelte&amp;lt;/h1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;add on &lt;code&gt;application.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import HelloWorld from '../javascript/HelloWorld.svelte'
import { mount } from 'svelte';
const tag = document.getElementById('svelte-hello-world')
mount(HelloWorld, { target: tag })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you should see your component on the browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1"&gt;Overview&lt;/a&gt;&lt;/p&gt;

</description>
      <category>svelte</category>
      <category>rails</category>
      <category>vite</category>
    </item>
    <item>
      <title>Setup jQuery on Vite</title>
      <dc:creator>Christian Sedlmair</dc:creator>
      <pubDate>Tue, 20 Sep 2022 13:51:47 +0000</pubDate>
      <link>https://dev.to/chmich/setup-jquery-on-vite-598k</link>
      <guid>https://dev.to/chmich/setup-jquery-on-vite-598k</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1"&gt;Overview&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/chmich/setup-vite-on-rails-7-f1i"&gt;Vite&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setup&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm i @rollup/plugin-inject --save-dev
$ npm i jquery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;vite.config.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;add the lines with the «+».&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  import { defineConfig } from 'vite'
  import RubyPlugin from 'vite-plugin-ruby'
+ import inject from "@rollup/plugin-inject";

  export default defineConfig({
    plugins: [
+       inject({   // =&amp;gt; that should be first under plugins array
+         $: 'jquery',
+         jQuery: 'jquery',
+       }),
      RubyPlugin(),
    ],
  })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart the Server&lt;/p&gt;

&lt;p&gt;&lt;code&gt;frontend/entrypoints/application.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import $ from 'jquery';
window.$ = $;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Refresh the browser&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Testcode&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;frontend/entrypoints/application.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;window.test_jquery = function() {
    $('#test-jquery').html('jQuery works!')
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;app/views/layout/application.haml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%button#test-jquery{onclick: 'test_jquery()'}
  Test if jQuery is working
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;=&amp;gt;&lt;/strong&gt; Click the Button and you should see: «jQuery works!»&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1"&gt;Overview&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vite</category>
    </item>
    <item>
      <title>Setup Bootstrap on Rails-7 and Vite</title>
      <dc:creator>Christian Sedlmair</dc:creator>
      <pubDate>Tue, 20 Sep 2022 13:51:42 +0000</pubDate>
      <link>https://dev.to/chmich/setup-bootstrap-on-rails-7-and-vite-g5a</link>
      <guid>https://dev.to/chmich/setup-bootstrap-on-rails-7-and-vite-g5a</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1"&gt;Overview&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This guide may work, but as of 2024, we would prefer to build new projects without any style framework. CSS has become feature-rich enough.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We setup Bootstrap scss files upon asset pipeline for avoide slowing down Vite HMR. Custom stylesheets can be added inside vite &lt;code&gt;app/frontend/&lt;/code&gt;. We setup one popover to make it working for checking if all the popper.js and bootstrapp javascript is working.&lt;/p&gt;

&lt;p&gt;(&lt;strong&gt;Alternative&lt;/strong&gt; would be: minify the bootstrap CSS and add the minified version to the frontend folder. Then, Vite is clever enough for not loosing time for bundling that. On that way HMR would also rely fast and its cleaner because having only one asset pipeline)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://getbootstrap.com/docs/5.2/getting-started/vite/" rel="noopener noreferrer"&gt;Bootstrap/getting-started/vite&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequesites&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/chmich/setup-vite-on-rails-7-f1i"&gt;Vite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/chmich/setup-jquery-on-vite-598k"&gt;jQuery&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Asset Pipeline&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

gem 'bootstrap', '~&amp;gt; 5.2.0'


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ bundle install


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;add &lt;code&gt;app/assets/stylesheets/_settings.scss&lt;/code&gt; for configuring bootstrap sass variables&lt;/p&gt;

&lt;p&gt;rename &lt;code&gt;app/assets/stylesheets/application.css&lt;/code&gt; to &lt;code&gt;.scss&lt;/code&gt; if it isnt already done&lt;/p&gt;

&lt;p&gt;remove the lines &lt;code&gt;//= require tree&lt;/code&gt; and &lt;code&gt;//= require .&lt;/code&gt; if it isnt yet.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@import "settings";
@import "bootstrap";


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We use only the scss files from the gem. On &lt;a href="https://dev.to/chmich/foundation-sites-on-rails-and-vite-3d6p-temp-slug-3440249?preview=877b694990be4af67440cd13962724e28a94f4efcc2185db0ce34f16060d637882859d26bbb4e315eff1ff98ac0e247b1b8ea3a48a50f47197e697d9"&gt;Foundation&lt;/a&gt; i did the same by smylinking to the yarn package into the asset pipeline. The javascript we get from the yarn package:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Javascript Packages&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ npm i bootstrap @popperjs/core


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;frontend/entrypoints/application.js&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

// import 'bootstrap/js/src/alert'  
// import 'bootstrap/js/src/button'  
// import 'bootstrap/js/src/carousel'  
import 'bootstrap/js/src/collapse'  
import 'bootstrap/js/src/dropdown'  
// import 'bootstrap/js/src/modal'  
// import 'bootstrap/js/src/popover'  
import 'bootstrap/js/src/scrollspy'  
// import 'bootstrap/js/src/tab'  
// import 'bootstrap/js/src/toast'  
// import 'bootstrap/js/src/tooltip' 

import * as bootstrap from 'bootstrap';
window.bootstrap = bootstrap;

// initialize the page
window.addEventListener('load', (event) =&amp;gt; {
    initPage();
});
window.addEventListener('turbo:render', (event) =&amp;gt; {
    initPage();
});
function initPage() {
      // initialize popovers
  var popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'))
  var popoverList = popoverTriggerList.map(function (popoverTriggerEl) {
    return new bootstrap.Popover(popoverTriggerEl)
  }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Layout&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;make sure the &lt;code&gt;stylesheet_link_tag&lt;/code&gt; is present like &lt;a href="https://dev.to/chmich/setup-vite-on-rails-7-2om4-temp-slug-6860562?preview=cdfb38d3f8e1987a3eaf858d2adc20a743f4bffe3fbab10c744d1c1a9a35a51c52e3a64aa225e136f7aa5df94032d6af821e22609020bbfb7488ca56"&gt;described&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test View&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

%button.btn.btn-lg.btn-danger{"data-bs-content" =&amp;gt; "And here's some amazing content. It's very engaging. Right?", "data-bs-title" =&amp;gt; "Popover title", "data-bs-toggle" =&amp;gt; "popover", :type =&amp;gt; "button"} Click to toggle popover


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Falicut9zir0gmm7ztdsb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Falicut9zir0gmm7ztdsb.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;=&amp;gt;&lt;/strong&gt; If the popover works and looks like this, bootstrap styles are working, popper.js is working and popovers are initialized.&lt;/p&gt;

&lt;p&gt;YIPPIE!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1"&gt;Overview&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;P.S.:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Symlinking SASS variables&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Inside &lt;code&gt;frontend/stylesheets/&lt;/code&gt;, create a symlink like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ ln -s ../../assets/stylesheets/_settings.scss .


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then from all the stylesheets, by example &lt;code&gt;frontend/stylesheets/components/button.scss&lt;/code&gt;, import it like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@import '../settings';


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;symlinks to a file are well handled by git (otherwise than symlinks to a folder) and they should landing well on production machine.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>vite</category>
    </item>
    <item>
      <title>Setup Foundation-Sites on Rails-7 and Vite</title>
      <dc:creator>Christian Sedlmair</dc:creator>
      <pubDate>Tue, 20 Sep 2022 13:51:38 +0000</pubDate>
      <link>https://dev.to/chmich/setup-foundation-sites-on-rails-7-and-vite-23mm</link>
      <guid>https://dev.to/chmich/setup-foundation-sites-on-rails-7-and-vite-23mm</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1"&gt;Overview&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For many years we were satisfied with zurb-foundation. But in 2024, we would not setup a new project with it. CSS has become enough future-rich, and the requirements of Rails-7, in particular, the initialization steps has changed so that we setup new projects with pure CSS.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Foundation/sass slows down HMR aproximately 12 seconds. So, i use the asset pipeline for Foundation, by symlinking sass files from node_modules/, so Vite is freed from them. Custom Stylesheets, where will be the most changes, resides in frontend folder. Javascript goes the direct way, from the yarn (node_modules/) package to Vite.&lt;/p&gt;

&lt;p&gt;(&lt;strong&gt;Alternative&lt;/strong&gt; would be: minify the foundation CSS and add the minified version to the frontend folder. Then, Vite is clever enough for not loosing time for bundling that. On that way HMR would also rely fast and its cleaner because having only one asset pipeline)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequesites&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/chmich/setup-vite-on-rails-7-f1i"&gt;Vite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/chmich/setup-jquery-on-vite-598k"&gt;jQuery&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Vite&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ npm i foundation-sites


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;application.js&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import 'foundation-sites'

import $ from 'jquery'; //=&amp;gt; ATTENTION: jQuery has to be imported after foundation. Foundation would not need this, it would be satisfied with the settings in vite.config.js
window.$ = $;

// initialize the page
window.addEventListener('load', (event) =&amp;gt; {
    initPage();
});

window.addEventListener('turbo:render', (event) =&amp;gt; {
    initPage();
});
function initPage() {
    $(document).foundation();
    console.log('foundation ready');
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;=&amp;gt;&lt;/strong&gt; Javascript should work, you should see &lt;code&gt;foundation ready&lt;/code&gt; on Browser/development/console&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Asset Pipeline&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;add settings file for sass variables and copy foundation&lt;/p&gt;

&lt;p&gt;from app-root, run:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ mkdir ./app/assets/stylesheets/foundation
$ touch ./app/assets/stylesheets/_settings.scss
$ cp -r ./node_modules/foundation-sites/scss ./app/assets/stylesheets/foundation
$ cp -r ./node_modules/foundation-sites/_vendor ./app/assets/stylesheets/foundation


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Smarter it would be symlinking them instead of copying. But, at least in my case, &lt;code&gt;git&lt;/code&gt; doesnt transfer symlinks to folders (unlike symlinks to files). So, you would have to exclude them with .gitignore and adding them again by a deployment task (in my case capistrano) on the server. Here is gone the easier way.  &lt;/p&gt;

&lt;p&gt;&lt;code&gt;app/assets/stylesheets/application.scss&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;make sure that the lines &lt;code&gt;//= require tree&lt;/code&gt; and &lt;code&gt;//= require .&lt;/code&gt; are removed&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@import "settings";
@import "./foundation/scss/foundation";
@include foundation-everything()


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;cannot tell why, but i was not able to refer the @import directly to the nodes folder, i had to do theese symlinks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vite Stylesheets&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create folder &lt;code&gt;stylesheets&lt;/code&gt; and symlink settings file&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ mkdir app/frontend/stylesheets
$ cd app/frontend/stylesheets
$ ln -s ../../../app/assets/stylesheets/_settings.scss .
$ cd ../../..


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Layout&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;make sure &lt;code&gt;stylesheet_link_tag "application"&lt;/code&gt; and &lt;code&gt;= vite_stylesheet_tag 'application.scss', media: :all&lt;/code&gt; are present.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test view&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.haml&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

%button.button{"data-toggle" =&amp;gt; "example-dropdown", :type =&amp;gt; "button"} Toggle Dropdown
#example-dropdown.dropdown-pane{"data-auto-focus" =&amp;gt; "true", "data-dropdown" =&amp;gt; ""}
  Example dropdown.


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;.html.erb&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;button class="button" data-toggle="example-dropdown" type="button"&amp;gt;Toggle Dropdown&amp;lt;/button&amp;gt;
&amp;lt;div class="dropdown-pane" data-auto-focus="true" id="example-dropdown"&amp;gt;
  Example dropdown.
&amp;lt;/div&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fictxy2t2qnzm6d4d6kml.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fictxy2t2qnzm6d4d6kml.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;=&amp;gt;&lt;/strong&gt; If the Popover works and looks like this, Foundation styles and javascript is working like it should.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1"&gt;Overview&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>vite</category>
    </item>
    <item>
      <title>Setup Stimulus on Vite</title>
      <dc:creator>Christian Sedlmair</dc:creator>
      <pubDate>Tue, 20 Sep 2022 13:51:34 +0000</pubDate>
      <link>https://dev.to/chmich/setup-stimulus-on-vite-5cp8</link>
      <guid>https://dev.to/chmich/setup-stimulus-on-vite-5cp8</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1"&gt;Overview&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stimulus.hotwired.dev/"&gt;Stimulus&lt;/a&gt; is a «modest» Lightwight Library, created by the rails team that connects html and JavaScript. JS and html are in separated files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stimulus.hotwired.dev/handbook/hello-stimulus"&gt;Stimulus-docs/Installation/hello-stimulus&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/chmich/setup-vite-on-rails-7-2om4-temp-slug-6860562?preview=cdfb38d3f8e1987a3eaf858d2adc20a743f4bffe3fbab10c744d1c1a9a35a51c52e3a64aa225e136f7aa5df94032d6af821e22609020bbfb7488ca56"&gt;Vite&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setup&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm i @hotwired/stimulus stimulus-vite-helpers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;see: &lt;a href="https://github.com/ElMassimo/stimulus-vite-helpers"&gt;GitHub/stimulus-vite-helpers&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;frontend/controllers/index.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Application } from '@hotwired/stimulus'
import { registerControllers } from 'stimulus-vite-helpers'

const application = Application.start()
const controllers = import.meta.globEager('./**/*_controller.js')
registerControllers(application, controllers)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;frontend/entrypoints/application.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import '../controllers'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Test Code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;see: &lt;a href="https://stimulus.hotwired.dev/handbook/hello-stimulus#is-this-thing-on%3F"&gt;Stimulus Docs/is this thing on?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;frontend/controllers/hello_controller.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Controller } from "@hotwired/stimulus" 

export default class extends Controller {

    connect() { // =&amp;gt; «connect» method is similar to initialize method in a ruby-class
        console.log("Hello, Stimulus!")
    }

    static targets = [ "name" ]

    greet() { // =&amp;gt; doesnt matter for now, but later for the view
        const element = this.nameTarget
        const name = element.value
        console.log(`Hello, ${name}!`)
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;any view &lt;code&gt;.haml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%div{"data-controller" =&amp;gt; "hello"}
  %input{"data-hello-target" =&amp;gt; "name", :type =&amp;gt; "text"}/
  %button{"data-action" =&amp;gt; "click-&amp;gt;hello#greet"} Greet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;view in &lt;code&gt;.html.erb&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div data-controller="hello"&amp;gt;
  &amp;lt;input data-hello-target="name" type="text"&amp;gt;/&amp;lt;/input&amp;gt;
  &amp;lt;button data-action="click-&amp;gt;hello#greet"&amp;gt;Greet&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;=&amp;gt;&lt;/strong&gt; Reload page and you should see «Hello Stimulus!» in Browser/developer Console.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;=&amp;gt;&lt;/strong&gt; type «User» in the input, click «Greet» button and you should see «Hello User!» in the Browser/Developer Console&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1"&gt;Overview&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Setup Node on Debian ..and !Yarn</title>
      <dc:creator>Christian Sedlmair</dc:creator>
      <pubDate>Tue, 20 Sep 2022 13:51:28 +0000</pubDate>
      <link>https://dev.to/chmich/setup-node-on-debian-5e8p</link>
      <guid>https://dev.to/chmich/setup-node-on-debian-5e8p</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1"&gt;Overview&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Vite needs &lt;code&gt;Node&lt;/code&gt; and its included &lt;code&gt;npm&lt;/code&gt; and &lt;code&gt;npx&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Yarn&lt;/strong&gt; Since 2.x, by default, deletes the node_modules folder, see &lt;a href="https://stackoverflow.com/questions/68735025/yarn-erased-my-node-modules-folder-every-time"&gt;Stackoverflow&lt;/a&gt;. For Vite it means that it breaks. Finally i decided for &lt;code&gt;npm&lt;/code&gt;, removed all yarn and all folders and files it created on my projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production server
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;remove all Nodes from root level&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Login on production server with root user and check that the output is «command not found». If there would be a stale smylink, the &lt;code&gt;-v&lt;/code&gt; would also not work but different output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ node -v
bash: node: command not found
$ npx -v
bash: npx: command not found
$ npm -v
bash: npm: command not found
$ yarn -v
bash: yarn: command not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check &lt;code&gt;/bin/&lt;/code&gt;, &lt;code&gt;/sbin/&lt;/code&gt;, &lt;code&gt;/usr/bin/&lt;/code&gt;, &lt;code&gt;/usr/sbin/&lt;/code&gt; for stale symlinks and check for &lt;code&gt;node_modules/&lt;/code&gt; folder in &lt;code&gt;/bin/lib/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Check &lt;strong&gt;sources&lt;/strong&gt; (&lt;code&gt;/etc/apt/sources.list&lt;/code&gt;, &lt;code&gt;/etc/apt/sources.list.d/&lt;/code&gt; for any nodes, and, if, remove them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check on user level&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Login on the user where your app should run.&lt;/p&gt;

&lt;p&gt;NVM is &lt;a href="https://docs.npmjs.com/downloading-and-installing-node-js-and-npm"&gt;recommended by npm&lt;/a&gt; and i am satisfied since years. Just updating Node version did'nt work for me, so i gone the hard way, removed the old, installed it  anew and then installed the current Node version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;`$ rm -rf ~/.nvm`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Like on root level, check that no nodes are there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install Node&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Regarding to &lt;a href="https://github.com/nvm-sh/nvm#installing-and-updating"&gt;GitHub/nvm#installation&lt;/a&gt;, perform:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Logout, Login, and &lt;code&gt;nvm -v&lt;/code&gt; should work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ nvm install node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;=&amp;gt;&lt;/strong&gt; &lt;code&gt;node -v&lt;/code&gt;, &lt;code&gt;npm -v&lt;/code&gt;, &lt;code&gt;npx -v&lt;/code&gt; should print out the current versions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;⚠️ Pitfall!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;~/.bashrc&lt;/code&gt; there may be a piece of code like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If shell is running &lt;a href="https://www.geeksforgeeks.org/shell-scripting-interactive-and-non-interactive-shell/"&gt;interactive&lt;/a&gt;, which means you are not logged in by username and password (&lt;a href="https://linuxhandbook.com/login-shell/"&gt;login-shell&lt;/a&gt;) the code execution ends there.&lt;/p&gt;

&lt;p&gt;nvm needs a code piece like this, i have it also  in &lt;code&gt;~/.bashrc&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] &amp;amp;&amp;amp; \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] &amp;amp;&amp;amp; \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, &lt;strong&gt;make sure that its above the previous mentioned code block&lt;/strong&gt;. Otherwise, if the app is running, it runs shell as interactive and nvm is not working, while you, by ssh on the server, think its working.&lt;/p&gt;

</description>
      <category>node</category>
      <category>yarn</category>
    </item>
  </channel>
</rss>
