如何确保资产在测试模式 (RSpec) 下存在于 Rail 7、cssbundling-rails、jsbundling-rails 中?

问题描述 投票:0回答:3

我正在将一个大型商业(专有)Rails 6 应用程序升级到 Rails 7。我们从未使用过 Webpacker,而是直接从 Bootstrap 之类的捆绑 gem 转向“Rails 7 方式”。

事实证明,Rails 7 的“无节点”工作流程对于同时包含 CSS 和 JS 组件的组件没有好的答案。在我们的例子中,最明显的罪犯是 Bootstrap。面对通过导入映射维护 Bootstrap 的 JS“一半”和通过旧的 Bootstrap gem 或手动供应商之类的 CSS“一半”(是的,这里真的没有其他解决方案没有 Node 这里),我们最终回到了完整的 Node 工作流程。

这是在一起的。所有提供 CSS 和/或 JS 的前端组件都已经在 NPM 中可用,所以现在所有这些都通过

package.json
和 Yarn 进行管理,其中
bin/dev
驱动 Sass 和
esbuild
 编译从中提取的 SCSS 和 JS 组件
app/assets
app/javascript
 或
node_modules/...
;因此,资产管道
manifest.js
仅包含对
build
内的
images
app/assets
文件夹的引用。

感觉有点倒退,所有重量级的文件名列表手动维护(不再支持通配符导入)以及现在在 Foreman 下运行的多个进程的复杂性与仅在 Sprockets 中同步处理的内容相比请求基础,但随着所有这些东西被弃用/废弃软件,显然是时候更新了。

这一切在开发和生产模式下都运行良好,但是测试呢?我们使用RSpec;在 CI 中,没有内置资产,开发人员不想每次运行

esbuild
时都必须记住运行
assets:precompile
 或
rspec
 或其他任何内容。别的不说,就是很慢。

当您想要使用最新资产运行测试时,基于 Yarn/Node 的工作流程中专门使用

cssbundling-rails
jsbundling-rails
的官方惯用 Rails 7 解决方案是什么?

ruby-on-rails rspec yarnpkg esbuild
3个回答
4
投票

jsbundling-rails

cssbundling-rails
都将自己附加到名为
test:prepare
的rake任务中。

有几种方法可以使

test:prepare

 运行,具体取决于您的整体构建过程。

  1. 直接拨打:

    bundle exec rails test:prepare test

    
    

    或者,如果在

    rails

     命令之外运行 rspec:

    bundle exec rails test:prepare && bundle exec rspec

    
    

  2. 使用已调用

    test:prepare

    的测试任务。

    奇怪的是,只有一些测试任务调用(依赖于)

    test:prepare

    ,而其他任务(包括默认的 
    test
     任务)则不会。示例:

    bundle exec rails test:all

    
    

  3. 使

    test:prepare

    成为您首选测试任务的依赖项。

    例如,如果您通常通过运行

    spec

     来使用 
    bundle exec rails spec
     任务,请将其添加到新的或现有的任务文件(例如 
    lib/tasks/tests.rake
    )中:

    task spec: ['css:build', 'javascript:build']

    
    

背景

test:prepare

 是 Rails 定义的空任务。 
cssbundling-rails
jsbundling-rails
 都将自身添加为该任务的依赖项。

一般来说,

test:prepare

是一个有用的地方,可以添加运行测试所需的任何类型的依赖项,但需要注意的是,只有一些 Rails 的默认测试任务依赖于它。但如上所述,您始终可以直接调用它或添加自己的依赖项。

在大多数情况下,调用

test:prepare

 相当于同时调用 
css:build
javascript:build
,这就是我在上述大多数示例中显示 
test:prepare
 的原因。有时,其他 gem 或您的应用程序也可能会扩展 
test:prepare
 以及其他命令,在这种情况下,这些命令也将运行(并且可能需要)。

另请注意,

assets:precompile

 还取决于 
css:build
javascript:build
。根据我的经验,
test:prepare
(或单独的
css:build
javascript:build
)比
assets:precompile
运行得更快,可能是因为我们正在运行
sprockets-rails
(而不是
propshaft
)和
assets:precompile
的轻量级配置运行整个链轮编译过程。


4
投票
这很困难,但现在总比没有好;它将确保 CI 始终构建资产,并确保本地开发始终拥有最新的资产,即使事情已经被修改,例如

bin/dev

 未运行。

# Under Rails 7 with 'cssbundling-rails' and/or the 'jsbundling-rails' gems, # entirely external systems are used for asset management. With Sprockets no # longer synchronously building assets on-demand and only when the source files # changed, compiled assets might be (during local development) or will almost # always be (CI systems) either out of date or missing when tests are run. # # People are used to "bundle exec rspec" and things working. The out-of-box gem # 'cssbundling-rails' hooks into a vanilla Rails "prepare" task, running a full # "css:build" task in response. This is quite slow and generates console spam # on every test run, but points to a slightly better solution for RSpec. # # This class is a way of packaging that solution. The class wrapper is really # just a namespace / container for the code. # # First, if you aren't already doing this, add the folllowing lines to # "spec_helper.rb" somewhere *after* the "require 'rspec/rails'" line: # # require 'rake' # YourAppName::Application.load_tasks # # ...and call MaintainTestAssets::maintain! (see that method's documentation # for details). See also constants MaintainTestAssets::ASSET_SOURCE_FOLDERS and # MaintainTestAssets::EXPECTED_ASSETS for things you may want to customise. # class MaintainTestAssets # All the places where you have asset files of any kind that you expect to be # dynamically compiled/transpiled/etc. via external tooling. The given arrays # are passed to "Rails.root.join..." to generate full pathnames. # # Folders are checked recursively. If any file timestamp therein is greater # than (newer than) any of EXPECTED_ASSETS, a rebuild is triggered. # ASSET_SOURCE_FOLDERS = [ ['app', 'assets', 'stylesheets'], ['app', 'javascript'], ['vendor'] ] # The leaf files that ASSET_SOURCE_FOLDERS will build. These are all checked # for in "File.join(Rails.root, 'app', 'assets', 'builds')". Where files are # written together - e.g. a ".js" and ".js.map" file - you only need to list # any one of the group of concurrently generated files. # # In a standard JS / CSS combination this would just be 'application.css' and # 'application.js', but more complex applications might have added or changed # entries in the "scripts" section of 'package.json'. # EXPECTED_ASSETS = %w{ application.js application.css } # Call this method somewhere at test startup, e.g. in "spec_helper.rb" before # tests are actually run (just above "RSpec.configure..." works reasonably). # def self.maintain! run_build = false newest_mtime = Time.now - 100.years # Find the newest modificaftion time across all source files of any type - # for simplicity, timestamps of JS vs CSS aren't considered # ASSET_SOURCE_FOLDERS.each do | relative_array | glob_path = Rails.root.join(*relative_array, '**', '*') Dir[glob_path].each do | filename | next if File.directory?(filename) # NOTE EARLY LOOP RESTART source_mtime = File.mtime(filename) newest_mtime = source_mtime if source_mtime > newest_mtime end end # Compile the built asset leaf names into full file names for convenience. # built_assets = EXPECTED_ASSETS.map do | leaf | Rails.root.join('app', 'assets', 'builds', leaf) end # If any of the source files are newer than expected built assets, or if # any of those assets are missing, trigger a rebuild task *and* force a new # timestamp on all output assets (just in case build script optimisations # result in a file being skipped as "already up to date", which would cause # the code here to otherwise keep trying to rebuild it on every run). # run_build = built_assets.any? do | filename | File.exist?(filename) == false || File.mtime(filename) < newest_mtime end if run_build Rake::Task['javascript:build'].invoke() Rake::Task[ 'css:build'].invoke() built_assets.each { | filename | FileUtils.touch(filename, nocreate: true) } end end end
(编辑)正如下面的评论者指出的,您需要确保 Rake 任务已加载到您的 

spec_helper.rb

 中,例如:

require 'rake' Rails.application.load_tasks
    

0
投票
我最近在新的 Rails 7.1.2 应用程序上使用 RSpec 时遇到了同样的困境。

在阅读了 @tm 上面写的精彩答案后,我运行

rails -T

 寻找 RSpec 的类似任务并“发现”了这个:

bin/rails spec # Run all specs in spec directory (excluding plugin specs)
它还调用@tm提到的

test:prepare

© www.soinside.com 2019 - 2024. All rights reserved.