diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4af73090..40ce3031 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,7 +24,7 @@ jobs: with: bundler-cache: true - run: bundle exec rake compile:local - - run: bundle exec sus + - run: bin/test rubocop: runs-on: "ubuntu-latest" diff --git a/.rubocop.yml b/.rubocop.yml index feb08abf..c45472a3 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -2,6 +2,7 @@ require: - rubocop-packaging - rubocop-performance - rubocop-rake + - rubocop-minitest inherit_mode: merge: @@ -20,8 +21,7 @@ AllCops: Layout/LineLength: Max: 100 - Exclude: - - "test/**/*" + Metrics/BlockLength: Enabled: false Metrics/MethodLength: @@ -34,7 +34,11 @@ Metrics/PerceivedComplexity: Enabled: false Metrics/ClassLength: Enabled: false + Style/Documentation: Enabled: false Style/ClassAndModuleChildren: Enabled: false + +Minitest/EmptyLineBeforeAssertionMethods: + Enabled: false diff --git a/Gemfile b/Gemfile index 2f67f706..aa5e4961 100644 --- a/Gemfile +++ b/Gemfile @@ -5,16 +5,17 @@ source 'https://rubygems.org' # Specify your gem's dependencies in proscenium.gemspec gemspec +gem 'debug' gem 'rails', '~> 7.0' group :development do gem 'benchmark-ips' - gem 'debug' gem 'puma' gem 'rubocop' - gem 'rubocop-packaging' - gem 'rubocop-performance' - gem 'rubocop-rake' + gem 'rubocop-minitest', require: false + gem 'rubocop-packaging', require: false + gem 'rubocop-performance', require: false + gem 'rubocop-rake', require: false gem 'sqlite3' gem 'web-console' @@ -33,7 +34,9 @@ group :test do gem 'gem2', path: './fixtures/external/gem2' gem 'gem3', path: './fixtures/dummy/vendor/gem3' gem 'gem4', path: './fixtures/external/gem4' + gem 'maxitest' + gem 'minitest-focus' + gem 'minitest-spec-rails' gem 'phlex-testing-capybara' - gem 'sus' gem 'view_component', '~> 3.6.0' end diff --git a/Gemfile.lock b/Gemfile.lock index bbaed6a9..320a28ff 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -171,7 +171,7 @@ GEM irb (1.14.1) rdoc (>= 4.0.0) reline (>= 0.4.2) - json (2.7.4) + json (2.7.5) language_server-protocol (3.17.0.3) logger (1.6.1) loofah (2.23.1) @@ -184,9 +184,16 @@ GEM net-smtp marcel (1.0.4) matrix (0.4.2) + maxitest (5.7.1) + minitest (>= 5.14.0, < 5.26.0) method_source (1.1.0) mini_mime (1.1.5) minitest (5.25.1) + minitest-focus (1.4.0) + minitest (>= 4, < 6) + minitest-spec-rails (7.3.0) + minitest (>= 5.0) + railties (>= 4.1) net-imap (0.5.0) date net-protocol @@ -284,6 +291,9 @@ GEM unicode-display_width (>= 2.4.0, < 3.0) rubocop-ast (1.33.0) parser (>= 3.3.1.0) + rubocop-minitest (0.36.0) + rubocop (>= 1.61, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) rubocop-packaging (0.5.2) rubocop (>= 1.33, < 2.0) rubocop-performance (1.22.1) @@ -302,10 +312,9 @@ GEM parser (>= 3.0.3.1) ruby-progressbar (1.13.0) securerandom (0.3.1) - sqlite3 (2.1.1-arm64-darwin) - sqlite3 (2.1.1-x86_64-linux-gnu) + sqlite3 (2.2.0-arm64-darwin) + sqlite3 (2.2.0-x86_64-linux-gnu) stringio (3.1.1) - sus (0.31.0) thor (1.3.2) timeout (0.4.1) tzinfo (2.0.6) @@ -351,6 +360,9 @@ DEPENDENCIES gem3! gem4! htmlbeautifier + maxitest + minitest-focus + minitest-spec-rails phlex-testing-capybara phlexible proscenium! @@ -358,13 +370,13 @@ DEPENDENCIES rails (~> 7.0) rouge rubocop + rubocop-minitest rubocop-packaging rubocop-performance rubocop-rake sqlite3 - sus view_component (~> 3.6.0) web-console BUNDLED WITH - 2.4.1 + 2.5.22 diff --git a/README.md b/README.md index b15ada22..e97b5426 100644 --- a/README.md +++ b/README.md @@ -890,7 +890,7 @@ bundle exec rake compile:local We have tests for both Ruby and Go. To run the Ruby tests: ```bash -bundle exec sus +bin/test ``` To run the Go tests: diff --git a/Rakefile b/Rakefile index 620fd40f..cb518fa9 100644 --- a/Rakefile +++ b/Rakefile @@ -1,22 +1,11 @@ # frozen_string_literal: true require 'bundler/setup' +require 'bundler/gem_tasks' APP_RAKEFILE = File.expand_path('playground/Rakefile', __dir__) load 'rails/tasks/engine.rake' -require 'bundler/gem_tasks' -require 'rake/testtask' -require 'rubocop/rake_task' - -Rake::TestTask.new(:test) do |t| - t.libs << 'test' - t.pattern = 'test/**/*_test.rb' - t.verbose = false - t.warning = false -end - -RuboCop::RakeTask.new CLOBBER.include 'pkg' task default: %i[test rubocop] diff --git a/bin/sus b/bin/sus deleted file mode 100755 index c278d71b..00000000 --- a/bin/sus +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -# -# This file was generated by Bundler. -# -# The application 'sus' is installed as part of a gem, and -# this file is here to facilitate running it. -# - -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) - -bundle_binstub = File.expand_path('bundle', __dir__) - -if File.file?(bundle_binstub) - if File.read(bundle_binstub, 300).include?('This file was generated by Bundler') - load(bundle_binstub) - else - abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. -Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") - end -end - -require 'rubygems' -require 'bundler/setup' - -load Gem.bin_path('sus', 'sus') diff --git a/bin/sus-host b/bin/sus-host deleted file mode 100755 index bea5584d..00000000 --- a/bin/sus-host +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -# -# This file was generated by Bundler. -# -# The application 'sus-host' is installed as part of a gem, and -# this file is here to facilitate running it. -# - -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) - -bundle_binstub = File.expand_path('bundle', __dir__) - -if File.file?(bundle_binstub) - if File.read(bundle_binstub, 300).include?('This file was generated by Bundler') - load(bundle_binstub) - else - abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. -Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") - end -end - -require 'rubygems' -require 'bundler/setup' - -load Gem.bin_path('sus', 'sus-host') diff --git a/bin/sus-parallel b/bin/sus-parallel deleted file mode 100755 index 3ac7eed6..00000000 --- a/bin/sus-parallel +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -# -# This file was generated by Bundler. -# -# The application 'sus-parallel' is installed as part of a gem, and -# this file is here to facilitate running it. -# - -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) - -bundle_binstub = File.expand_path('bundle', __dir__) - -if File.file?(bundle_binstub) - if File.read(bundle_binstub, 300).include?('This file was generated by Bundler') - load(bundle_binstub) - else - abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. -Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") - end -end - -require 'rubygems' -require 'bundler/setup' - -load Gem.bin_path('sus', 'sus-parallel') diff --git a/bin/sus-tree b/bin/sus-tree deleted file mode 100755 index fa3c26d6..00000000 --- a/bin/sus-tree +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -# -# This file was generated by Bundler. -# -# The application 'sus-tree' is installed as part of a gem, and -# this file is here to facilitate running it. -# - -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) - -bundle_binstub = File.expand_path('bundle', __dir__) - -if File.file?(bundle_binstub) - if File.read(bundle_binstub, 300).include?('This file was generated by Bundler') - load(bundle_binstub) - else - abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. -Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") - end -end - -require 'rubygems' -require 'bundler/setup' - -load Gem.bin_path('sus', 'sus-tree') diff --git a/bin/test b/bin/test new file mode 100755 index 00000000..5516a12b --- /dev/null +++ b/bin/test @@ -0,0 +1,5 @@ +#!/usr/bin/env ruby +$: << File.expand_path("../test", __dir__) + +require "bundler/setup" +require "rails/plugin/test" diff --git a/config/sus.rb b/config/sus.rb deleted file mode 100644 index 72d75228..00000000 --- a/config/sus.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -ENV['RAILS_ENV'] = 'test' - -require_relative 'sus/include' - -Bundler.require :default, :test - -require 'proscenium' -require_relative '../fixtures/dummy/config/environment' diff --git a/config/sus/include.rb b/config/sus/include.rb deleted file mode 100644 index c792b203..00000000 --- a/config/sus/include.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module Sus - class Include - def initialize(to_include) - @to_include = to_include - end - - def print(output) - output.write('include ', :include, :variable, @to_include, :reset) - end - - def call(assertions, subject) - assertions.nested(self) do |x| - x.assert(subject.include?(@to_include)) - end - end - end - - class Base - def include(...) - Include.new(...) - end - end -end diff --git a/fixtures/file_path/module.rb b/fixtures/file_path/module.rb deleted file mode 100644 index f9554e6c..00000000 --- a/fixtures/file_path/module.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -module Fixtures::Module - include Proscenium::FilePath -end diff --git a/fixtures/fixtures.rb b/fixtures/fixtures.rb deleted file mode 100644 index a03b4ec9..00000000 --- a/fixtures/fixtures.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -module Fixtures - module_function - - def path(*path) - File.expand_path(File.join(__dir__, *path)) - end - - def app_path(*path) - File.expand_path(File.join(__dir__, '../test/dummy', *path)) - end -end diff --git a/fixtures/system_testing.rb b/fixtures/system_testing.rb deleted file mode 100644 index 66a6f586..00000000 --- a/fixtures/system_testing.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true - -require 'capybara/rails' -require 'capybara/cuprite' -require_relative 'system_testing/console_logger' - -Capybara.register_driver :cuprite do |app| - Capybara::Cuprite::Driver.new( - app, - # Enable debugging by setting the INSPECTOR environment variable to true, and inserting the - # following into the code you want to debug: - # - # page.driver.debug(binding) - inspector: ENV.fetch('INSPECTOR', nil), - logger: SystemTesting::ConsoleLogger.new - ) -end - -Capybara.default_max_wait_time = 5 -Capybara.default_driver = :cuprite -Capybara.javascript_driver = :cuprite - -# Reduce extra logs produced by puma booting up -Capybara.server = :puma, { Silent: true } - -# Include into any `describe` or `with` block with `include_context SystemTest`. -SystemTest = Sus::Shared('system test') do - include Capybara::DSL - - def after(error = nil) - super - Capybara.reset_sessions! - end -end - -class Capybara::Session - def console_logs - driver.browser.options.logger.logs - end - - def console_messages - driver.browser.options.logger.messages - end -end diff --git a/test/application_system_test_case.rb b/test/application_system_test_case.rb new file mode 100644 index 00000000..935011d8 --- /dev/null +++ b/test/application_system_test_case.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require 'test_helper' +require 'capybara/rails' +require 'capybara/cuprite' + +class ApplicationSystemTestCase < ActionDispatch::SystemTestCase + driven_by :cuprite, using: :chrome, screen_size: [1400, 1400], options: { js_errors: true } +end + +Capybara.default_max_wait_time = 5 +Capybara.default_driver = :cuprite +Capybara.javascript_driver = :cuprite + +Capybara.configure do |config| + config.server = :puma, { Silent: true } +end + +# Capybara.register_driver :cuprite do |app| +# Capybara::Cuprite::Driver.new( +# app, +# # Enable debugging by setting the INSPECTOR environment variable to true, and inserting the +# # following into the code you want to debug: +# # +# # page.driver.debug(binding) +# inspector: ENV.fetch('INSPECTOR', nil), +# logger: SystemTesting::ConsoleLogger.new +# ) +# end + +# class Capybara::Session +# def console_logs +# driver.browser.options.logger.logs +# end + +# def console_messages +# driver.browser.options.logger.messages +# end +# end diff --git a/test/builder_test.rb b/test/builder_test.rb new file mode 100644 index 00000000..070b1f1f --- /dev/null +++ b/test/builder_test.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require 'test_helper' + +class Proscenium::BuilderTest < ActiveSupport::TestCase + before do + Proscenium.config.env_vars = Set.new + end + + let(:subject) { Proscenium::Builder } + + context '.build_to_path' do + it 'builds multiple files' do + exp = %( + lib/code_splitting/son.js::public/assets/lib/code_splitting/son$LAGMAD6O$.js; + lib/code_splitting/daughter.js::public/assets/lib/code_splitting/daughter$7JJ2HGHC$.js + ).gsub(/[[:space:]]/, '') + + assert_equal exp, + subject.build_to_path('lib/code_splitting/son.js;lib/code_splitting/daughter.js') + end + end + + context '.build_to_string' do + it 'replaces NODE_ENV and RAILS_ENV' do + assert_includes subject.build_to_string('lib/env/env.js'), 'console.log("testtest")' + end + + context 'config.env_vars' do + it 'replaces' do + Proscenium.config.env_vars << 'USER_NAME' + ENV['USER_NAME'] = 'joelmoss' + + assert_includes subject.build_to_string('lib/env/extra.js'), 'console.log("joelmoss")' + end + end + + context 'unknown path' do + it 'raise' do + error = assert_raises(Proscenium::Builder::BuildError) do + subject.build_to_string('unknown.js') + end + + assert_equal 'Could not resolve "unknown.js"', error.message + end + end + end + + context '.resolve' do + it 'resolves value' do + assert_equal '/packages/mypackage/index.js', subject.resolve('mypackage') + end + end +end diff --git a/test/proscenium/css_module/transformer.rb b/test/css_module/transformer_test.rb similarity index 58% rename from test/proscenium/css_module/transformer.rb rename to test/css_module/transformer_test.rb index e7f77abd..8cd88ad8 100644 --- a/test/proscenium/css_module/transformer.rb +++ b/test/css_module/transformer_test.rb @@ -1,140 +1,132 @@ # frozen_string_literal: true -describe Proscenium::CssModule::Transformer do - def before - Proscenium::Importer.reset - Proscenium::Resolver.reset - end - - attr_reader :page - - def render(output) - @page = Capybara::Node::Simple.new(output) - end +require 'test_helper' +class Proscenium::CssModule::TransformerTest < ActiveSupport::TestCase describe '#class_names' do it 'transforms class names beginning with @' do names = Proscenium::CssModule::Transformer.class_names('/lib/css_modules/basic', :@title) - expect(names).to be == [['title-c3f452b4', '/lib/css_modules/basic.module.css']] + assert_equal [['title-c3f452b4', '/lib/css_modules/basic.module.css']], names end it 'transforms class names beginning with @ and underscore' do names = Proscenium::CssModule::Transformer.class_names('/lib/css_modules/basic', :@_title) - expect(names).to be == [['_title-c3f452b4', '/lib/css_modules/basic.module.css']] + assert_equal [['_title-c3f452b4', '/lib/css_modules/basic.module.css']], names end it 'passes through regular class names' do names = Proscenium::CssModule::Transformer.class_names('/lib/css_modules/basic', :title) - expect(names).to be == ['title'] + assert_equal ['title'], names end it 'accepts multiple names' do names = Proscenium::CssModule::Transformer.class_names('/lib/css_modules/basic', :title, :@subtitle) - expect(names).to be == ['title', ['subtitle-c3f452b4', '/lib/css_modules/basic.module.css']] + assert_equal ['title', ['subtitle-c3f452b4', '/lib/css_modules/basic.module.css']], names end it 'imports stylesheet' do Proscenium::CssModule::Transformer.class_names('/lib/css_modules/basic', :@title) - expect(Proscenium::Importer.imported).to be == { - '/lib/css_modules/basic.module.css' => { digest: 'c3f452b4' } - } + assert_equal({ + '/lib/css_modules/basic.module.css' => { digest: 'c3f452b4' } + }, Proscenium::Importer.imported) end - with 'local path' do + context 'local path' do it 'transforms class names' do names = Proscenium::CssModule::Transformer.class_names('/lib/css_modules/basic', '/lib/css_modules/basic2@title', :@subtitle) - expect(names).to be == [['title-6fd80271', '/lib/css_modules/basic2.module.css'], - ['subtitle-c3f452b4', '/lib/css_modules/basic.module.css']] + assert_equal [['title-6fd80271', '/lib/css_modules/basic2.module.css'], + ['subtitle-c3f452b4', '/lib/css_modules/basic.module.css']], names end it 'imports stylesheets' do Proscenium::CssModule::Transformer.class_names('/lib/css_modules/basic', '/lib/css_modules/basic2@title', :@subtitle) - expect(Proscenium::Importer.imported).to be == { - '/lib/css_modules/basic2.module.css' => { digest: '6fd80271' }, - '/lib/css_modules/basic.module.css' => { digest: 'c3f452b4' } - } + assert_equal({ + '/lib/css_modules/basic2.module.css' => { digest: '6fd80271' }, + '/lib/css_modules/basic.module.css' => { digest: 'c3f452b4' } + }, Proscenium::Importer.imported) end end - with 'npm package path' do + context 'npm package path' do it 'transforms class names' do names = Proscenium::CssModule::Transformer.class_names('/lib/css_modules/basic', 'mypackage/foo@foo') - expect(names).to be == [['foo-39337ba7', '/packages/mypackage/foo.module.css']] + assert_equal [['foo-39337ba7', '/packages/mypackage/foo.module.css']], names end it 'imports stylesheets' do - Proscenium::CssModule::Transformer.class_names('/lib/css_modules/basic', 'mypackage/foo@foo') + Proscenium::CssModule::Transformer.class_names('/lib/css_modules/basic', + 'mypackage/foo@foo') - expect(Proscenium::Importer.imported).to be == { - '/packages/mypackage/foo.module.css' => { digest: '39337ba7' } - } + assert_equal({ + '/packages/mypackage/foo.module.css' => { digest: '39337ba7' } + }, Proscenium::Importer.imported) end end - with 'gem path' do + context 'gem path' do it 'transforms class names' do names = Proscenium::CssModule::Transformer.class_names('/lib/css_modules/basic', '/gem2/lib/gem2/styles@foo') - expect(names).to be == [['foo-a074d644', '/gem2/lib/gem2/styles.module.css']] + assert_equal [['foo-a074d644', '/gem2/lib/gem2/styles.module.css']], names end it 'imports stylesheets' do Proscenium::CssModule::Transformer.class_names('/lib/css_modules/basic', '/gem2/lib/gem2/styles@@foo') - expect(Proscenium::Importer.imported).to be == { - '/gem2/lib/gem2/styles.module.css' => { digest: 'a074d644' } - } + assert_equal({ + '/gem2/lib/gem2/styles.module.css' => { digest: 'a074d644' } + }, Proscenium::Importer.imported) end end end describe '.class_names' do - with 'given path is nil' do + context 'given path is nil' do let(:transformer) { Proscenium::CssModule::Transformer.new(nil) } it 'should raise when transforming class with leading @' do - expect do + assert_raises Proscenium::CssModule::TransformError do transformer.class_names(:@title) - end.to raise_exception Proscenium::CssModule::TransformError + end end it 'should transform regular class' do names = transformer.class_names(:title) - expect(names).to be == ['title'] + assert_equal ['title'], names end it 'should transform local path' do names = transformer.class_names('/lib/css_modules/basic2@title') - expect(names).to be == [['title-6fd80271', '/lib/css_modules/basic2.module.css']] + assert_equal [['title-6fd80271', '/lib/css_modules/basic2.module.css']], names end it 'should transform npm path' do names = transformer.class_names('mypackage/foo@foo') - expect(names).to be == [['foo-39337ba7', '/packages/mypackage/foo.module.css']] + assert_equal [['foo-39337ba7', '/packages/mypackage/foo.module.css']], names end it 'should transform gem path' do names = transformer.class_names('/gem2/lib/gem2/styles@foo') - expect(names).to be == [['foo-a074d644', '/gem2/lib/gem2/styles.module.css']] + assert_equal [['foo-a074d644', '/gem2/lib/gem2/styles.module.css']], names end end end diff --git a/test/proscenium/helper.rb b/test/helper_test.rb similarity index 71% rename from test/proscenium/helper.rb rename to test/helper_test.rb index 82813ba1..acdc856e 100644 --- a/test/proscenium/helper.rb +++ b/test/helper_test.rb @@ -1,58 +1,51 @@ # frozen_string_literal: true -require 'system_testing' - -describe Proscenium::Helper do - def before - Proscenium::Importer.reset - Proscenium::Resolver.reset - end - - attr_reader :page - - def render(output) - @page = Capybara::Node::Simple.new(output) - end +require 'application_system_test_case' +# rubocop:disable Layout/LineLength +class Proscenium::HelperTest < ApplicationSystemTestCase describe '#css_module' do it 'transforms class names beginning with @' do - render CssmHelperController.render :index + page = Capybara::Node::Simple.new(CssmHelperController.render(:index)) - expect(page.has_css?('body.body-ead1b5bc')).to be == true - expect(page.has_css?('h2.view-ba1ab2b7')).to be == true - expect(page.has_css?('div.partial-7800dcdf.world')).to be == true + assert page.has_css?('body.body-ead1b5bc') + assert page.has_css?('h2.view-ba1ab2b7') + assert page.has_css?('div.partial-7800dcdf.world') end end describe '#include_stylesheets' do - include_context SystemTest - it 'includes side loaded stylesheets' do visit '/' - expect(page.html).to include '' - expect(page.html).to include '' + assert_includes page.html, <<~HTML.squish + + HTML + assert_includes page.html, <<~HTML.squish + + HTML end end describe '#include_javascripts' do - include_context SystemTest - it 'includes side loaded javascripts' do visit '/' - expect(page.html).to include '' - expect(page.html).to include '' + assert_includes page.html, <<~HTML.squish + + HTML + assert_includes page.html, <<~HTML.squish + + HTML end end describe '#include_assets' do - include_context SystemTest - it 'includes side loaded assets' do visit '/include_assets' - expect(page.html).to include( + assert_includes( + page.html, '' \ '' \ '' \ @@ -67,82 +60,93 @@ def render(output) end describe '#sideload_assets' do - include_context SystemTest - - with 'false in controller' do + context 'false in controller' do it 'does not include assets' do BarePagesController.sideload_assets false visit '/' - expect(page.html).to include '' + assert_includes page.html, '' ensure BarePagesController.sideload_assets nil end end - with 'proc in controller' do + context 'proc in controller' do it 'does not include assets' do BarePagesController.sideload_assets proc { request.xhr? } visit '/' - expect(page.html).to include '' + assert_includes page.html, '' ensure BarePagesController.sideload_assets nil end end - with 'css: false in controller' do + context 'css: false in controller' do it 'does not includes stylesheets' do BarePagesController.sideload_assets css: false visit '/' - expect(page.html).not.to include '' - expect(page.html).to include '' + assert_includes( + page.html, + '' + ) + assert_includes( + page.html, + '' + ) ensure BarePagesController.sideload_assets nil end end - with 'js: false in controller' do + context 'js: false in controller' do it 'does not includes javascripts' do BarePagesController.sideload_assets js: false visit '/' - expect(page.html).not.to include '' - expect(page.html).to include '' + assert_includes( + page.html, + '' + ) + assert_includes( + page.html, + '' + ) ensure BarePagesController.sideload_assets nil end end - with 'false in controller; true in view template' do + context 'false in controller; true in view template' do it 'excludes all except view template assets' do BarePagesController.sideload_assets false visit '/include_assets?sideload_view_assets=true' - expect(page.html).to include( + assert_includes( + page.html, '' \ '' \ '' \ @@ -153,12 +157,13 @@ def render(output) end end - with 'false in controller; true in partials' do + context 'false in controller; true in partials' do it 'excludes all except partial assets' do BarePagesController.sideload_assets false visit '/include_assets?sideload_partial_assets=true' - expect(page.html).to include( + assert_includes( + page.html, '' \ '' \ '' \ @@ -169,11 +174,12 @@ def render(output) end end - with 'false in view template' do + context 'false in view template' do it 'does not include template view assets' do visit '/include_assets?sideload_view_assets=false' - expect(page.html).to include( + assert_includes( + page.html, '' \ '' \ '' \ @@ -185,11 +191,12 @@ def render(output) end end - with 'false in layout template' do + context 'false in layout template' do it 'does not include template layout assets' do visit '/include_assets?sideload_layout_assets=false' - expect(page.html).to include( + assert_includes( + page.html, '' \ '' \ '' \ @@ -201,11 +208,12 @@ def render(output) end end - with 'false in partial' do + context 'false in partial' do it 'does not include partial assets' do visit '/include_assets?sideload_partial_assets=false' - expect(page.html).to include( + assert_includes( + page.html, '' \ '' \ '' \ @@ -217,11 +225,12 @@ def render(output) end end - with 'false in partial layout' do + context 'false in partial layout' do it 'does not include partial layout assets' do visit '/include_assets?sideload_partial_layout_assets=false' - expect(page.html).to include( + assert_includes( + page.html, '' \ '' \ '' \ @@ -235,3 +244,4 @@ def render(output) end end end +# rubocop:enable Layout/LineLength diff --git a/test/proscenium/importer.rb b/test/importer_test.rb similarity index 56% rename from test/proscenium/importer.rb rename to test/importer_test.rb index 149ce49b..eab4f08d 100644 --- a/test/proscenium/importer.rb +++ b/test/importer_test.rb @@ -1,93 +1,97 @@ # frozen_string_literal: true -describe Proscenium::Importer do - def before +require 'test_helper' + +class Proscenium::ImporterTest < ActiveSupport::TestCase + before do Proscenium::Importer.reset Proscenium::Resolver.reset end - with '.import' do + let(:subject) { Proscenium::Importer } + + describe '.import' do it 'single file' do subject.import '/app/views/layouts/application.js' - expect(subject.imported).to be == { '/app/views/layouts/application.js' => {} } + assert_equal({ '/app/views/layouts/application.js' => {} }, subject.imported) end it 'passes additional kwargs' do subject.import '/app/views/layouts/application.js', name: 'bob' - expect(subject.imported).to be == { - '/app/views/layouts/application.js' => { name: 'bob' } - } + assert_equal({ + '/app/views/layouts/application.js' => { name: 'bob' } + }, subject.imported) end it 'concatanates multiple calls' do subject.import '/app/views/layouts/application.js' subject.import '/app/views/layouts/application.css' - expect(subject.imported).to be == { - '/app/views/layouts/application.js' => {}, - '/app/views/layouts/application.css' => {} - } + assert_equal({ + '/app/views/layouts/application.js' => {}, + '/app/views/layouts/application.css' => {} + }, subject.imported) end it 'deduplicates paths' do subject.import '/app/views/layouts/application.js' subject.import '/app/views/layouts/application.js' - expect(subject.imported).to be == { '/app/views/layouts/application.js' => {} } + assert_equal({ '/app/views/layouts/application.js' => {} }, subject.imported) end it 'imports @proscenium/* runtime files' do subject.import resolve: '@proscenium/react-manager/index.jsx' - expect(subject.imported).to be == { '/@proscenium/react-manager/index.jsx' => {} } + assert_equal({ '/@proscenium/react-manager/index.jsx' => {} }, subject.imported) end end - with '.sideload' do - with 'js and css' do + describe '.sideload' do + context 'js and css' do it 'sideloads' do mock_files 'app/views/user.rb', 'app/views/user.js', 'app/views/user.css' do subject.sideload Rails.root.join('app/views/user.rb') end - expect(subject.imported).to be == { - '/app/views/user.js' => { sideloaded: true }, - '/app/views/user.css' => { sideloaded: true } - } + assert_equal({ + '/app/views/user.js' => { sideloaded: true }, + '/app/views/user.css' => { sideloaded: true } + }, subject.imported) end end - with 'no js, no css' do + context 'no js, no css' do it 'sideloads nothing' do mock_file 'app/views/user.rb' do Proscenium::Importer.sideload Rails.root.join('app/views/user.rb') end - expect(subject.imported).to be_nil + assert_nil subject.imported end end - with 'no js' do + context 'no js' do it 'sideloads' do mock_files 'app/views/user.rb', 'app/views/user.css' do subject.sideload Rails.root.join('app/views/user.rb') end - expect(subject.imported).to be == { '/app/views/user.css' => { sideloaded: true } } + assert_equal({ '/app/views/user.css' => { sideloaded: true } }, subject.imported) end end - with '.module.css and .css' do + context '.module.css and .css' do it 'sideloads css module' do mock_files 'app/views/user.rb', 'app/views/user.css', 'app/views/user.module.css' do subject.sideload Rails.root.join('app/views/user.rb') end - expect(subject.imported).to be == { '/app/views/user.module.css' => { - sideloaded: true, digest: 'ab65a4fd' - } } + assert_equal({ '/app/views/user.module.css' => { + sideloaded: true, digest: 'ab65a4fd' + } }, subject.imported) end end end @@ -103,5 +107,5 @@ def mock_file(*paths) yield end end - alias_method :mock_files, :mock_file + alias mock_files mock_file end diff --git a/test/middleware_test.rb b/test/middleware_test.rb new file mode 100644 index 00000000..41394a5d --- /dev/null +++ b/test/middleware_test.rb @@ -0,0 +1,164 @@ +# frozen_string_literal: true + +require 'test_helper' + +DummyApp = ->(_) { [404, {}, []] } +HelloApp = ->(_) { [200, { 'Content-Type' => 'text/plain' }, ['Hello, World!']] } + +class Proscenium::MiddlewareTest < ActiveSupport::TestCase + attr_reader :response + + before do + Proscenium.config.cache_query_string = false + Proscenium::Importer.reset + Proscenium::Resolver.reset + end + + let(:subject) { Proscenium::Middleware } + let(:app) { subject.new DummyApp } + + ['js', 'mjs', 'ts', 'jsx', 'tsx', 'css', 'js.map', 'mjs.map', 'jsx.map', 'ts.map', 'tsx.map', + 'css.map'].each do |extension| + it "serves .#{extension}" do + get "/lib/extensions/foo.#{extension}" + + assert_equal 200, response.status + end + end + + ['config/foo.js', 'app/views/foo.js', 'lib/foo.js', 'vendor/foo.js', + 'node_modules/mypackage/index.js'].each do |path| + it "serves from #{path}" do + get "/#{path}" + + assert_equal 200, response.status + end + end + + it 'raises on compilation error' do + assert_raises Proscenium::Builder::BuildError do + get '/lib/includes_error.js' + end + end + + context 'unsupported path' do + let(:app) { subject.new HelloApp } + + it 'passes through' do + get '/db/some.js' + + assert_equal 'Hello, World!', response.body + end + end + + context 'vendored engine with package.json' do + it 'serves assets from allowed dirs at /[GEM_NAME]/*' do + get '/gem1/lib/gem1/gem1.js' + + assert_includes response.body, 'console.log("gem1");' + end + end + + context 'vendored engine without package.json' do + it 'serves assets from allowed dirs at /[GEM_NAME]/*' do + get '/gem3/lib/gem3/gem3.js' + + assert_includes response.body, 'console.log("gem3");' + end + end + + context 'un-vendored engine with package.json' do + it 'serves assets from allowed dirs at /[GEM_NAME]/*' do + get '/gem2/lib/gem2/gem2.js' + + assert_includes response.body, 'console.log("gem2");' + end + end + + context 'un-vendored engine without package.json' do + it 'serves assets from allowed dirs at /[GEM_NAME]/*' do + get '/gem4/lib/gem4/gem4.js' + + assert_includes response.body, 'console.log("gem4");' + end + end + + it 'serves javascript' do + get '/lib/foo.js' + + assert_equal 'application/javascript', response.headers['Content-Type'] + assert_includes response.body.squish, %( + console.log("/lib/foo.js"); + //# sourceMappingURL=foo.js.map + ).squish + end + + it 'serves javascript source map' do + get '/lib/foo.js.map' + + assert_equal 'application/json', response.headers['Content-Type'] + assert_includes response.body, %("sources": ["../../../lib/foo.js"]) + end + + it 'serves css' do + get '/lib/foo.css' + + assert_includes response.body.squish, %( + .body { color: red; } + /*# sourceMappingURL=foo.css.map */ + ).squish + end + + it 'serves css source map' do + get '/lib/foo.css.map' + + assert_equal 'application/json', response.headers['Content-Type'] + assert_includes response.body, %("sources": ["../../../lib/foo.css"]) + end + + it 'serves css module' do + get '/lib/styles.module.css' + + assert_equal 'text/css', response.headers['Content-Type'] + assert_includes response.body.squish, %( + .myClass-330940eb { color: pink; } + /*# sourceMappingURL=styles.module.css.map */ + ).squish + end + + it 'serves @proscenium/* runtime libs' do + get '/@proscenium/test.js' + + assert_includes response.body, 'console.log("/@proscenium/test.js")' + end + + it 'serves proscenium/ui/* ui' do + get '/proscenium/ui/test.js' + + assert_includes response.body, 'console.log("/proscenium/ui/test.js")' + end + + context 'cache_query_string' do + it 'should set cache header ' do + Proscenium.config.cache_query_string = 'v1' + get '/lib/query_cache.js' + + assert_equal 'public, max-age=2592000', response.headers['Cache-Control'] + end + + it 'should propogate cache_query_string' do + skip 'TODO' + + Proscenium.config.cache_query_string = 'v1' + get '/lib/query_cache.js?v1' + + assert_includes response.body, 'console.log("/lib/query_cache.js")' + end + end + + private + + def get(path) + @response = Rack::MockRequest.new(app).request('GET', path) + end +end diff --git a/test/proscenium/phlex/asset_inclusions.rb b/test/phlex/asset_inclusions_test.rb similarity index 71% rename from test/proscenium/phlex/asset_inclusions.rb rename to test/phlex/asset_inclusions_test.rb index 49924a89..dd6b8660 100644 --- a/test/proscenium/phlex/asset_inclusions.rb +++ b/test/phlex/asset_inclusions_test.rb @@ -1,23 +1,18 @@ # frozen_string_literal: true -require 'system_testing' +require 'application_system_test_case' require 'phlex/testing/rails/view_helper' require 'phlex/testing/capybara' -describe Proscenium::Phlex::AssetInclusions do - def before - Proscenium::Importer.reset - Proscenium::Resolver.reset - end - +# rubocop:disable Layout/LineLength +class Proscenium::Phlex::AssetInclusionsTest < ApplicationSystemTestCase describe '#include_assets' do - include_context SystemTest - - with 'controller is false; view is true' do + context 'controller is false; view is true' do it 'includes side loaded assets' do visit '/phlex/include_assets' - expect(page.html).to include( + assert_includes( + page.html, '' \ '' \ '' \ @@ -28,3 +23,4 @@ def before end end end +# rubocop:enable Layout/LineLength diff --git a/test/proscenium/phlex/css_modules.rb b/test/phlex/css_modules_test.rb similarity index 54% rename from test/proscenium/phlex/css_modules.rb rename to test/phlex/css_modules_test.rb index 72c69dd3..7b38ea84 100644 --- a/test/proscenium/phlex/css_modules.rb +++ b/test/phlex/css_modules_test.rb @@ -1,31 +1,27 @@ # frozen_string_literal: true +require 'test_helper' require 'phlex/testing/rails/view_helper' require 'phlex/testing/capybara' -describe Proscenium::Phlex::CssModules do +class Proscenium::Phlex::CssModulesTest < ActiveSupport::TestCase include Phlex::Testing::Rails::ViewHelper include Phlex::Testing::Capybara::ViewHelper - def before - Proscenium::Importer.reset - Proscenium::Resolver.reset - end - describe 'class attribute' do - with 'plain class name' do + context 'plain class name' do it 'should not use css module name' do render Phlex::SideLoadCssModuleFromAttributesView.new('base') - expect(page.has_css?('div.base', text: 'Hello')).to be == true + assert page.has_css?('div.base', text: 'Hello') end end - with 'css module class name' do + context 'css module class name' do it 'should use css module name' do render Phlex::SideLoadCssModuleFromAttributesView.new(:@base) - expect(page.has_css?('div.base-02dcd653', text: 'Hello')).to be == true + assert page.has_css?('div.base-02dcd653', text: 'Hello') end end end @@ -34,7 +30,7 @@ def before it 'replaces with CSS module name' do render Phlex::CssModuleHelperComponent.new - expect(page.has_css?('h1.header-ab5b1c05', text: 'Hello')).to be == true + assert page.has_css?('h1.header-ab5b1c05', text: 'Hello') end end @@ -43,31 +39,31 @@ def before father = Phlex::Father.css_module_path child = Phlex::Child.css_module_path - expect(father).to be == child + assert_equal father, child end end - with 'child and parent css module path' do + context 'child and parent css module path' do it 'uses child' do render Phlex::Father.new - expect(page.has_css?('h1.grandfather-267f8f06', text: 'Grandfather')).to be == true + assert page.has_css?('h1.grandfather-267f8f06', text: 'Grandfather') end end - with 'parent and no child css module path' do + context 'parent and no child css module path' do it 'uses parent' do render Phlex::Child.new - expect(page.has_css?('h1.grandfather-267f8f06', text: 'Grandfather')).to be == true + assert page.has_css?('h1.grandfather-267f8f06', text: 'Grandfather') end end - with 'child and no parent css module path' do + context 'child and no parent css module path' do it 'uses parent' do render Phlex::Grandfather.new - expect(page.has_css?('h1.grandfather-06141f76', text: 'Grandfather')).to be == true + assert page.has_css?('h1.grandfather-06141f76', text: 'Grandfather') end end end diff --git a/test/proscenium/phlex/react_component.rb b/test/phlex/react_component_test.rb similarity index 67% rename from test/proscenium/phlex/react_component.rb rename to test/phlex/react_component_test.rb index 9719b069..e1ac3ad9 100644 --- a/test/proscenium/phlex/react_component.rb +++ b/test/phlex/react_component_test.rb @@ -1,18 +1,13 @@ # frozen_string_literal: true +require 'test_helper' require 'phlex/testing/rails/view_helper' require 'phlex/testing/capybara' -require 'system_testing' -describe Proscenium::Phlex::ReactComponent do +class Proscenium::Phlex::ReactComponentTest < ActiveSupport::TestCase include Phlex::Testing::Rails::ViewHelper include Phlex::Testing::Capybara::ViewHelper - def before - Proscenium::Importer.reset - Proscenium::Resolver.reset - end - # describe 'system' do # include_context SystemTest @@ -31,57 +26,59 @@ def before it 'has data-proscenium-component attribute' do render Phlex::BasicReactComponent.new - expect(page.has_css?(selector)).to be == true + assert page.has_css?(selector) end it 'forwards block to content' do render(Phlex::BasicReactComponent.new) { 'Hello' } - expect(page.has_text?('Hello')).to be == true + assert page.has_text?('Hello') end it 'has empty props' do render Phlex::BasicReactComponent.new - expect(page.find(selector)['data-proscenium-component-props']).to be == '{}' + assert_equal '{}', page.find(selector)['data-proscenium-component-props'] end - with 'props' do + context 'props' do it 'should pass through props' do render Phlex::BasicReactComponent.new(props: { name: 'Joel' }) - expect(page.find(selector)['data-proscenium-component-props']).to be == %({"name":"Joel"}) + assert_equal %({"name":"Joel"}), page.find(selector)['data-proscenium-component-props'] end it 'should camelCase props keys' do render Phlex::BasicReactComponent.new(props: { first_name: 'Joel', 'some/last_name': 'Moss' }) - expect(page.find(selector)['data-proscenium-component-props']).to be == %({ - "firstName":"Joel", "some/lastName": "Moss" - }).gsub(/[[:space:]]/, '') + assert_equal( + %({ + "firstName":"Joel", "some/lastName": "Moss" + }).gsub(/[[:space:]]/, ''), + page.find(selector)['data-proscenium-component-props'] + ) end end - with 'root_tag' do - def after(error = nil) + context 'root_tag' do + after do Phlex::BasicReactComponent.root_tag = :div # reset - super end it 'should use the given tag' do Phlex::BasicReactComponent.root_tag = :span render Phlex::BasicReactComponent.new - expect(page.has_css?('span[data-proscenium-component-path]')).to be == true + assert page.has_css?('span[data-proscenium-component-path]') end end it 'should import component manager' do render Phlex::BasicReactComponent.new - expect(Proscenium::Importer.imported['/@proscenium/react-manager/index.jsx']).to be == { - js: { type: 'module' } - } + assert_equal({ + js: { type: 'module' } + }, Proscenium::Importer.imported['/@proscenium/react-manager/index.jsx']) end # describe ':loader' do @@ -95,40 +92,39 @@ def after(error = nil) # end describe 'lazy loading' do - def after(error = nil) + after do Phlex::BasicReactComponent.lazy = false # reset - super end it 'should import component with `lazy: true` option' do render Phlex::BasicReactComponent.new(lazy: true) - expect(Proscenium::Importer.imported['/app/components/phlex/basic_react_component.jsx'][:lazy]).to be == true + assert Proscenium::Importer.imported['/app/components/phlex/basic_react_component.jsx'][:lazy] end - with '`.lazy = true`' do + context '`.lazy = true`' do it 'adds lazy data attribute' do Phlex::BasicReactComponent.lazy = true render Phlex::BasicReactComponent.new - expect(page.has_css?("#{selector}[data-proscenium-component-lazy]")).to be == true + assert page.has_css?("#{selector}[data-proscenium-component-lazy]") end end - with '`.lazy = true` + `#lazy = false`' do + context '`.lazy = true` + `#lazy = false`' do it 'does not add lazy data attribute' do Phlex::BasicReactComponent.lazy = true render Phlex::BasicReactComponent.new(lazy: false) - expect(page.has_css?("#{selector}[data-proscenium-component-lazy]")).to be == false + assert_not page.has_css?("#{selector}[data-proscenium-component-lazy]") end end - with '`.lazy = false` (default) + `#lazy = true`' do + context '`.lazy = false` (default) + `#lazy = true`' do it 'adds lazy data attribute' do render Phlex::BasicReactComponent.new(lazy: true) - expect(page.has_css?("#{selector}[data-proscenium-component-lazy]")).to be == true + assert page.has_css?("#{selector}[data-proscenium-component-lazy]") end end @@ -147,10 +143,9 @@ def after(error = nil) # end end - with '`.forward_children = true`' do - def after(error = nil) + context '`.forward_children = true`' do + after do Phlex::React::ForwardChildren::Component.forward_children = true - super end let(:selector) do @@ -160,16 +155,16 @@ def after(error = nil) it 'adds forward-children data attribute' do render(Phlex::React::ForwardChildren::Component.new) { 'Hello' } - expect(page.has_css?("#{selector}[data-proscenium-component-forward-children]")).to be == true + assert page.has_css?("#{selector}[data-proscenium-component-forward-children]") end it 'renders content block as children' do render(Phlex::React::ForwardChildren::Component.new) { 'Hello' } - expect(page.has_text?('Hello')).to be == true + assert page.has_text?('Hello') end - # with 'system' do + # context 'system' do # include_context SystemTest # it 'forwards block in children prop' do @@ -180,15 +175,13 @@ def after(error = nil) # end end - with '`.forward_children = false`' do - def before + context '`.forward_children = false`' do + before do Phlex::React::ForwardChildren::Component.forward_children = false - super end - def after(error = nil) + after do Phlex::React::ForwardChildren::Component.forward_children = true - super end let(:selector) do @@ -198,16 +191,16 @@ def after(error = nil) it 'does not adds forward-children data attribute' do render(Phlex::React::ForwardChildren::Component.new) { 'Hello' } - expect(page.has_css?("#{selector}[data-proscenium-component-forward-children]")).to be == false + assert_not page.has_css?("#{selector}[data-proscenium-component-forward-children]") end it 'renders content block as children' do render(Phlex::React::ForwardChildren::Component.new) { 'Hello' } - expect(page.has_text?('Hello')).to be == true + assert page.has_text?('Hello') end - # with 'system' do + # context 'system' do # include_context SystemTest # it 'does not forward block in children prop' do diff --git a/test/phlex_test.rb b/test/phlex_test.rb new file mode 100644 index 00000000..996a82f9 --- /dev/null +++ b/test/phlex_test.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'test_helper' +require 'phlex/testing/rails/view_helper' + +class Proscenium::PhlexTest < ActiveSupport::TestCase + include Phlex::Testing::Rails::ViewHelper + + it 'side loads component js and css' do + render Phlex::SideLoadView.new + + assert_equal({ + '/app/components/phlex/side_load_view.css' => { sideloaded: true }, + '/app/components/phlex/side_load_view.js' => { sideloaded: true } + }, Proscenium::Importer.imported) + end + + test 'nested side load' do + render Phlex::NestedSideLoadView.new + + assert_equal({ + '/app/components/phlex/nested_side_load_view.css' => { sideloaded: true }, + '/app/components/phlex/side_load_view.css' => { sideloaded: true }, + '/app/components/phlex/side_load_view.js' => { sideloaded: true } + }, Proscenium::Importer.imported) + end +end diff --git a/test/proscenium/builder.rb b/test/proscenium/builder.rb deleted file mode 100644 index 09ca20c2..00000000 --- a/test/proscenium/builder.rb +++ /dev/null @@ -1,48 +0,0 @@ -# frozen_string_literal: true - -describe Proscenium::Builder do - def before - Proscenium::Importer.reset - Proscenium::Resolver.reset - Proscenium.config.env_vars = Set.new - end - - with '.build_to_path' do - it 'builds multiple files' do - expect(subject.build_to_path('lib/code_splitting/son.js;lib/code_splitting/daughter.js')).to be == %( - lib/code_splitting/son.js::public/assets/lib/code_splitting/son$LAGMAD6O$.js; - lib/code_splitting/daughter.js::public/assets/lib/code_splitting/daughter$7JJ2HGHC$.js - ).gsub(/[[:space:]]/, '') - end - end - - with '.build_to_string' do - it 'replaces NODE_ENV and RAILS_ENV' do - expect(subject.build_to_string('lib/env/env.js')).to include 'console.log("testtest")' - end - - with 'config.env_vars' do - it 'replaces' do - Proscenium.config.env_vars << 'USER_NAME' - ENV['USER_NAME'] = 'joelmoss' - - expect(subject.build_to_string('lib/env/extra.js')).to include 'console.log("joelmoss")' - end - end - - with 'unknown path' do - it 'raise' do - expect do - subject.build_to_string('unknown.js') - end.to raise_exception(Proscenium::Builder::BuildError, - message: be == 'Could not resolve "unknown.js"') - end - end - end - - with '.resolve' do - it 'resolves value' do - expect(subject.resolve('mypackage')).to be == '/packages/mypackage/index.js' - end - end -end diff --git a/test/proscenium/middleware.rb b/test/proscenium/middleware.rb deleted file mode 100644 index 4fe442a1..00000000 --- a/test/proscenium/middleware.rb +++ /dev/null @@ -1,177 +0,0 @@ -# frozen_string_literal: true - -DummyApp = ->(_) { [404, {}, []] } -HelloApp = ->(_) { [200, { 'Content-Type' => 'text/plain' }, ['Hello, World!']] } - -SupportedExtension = Sus::Shared('supported file extension') do |args| - it "serves .#{args[:extension]}" do - get "/lib/extensions/foo.#{args[:extension]}" - - expect(response.status).to be == 200 - end -end - -IncludedPath = Sus::Shared('included path') do |args| - it "serves from #{args[:path]}" do - get "/#{args[:path]}" - - expect(response.status).to be == 200 - end -end - -describe Proscenium::Middleware do - attr_reader :response - - def before - Proscenium.config.cache_query_string = false - Proscenium::Importer.reset - Proscenium::Resolver.reset - end - - let(:app) { subject.new DummyApp } - - it_behaves_like SupportedExtension, { extension: 'js' } - it_behaves_like SupportedExtension, { extension: 'mjs' } - it_behaves_like SupportedExtension, { extension: 'ts' } - it_behaves_like SupportedExtension, { extension: 'jsx' } - it_behaves_like SupportedExtension, { extension: 'tsx' } - it_behaves_like SupportedExtension, { extension: 'css' } - it_behaves_like SupportedExtension, { extension: 'js.map' } - it_behaves_like SupportedExtension, { extension: 'mjs.map' } - it_behaves_like SupportedExtension, { extension: 'jsx.map' } - it_behaves_like SupportedExtension, { extension: 'ts.map' } - it_behaves_like SupportedExtension, { extension: 'tsx.map' } - it_behaves_like SupportedExtension, { extension: 'css.map' } - - it_behaves_like IncludedPath, { path: 'config/foo.js' } - it_behaves_like IncludedPath, { path: 'app/views/foo.js' } - it_behaves_like IncludedPath, { path: 'lib/foo.js' } - it_behaves_like IncludedPath, { path: 'vendor/foo.js' } - it_behaves_like IncludedPath, { path: 'node_modules/mypackage/index.js' } - - it 'raises on compilation error' do - expect do - get '/lib/includes_error.js' - end.to raise_exception(Proscenium::Builder::BuildError) - end - - with 'unsupported path' do - let(:app) { subject.new HelloApp } - - it 'passes through' do - get '/db/some.js' - expect(response.body).to be == 'Hello, World!' - end - end - - with 'vendored engine with package.json' do - it 'serves assets from allowed dirs at /[GEM_NAME]/*' do - get '/gem1/lib/gem1/gem1.js' - - expect(response.body).to include 'console.log("gem1");' - end - end - - with 'vendored engine without package.json' do - it 'serves assets from allowed dirs at /[GEM_NAME]/*' do - get '/gem3/lib/gem3/gem3.js' - - expect(response.body).to include 'console.log("gem3");' - end - end - - with 'un-vendored engine with package.json' do - it 'serves assets from allowed dirs at /[GEM_NAME]/*' do - get '/gem2/lib/gem2/gem2.js' - - expect(response.body).to include 'console.log("gem2");' - end - end - - with 'un-vendored engine without package.json' do - it 'serves assets from allowed dirs at /[GEM_NAME]/*' do - get '/gem4/lib/gem4/gem4.js' - - expect(response.body).to include 'console.log("gem4");' - end - end - - it 'serves javascript' do - get '/lib/foo.js' - - expect(response.headers['Content-Type']).to be == 'application/javascript' - expect(response.body.squish).to include %( - console.log("/lib/foo.js"); - //# sourceMappingURL=foo.js.map - ).squish - end - - it 'serves javascript source map' do - get '/lib/foo.js.map' - - expect(response.headers['Content-Type']).to be == 'application/json' - expect(response.body).to include %("sources": ["../../../lib/foo.js"]) - end - - it 'serves css' do - get '/lib/foo.css' - - expect(response.body.squish).to include %( - .body { color: red; } - /*# sourceMappingURL=foo.css.map */ - ).squish - end - - it 'serves css source map' do - get '/lib/foo.css.map' - - expect(response.headers['Content-Type']).to be == 'application/json' - expect(response.body).to include %("sources": ["../../../lib/foo.css"]) - end - - it 'serves css module' do - get '/lib/styles.module.css' - - expect(response.headers['Content-Type']).to be == 'text/css' - expect(response.body.squish).to include %( - .myClass-330940eb { color: pink; } - /*# sourceMappingURL=styles.module.css.map */ - ).squish - end - - it 'serves @proscenium/* runtime libs' do - get '/@proscenium/test.js' - - expect(response.body).to include('console.log("/@proscenium/test.js")') - end - - it 'serves proscenium/ui/* ui' do - get '/proscenium/ui/test.js' - - expect(response.body).to include('console.log("/proscenium/ui/test.js")') - end - - with 'cache_query_string' do - it 'should set cache header ' do - Proscenium.config.cache_query_string = 'v1' - get '/lib/query_cache.js' - - expect(response.headers['Cache-Control']).to be == 'public, max-age=2592000' - end - - it 'should propogate cache_query_string' do - skip 'TODO' - - Proscenium.config.cache_query_string = 'v1' - get '/lib/query_cache.js?v1' - - expect(response.body).to include 'console.log("/lib/query_cache.js")' - end - end - - private - - def get(path) - @response = Rack::MockRequest.new(app).request('GET', path) - end -end diff --git a/test/proscenium/phlex.rb b/test/proscenium/phlex.rb deleted file mode 100644 index 745d2cd4..00000000 --- a/test/proscenium/phlex.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -require 'phlex/testing/rails/view_helper' - -describe Proscenium::Phlex do - include Phlex::Testing::Rails::ViewHelper - - def before - Proscenium::Importer.reset - Proscenium::Resolver.reset - end - - it 'side loads component js and css' do - render Phlex::SideLoadView.new - - expect(Proscenium::Importer.imported).to be == { - '/app/components/phlex/side_load_view.css' => { sideloaded: true }, - '/app/components/phlex/side_load_view.js' => { sideloaded: true } - } - end - - it 'nested side load' do - render Phlex::NestedSideLoadView.new - - expect(Proscenium::Importer.imported).to be == { - '/app/components/phlex/nested_side_load_view.css' => { sideloaded: true }, - '/app/components/phlex/side_load_view.css' => { sideloaded: true }, - '/app/components/phlex/side_load_view.js' => { sideloaded: true } - } - end -end diff --git a/test/proscenium/resolver.rb b/test/proscenium/resolver.rb deleted file mode 100644 index f06c483b..00000000 --- a/test/proscenium/resolver.rb +++ /dev/null @@ -1,50 +0,0 @@ -# frozen_string_literal: true - -describe Proscenium::Resolver do - def before - Proscenium::Importer.reset - Proscenium::Resolver.reset - end - - with '.resolve' do - with value: './foo' do - it 'raises' do - expect do - subject.resolve(value) - end.to raise_exception(ArgumentError, message: be == 'path must be an absolute file system or URL path') - end - end - - with 'unknown path', value: 'unknown' do - it 'raises' do - expect do - subject.resolve(value) - end.to raise_exception(Proscenium::Builder::ResolveError) - end - end - - with 'bare specifier (NPM package)', value: 'mypackage' do - it 'resolves' do - expect(subject.resolve(value)).to be == '/packages/mypackage/index.js' - end - end - - with 'absolute file system path', value: Rails.root.join('lib/foo.js').to_s do - it 'resolves' do - expect(subject.resolve(value)).to be == '/lib/foo.js' - end - end - - with 'absolute URL path', value: '/lib/foo.js' do - it 'resolves' do - expect(subject.resolve(value)).to be == '/lib/foo.js' - end - end - - with '@proscenium runtime', value: '@proscenium/react-manager/index.jsx' do - it 'resolves' do - expect(subject.resolve(value)).to be == '/@proscenium/react-manager/index.jsx' - end - end - end -end diff --git a/test/proscenium/side_load.rb b/test/proscenium/side_load.rb deleted file mode 100644 index d984667d..00000000 --- a/test/proscenium/side_load.rb +++ /dev/null @@ -1,91 +0,0 @@ -# frozen_string_literal: true - -describe Proscenium::SideLoad do - def before - Proscenium.config.side_load = true - Proscenium::Importer.reset - Proscenium::Resolver.reset - end - - with 'side load disabled' do - def before - super - Proscenium.config.side_load = false - end - - it 'does not side load layout and view' do - BarePagesController.render :home - - expect(Proscenium::Importer.imported).to be(:nil?) - end - - it 'does not side load partial' do - BarePagesController.render :sideloadpartial - - expect(Proscenium::Importer.imported).to be(:nil?) - end - - it 'does not side load vendored gem' do - BarePagesController.render :vendored_gem - - expect(Proscenium::Importer.imported).to be(:nil?) - end - - it 'does not side loads external gem' do - BarePagesController.render :external_gem - - expect(Proscenium::Importer.imported).to be(:nil?) - end - end - - it 'side loads layout and view' do - BarePagesController.render :home - - expect(Proscenium::Importer.imported).to be == { - '/app/views/layouts/bare.js' => { sideloaded: true }, - '/app/views/layouts/bare.css' => { sideloaded: true }, - '/app/views/bare_pages/home.js' => { sideloaded: true }, - '/app/views/bare_pages/home.css' => { sideloaded: true } - } - end - - it 'side loads variant' do - skip 'fixme' - pp PagesController.new.request - pp PagesController.render :variant - end - - it 'side loads partial' do - BarePagesController.render :sideloadpartial - - expect(Proscenium::Importer.imported).to be == { - '/app/views/layouts/bare.js' => { sideloaded: true }, - '/app/views/layouts/bare.css' => { sideloaded: true }, - '/app/views/pages/_side.js' => { sideloaded: true }, - '/app/views/pages/_side.module.css' => { sideloaded: true, digest: '08ab1f89' }, - '/app/views/pages/_side_layout.css' => { sideloaded: true } - } - end - - it 'side loads vendored gem' do - BarePagesController.render :vendored_gem - - expect(Proscenium::Importer.imported).to be == { - '/@proscenium/react-manager/index.jsx' => { js: { type: 'module' } }, - '/gem1/app/components/flash/component.jsx' => { sideloaded: true, lazy: true }, - '/app/views/layouts/bare.js' => { sideloaded: true }, - '/app/views/layouts/bare.css' => { sideloaded: true } - } - end - - it 'side loads external gem' do - BarePagesController.render :external_gem - - expect(Proscenium::Importer.imported).to be == { - '/@proscenium/react-manager/index.jsx' => { js: { type: 'module' } }, - '/gem2/app/components/flash/component.jsx' => { sideloaded: true, lazy: true }, - '/app/views/layouts/bare.js' => { sideloaded: true }, - '/app/views/layouts/bare.css' => { sideloaded: true } - } - end -end diff --git a/test/proscenium/source_path.rb b/test/proscenium/source_path.rb deleted file mode 100644 index 146c8002..00000000 --- a/test/proscenium/source_path.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -require 'fixtures' - -describe Proscenium::SourcePath do - def before - Proscenium::Importer.reset - Proscenium::Resolver.reset - end - - with 'view component' do - it 'returns file system path to source file' do - expect(ViewComponent::CssModule::Component.source_path).to be == Rails.root.join('app/components/view_component/css_module/component.rb') - end - end - - with 'phlex component' do - it 'returns file system path to source file' do - expect(Phlex::Plain.source_path).to be == Rails.root.join('app/components/phlex/plain.rb') - end - end -end diff --git a/test/proscenium/ui/breadcrumbs/component.rb b/test/proscenium/ui/breadcrumbs/component.rb deleted file mode 100644 index 07ef2013..00000000 --- a/test/proscenium/ui/breadcrumbs/component.rb +++ /dev/null @@ -1,219 +0,0 @@ -# frozen_string_literal: true - -require 'view_helper' - -describe Proscenium::UI::Breadcrumbs::Component do - extend ViewHelper - - def before - Proscenium::Importer.reset - Proscenium::Resolver.reset - - controller.class.include Proscenium::UI::Breadcrumbs::Control - end - - view -> { subject.new } - - it 'side loads CSS' do - view - imports = Proscenium::Importer.imported.keys - - expect(imports).to be == ['/proscenium/ui/breadcrumbs/component.module.css'] - end - - with '@hide_breadcrumbs = true' do - it 'does not render' do - controller.instance_variable_set :@hide_breadcrumbs, true - expect(view.has_selector?('ol')).to be_falsey - end - end - - it 'shows home element by default' do - expect(view.find('ol li:first-child a')['href']).to be == '/' - expect(view.has_selector?('ol li:first-child a>svg')).to be_truthy - end - - with "home_path: '/foo'" do - view -> { Proscenium::UI::Breadcrumbs::Component.new home_path: '/foo' } - - it 'uses custom home path' do - expect(view.find('ol li:first-child a')['href']).to be == '/foo' - end - end - - with 'with_home: false' do - view -> { Proscenium::UI::Breadcrumbs::Component.new with_home: false } - - it 'does not show home element' do - expect(view.has_selector?('ol li')).to be_falsey - end - end - - with 'redefined #home_template' do - view lambda { - Class.new(Proscenium::UI::Breadcrumbs::Component) do - def self.source_path - Proscenium::UI::Breadcrumbs::Component.source_path - end - - def home_template - super { 'Hello' } - end - end.new - } - - it 'renders #home_template' do - expect(view.find('ol li:first-child a')['href']).to be == '/' - expect(view.find('ol li:first-child a').text).to be == 'Hello' - end - end - - describe '#add_breadcrumb' do - view -> { Proscenium::UI::Breadcrumbs::Component.new with_home: false } - - with 'string name and path' do - it 'renders breadcrumb as link' do - controller.add_breadcrumb 'Foo', '/foo' - - expect(view.find('ol li:first-child a').has_content?('Foo')).to be_truthy - expect(view.find('ol li:first-child a')['href']).to be == '/foo' - end - end - - with 'name only; as string' do - it 'renders the name as-is, and does not render link' do - controller.add_breadcrumb 'Foo' - - expect(view.find('ol li:first-child').has_content?('Foo')).to be_truthy - expect(view.has_selector?('ol li:first-child a')).to be_falsey - end - end - - with 'name as Symbol' do - it 'calls controller method of the same name' do - controller.class.define_method(:foo) { 'Foo' } - controller.add_breadcrumb :foo - - expect(view.find('ol li:first-child').has_content?('Foo')).to be_truthy - expect(view.has_selector?('ol li:first-child a')).to be_falsey - end - - with 'name responds to :for_breadcrumb' do - it 'calls method of the same name on the name object' do - foo = Class.new do - def for_breadcrumb - 'Foo' - end - end - controller.class.define_method(:foo) { foo.new } - controller.add_breadcrumb :foo - - expect(view.find('ol li:first-child').has_content?('Foo')).to be_truthy - expect(view.has_selector?('ol li:first-child a')).to be_falsey - end - end - end - - with 'name responds to :for_breadcrumb' do - it 'calls method of the same name on the name object' do - foo = Class.new do - def for_breadcrumb - 'Foo' - end - end - controller.add_breadcrumb foo.new - - expect(view.find('ol li:first-child').has_content?('Foo')).to be_truthy - expect(view.has_selector?('ol li:first-child a')).to be_falsey - end - end - - with 'name as Symbol with leading @' do - it 'calls controller instance variable of the same name' do - controller.instance_variable_set :@foo, 'Foo' - controller.add_breadcrumb :@foo - - expect(view.find('ol li:first-child').has_content?('Foo')).to be_truthy - expect(view.has_selector?('ol li:first-child a')).to be_falsey - end - end - - with 'name as Proc' do - it 'called with helpers as context' do - controller.instance_variable_set :@foo, 'Foo' - controller.add_breadcrumb -> { @foo } - - expect(view.find('ol li:first-child').has_content?('Foo')).to be_truthy - expect(view.has_selector?('ol li:first-child a')).to be_falsey - end - end - - with 'name as Proc; path as Symbol' do - it 'called with helpers as context' do - controller.instance_variable_set :@foo, 'Foo' - controller.add_breadcrumb -> { @foo }, :root - - expect(view.find('ol li:first-child').has_content?('Foo')).to be_truthy - expect(view.find('ol li:first-child a')['href']).to be == '/' - end - end - - with 'path as Symbol' do - it 'is passed to url_for' do - controller.add_breadcrumb 'Foo', :root - - expect(view.find('ol li:first-child').has_content?('Foo')).to be_truthy - expect(view.find('ol li:first-child a')['href']).to be == '/' - end - end - - with 'path as Symbol which is a controller method' do - it 'calls controller method of the same name' do - controller.class.define_method(:foo) { '/foo' } - controller.add_breadcrumb 'Foo', :foo - - expect(view.find('ol li:first-child').has_content?('Foo')).to be_truthy - expect(view.find('ol li:first-child a')['href']).to be == '/foo' - end - end - - with 'path as an Array' do - it 'is passed to url_for' do - controller.add_breadcrumb 'Foo', [:root] - - expect(view.find('ol li:first-child').has_content?('Foo')).to be_truthy - expect(view.find('ol li:first-child a')['href']).to be == '/' - end - end - - with 'path as Proc' do - it 'called with helpers as context' do - controller.instance_variable_set :@foo, '/' - controller.add_breadcrumb 'Foo', -> { @foo } - - expect(view.find('ol li:first-child').has_content?('Foo')).to be_truthy - expect(view.find('ol li:first-child a')['href']).to be == '/' - end - end - - with 'path as Symbol with leading @' do - it 'calls controller instance variable of the same name' do - controller.instance_variable_set :@foo, '/foo' - controller.add_breadcrumb 'Foo', :@foo - - expect(view.find('ol li:first-child').has_content?('Foo')).to be_truthy - expect(view.find('ol li:first-child a')['href']).to be == '/foo' - end - end - - with 'path containing a Symbol with leading @' do - it 'calls controller instance variable of the same name' do - controller.instance_variable_set :@root_path, :root - controller.add_breadcrumb 'Foo', :@root_path - - expect(view.find('ol li:first-child').has_content?('Foo')).to be_truthy - expect(view.find('ol li:first-child a')['href']).to be == '/' - end - end - end -end diff --git a/test/proscenium/version.rb b/test/proscenium/version.rb deleted file mode 100644 index a4e49e44..00000000 --- a/test/proscenium/version.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -describe Proscenium::VERSION do - it 'has a version number' do - expect(subject).not.to be(:nil?) - end -end diff --git a/test/proscenium/view_component.rb b/test/proscenium/view_component.rb deleted file mode 100644 index f2c84b07..00000000 --- a/test/proscenium/view_component.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -require 'action_controller/test_case' - -describe Proscenium::ViewComponent do - include ViewComponent::TestHelpers - - def before - Proscenium::Importer.reset - Proscenium::Resolver.reset - end - - it 'side loads component js and css' do - render_inline ViewComponent::FirstComponent.new - - expect(Proscenium::Importer.imported).to be == { - '/app/components/view_component/first_component.js' => { sideloaded: true }, - '/app/components/view_component/first_component.css' => { sideloaded: true } - } - end -end diff --git a/test/proscenium/view_component/css_modules.rb b/test/proscenium/view_component/css_modules.rb deleted file mode 100644 index 07fcf15a..00000000 --- a/test/proscenium/view_component/css_modules.rb +++ /dev/null @@ -1,30 +0,0 @@ -# frozen_string_literal: true - -require 'action_controller/test_case' - -describe Proscenium::ViewComponent::CssModules do - include ViewComponent::TestHelpers - - def before - Proscenium::Importer.reset - Proscenium::Resolver.reset - end - - with 'css_module helper' do - it 'replaces with CSS module name' do - render_inline ViewComponent::CssModuleHelperComponent.new - - expect(page.has_css?('h1.header-03d622d6', text: 'Hello')).to be == true - end - - it 'side loads css module' do - render_inline ViewComponent::CssModuleHelperComponent.new - - expect(Proscenium::Importer.imported).to be == { - '/app/components/view_component/css_module_helper_component.module.css' => { - sideloaded: true, digest: '03d622d6' - } - } - end - end -end diff --git a/test/resolver_test.rb b/test/resolver_test.rb new file mode 100644 index 00000000..b9612f1f --- /dev/null +++ b/test/resolver_test.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'test_helper' + +class Proscenium::ResolverTest < ActiveSupport::TestCase + let(:subject) { Proscenium::Resolver } + + describe '.resolve' do + context './foo' do + it 'raises' do + error = assert_raises ArgumentError do + subject.resolve('./foo') + end + assert_equal 'path must be an absolute file system or URL path', error.message + end + end + + context 'unknown path' do + it 'raises' do + assert_raises Proscenium::Builder::ResolveError do + subject.resolve('unknown') + end + end + end + + context 'bare specifier (NPM package)' do + it 'resolves' do + assert_equal '/packages/mypackage/index.js', subject.resolve('mypackage') + end + end + + context 'absolute file system path' do + it 'resolves' do + assert_equal '/lib/foo.js', subject.resolve(Rails.root.join('lib/foo.js').to_s) + end + end + + context 'absolute URL path' do + it 'resolves' do + assert_equal '/lib/foo.js', subject.resolve('/lib/foo.js') + end + end + + context '@proscenium runtime' do + it 'resolves' do + assert_equal '/@proscenium/react-manager/index.jsx', + subject.resolve('@proscenium/react-manager/index.jsx') + end + end + end +end diff --git a/test/side_load_test.rb b/test/side_load_test.rb new file mode 100644 index 00000000..be585314 --- /dev/null +++ b/test/side_load_test.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +require 'test_helper' + +class Proscenium::SideLoadTest < ActiveSupport::TestCase + context 'side load disabled' do + before do + Proscenium.config.side_load = false + end + + it 'does not side load layout and view' do + BarePagesController.render :home + + assert_nil Proscenium::Importer.imported + end + + it 'does not side load partial' do + BarePagesController.render :sideloadpartial + + assert_nil Proscenium::Importer.imported + end + + it 'does not side load vendored gem' do + BarePagesController.render :vendored_gem + + assert_nil Proscenium::Importer.imported + end + + it 'does not side loads external gem' do + BarePagesController.render :external_gem + + assert_nil Proscenium::Importer.imported + end + end + + it 'side loads layout and view' do + BarePagesController.render :home + + assert_equal({ + '/app/views/layouts/bare.js' => { sideloaded: true }, + '/app/views/layouts/bare.css' => { sideloaded: true }, + '/app/views/bare_pages/home.js' => { sideloaded: true }, + '/app/views/bare_pages/home.css' => { sideloaded: true } + }, Proscenium::Importer.imported) + end + + it 'side loads variant' do + skip 'fixme' + pp PagesController.new.request + pp PagesController.render :variant + end + + it 'side loads partial' do + BarePagesController.render :sideloadpartial + + assert_equal({ + '/app/views/layouts/bare.js' => { sideloaded: true }, + '/app/views/layouts/bare.css' => { sideloaded: true }, + '/app/views/pages/_side.js' => { sideloaded: true }, + '/app/views/pages/_side.module.css' => { sideloaded: true, digest: '08ab1f89' }, + '/app/views/pages/_side_layout.css' => { sideloaded: true } + }, Proscenium::Importer.imported) + end + + it 'side loads vendored gem' do + BarePagesController.render :vendored_gem + + assert_equal({ + '/@proscenium/react-manager/index.jsx' => { js: { type: 'module' } }, + '/gem1/app/components/flash/component.jsx' => { sideloaded: true, lazy: true }, + '/app/views/layouts/bare.js' => { sideloaded: true }, + '/app/views/layouts/bare.css' => { sideloaded: true } + }, Proscenium::Importer.imported) + end + + it 'side loads external gem' do + BarePagesController.render :external_gem + + assert_equal({ + '/@proscenium/react-manager/index.jsx' => { js: { type: 'module' } }, + '/gem2/app/components/flash/component.jsx' => { sideloaded: true, lazy: true }, + '/app/views/layouts/bare.js' => { sideloaded: true }, + '/app/views/layouts/bare.css' => { sideloaded: true } + }, Proscenium::Importer.imported) + end +end diff --git a/test/source_path_test.rb b/test/source_path_test.rb new file mode 100644 index 00000000..688f391e --- /dev/null +++ b/test/source_path_test.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'test_helper' +# require 'fixtures' + +class Proscenium::SourcePathTest < ActiveSupport::TestCase + context 'view component' do + it 'returns file system path to source file' do + assert_equal Rails.root.join('app/components/view_component/css_module/component.rb'), + ViewComponent::CssModule::Component.source_path + end + end + + context 'phlex component' do + it 'returns file system path to source file' do + assert_equal Rails.root.join('app/components/phlex/plain.rb'), Phlex::Plain.source_path + end + end +end diff --git a/fixtures/system_testing/console_logger.rb b/test/support/console_logger.rb similarity index 100% rename from fixtures/system_testing/console_logger.rb rename to test/support/console_logger.rb diff --git a/fixtures/view_helper.rb b/test/test_helper.rb similarity index 67% rename from fixtures/view_helper.rb rename to test/test_helper.rb index b6f4ddc1..4e1e5eac 100644 --- a/fixtures/view_helper.rb +++ b/test/test_helper.rb @@ -1,5 +1,21 @@ # frozen_string_literal: true +ENV['RAILS_ENV'] = 'test' + +require_relative '../fixtures/dummy/config/environment' +require 'rails/test_help' +require 'maxitest/autorun' + +module ActiveSupport + class TestCase + before do + Proscenium.config.side_load = true + Proscenium::Importer.reset + Proscenium::Resolver.reset + end + end +end + module ViewHelper def self.extended(parent) parent.class_exec do diff --git a/test/ui/breadcrumbs/component_test.rb b/test/ui/breadcrumbs/component_test.rb new file mode 100644 index 00000000..03302023 --- /dev/null +++ b/test/ui/breadcrumbs/component_test.rb @@ -0,0 +1,218 @@ +# frozen_string_literal: true + +require 'test_helper' + +class Proscenium::UI::Breadcrumbs::ComponentTest < ActiveSupport::TestCase + extend ViewHelper + + before do + controller.class.include Proscenium::UI::Breadcrumbs::Control + end + + let(:subject) { Proscenium::UI::Breadcrumbs::Component } + view -> { subject.new } + + it 'side loads CSS' do + view + imports = Proscenium::Importer.imported.keys + + assert_equal ['/proscenium/ui/breadcrumbs/component.module.css'], imports + end + + context '@hide_breadcrumbs = true' do + it 'does not render' do + controller.instance_variable_set :@hide_breadcrumbs, true + + assert_not view.has_selector?('ol') + end + end + + it 'shows home element by default' do + assert_equal '/', view.find('ol li:first-child a')['href'] + assert view.has_selector?('ol li:first-child a>svg') + end + + context "home_path: '/foo'" do + view -> { Proscenium::UI::Breadcrumbs::Component.new home_path: '/foo' } + + it 'uses custom home path' do + assert_equal '/foo', view.find('ol li:first-child a')['href'] + end + end + + context 'with_home: false' do + view -> { Proscenium::UI::Breadcrumbs::Component.new with_home: false } + + it 'does not show home element' do + assert_not view.has_selector?('ol li') + end + end + + context 'redefined #home_template' do + view lambda { + Class.new(Proscenium::UI::Breadcrumbs::Component) do + def self.source_path + Proscenium::UI::Breadcrumbs::Component.source_path + end + + def home_template + super { 'Hello' } + end + end.new + } + + it 'renders #home_template' do + assert_equal '/', view.find('ol li:first-child a')['href'] + assert_equal 'Hello', view.find('ol li:first-child a').text + end + end + + describe '#add_breadcrumb' do + view -> { Proscenium::UI::Breadcrumbs::Component.new with_home: false } + + context 'string name and path' do + it 'renders breadcrumb as link' do + controller.add_breadcrumb 'Foo', '/foo' + + assert view.find('ol li:first-child a').has_content?('Foo') + assert_equal '/foo', view.find('ol li:first-child a')['href'] + end + end + + context 'name only; as string' do + it 'renders the name as-is, and does not render link' do + controller.add_breadcrumb 'Foo' + + assert view.find('ol li:first-child').has_content?('Foo') + assert_not view.has_selector?('ol li:first-child a') + end + end + + context 'name as Symbol' do + it 'calls controller method of the same name' do + controller.class.define_method(:foo) { 'Foo' } + controller.add_breadcrumb :foo + + assert view.find('ol li:first-child').has_content?('Foo') + assert_not view.has_selector?('ol li:first-child a') + end + + context 'name responds to :for_breadcrumb' do + it 'calls method of the same name on the name object' do + foo = Class.new do + def for_breadcrumb + 'Foo' + end + end + controller.class.define_method(:foo) { foo.new } + controller.add_breadcrumb :foo + + assert view.find('ol li:first-child').has_content?('Foo') + assert_not view.has_selector?('ol li:first-child a') + end + end + end + + context 'name responds to :for_breadcrumb' do + it 'calls method of the same name on the name object' do + foo = Class.new do + def for_breadcrumb + 'Foo' + end + end + controller.add_breadcrumb foo.new + + assert view.find('ol li:first-child').has_content?('Foo') + assert_not view.has_selector?('ol li:first-child a') + end + end + + context 'name as Symbol with leading @' do + it 'calls controller instance variable of the same name' do + controller.instance_variable_set :@foo, 'Foo' + controller.add_breadcrumb :@foo + + assert view.find('ol li:first-child').has_content?('Foo') + assert_not view.has_selector?('ol li:first-child a') + end + end + + context 'name as Proc' do + it 'called with helpers as context' do + controller.instance_variable_set :@foo, 'Foo' + controller.add_breadcrumb -> { @foo } + + assert view.find('ol li:first-child').has_content?('Foo') + assert_not view.has_selector?('ol li:first-child a') + end + end + + context 'name as Proc; path as Symbol' do + it 'called with helpers as context' do + controller.instance_variable_set :@foo, 'Foo' + controller.add_breadcrumb -> { @foo }, :root + + assert view.find('ol li:first-child').has_content?('Foo') + assert_equal '/', view.find('ol li:first-child a')['href'] + end + end + + context 'path as Symbol' do + it 'is passed to url_for' do + controller.add_breadcrumb 'Foo', :root + + assert view.find('ol li:first-child').has_content?('Foo') + assert_equal '/', view.find('ol li:first-child a')['href'] + end + end + + context 'path as Symbol which is a controller method' do + it 'calls controller method of the same name' do + controller.class.define_method(:foo) { '/foo' } + controller.add_breadcrumb 'Foo', :foo + + assert view.find('ol li:first-child').has_content?('Foo') + assert_equal '/foo', view.find('ol li:first-child a')['href'] + end + end + + context 'path as an Array' do + it 'is passed to url_for' do + controller.add_breadcrumb 'Foo', [:root] + + assert view.find('ol li:first-child').has_content?('Foo') + assert_equal '/', view.find('ol li:first-child a')['href'] + end + end + + context 'path as Proc' do + it 'called with helpers as context' do + controller.instance_variable_set :@foo, '/' + controller.add_breadcrumb 'Foo', -> { @foo } + + assert view.find('ol li:first-child').has_content?('Foo') + assert_equal '/', view.find('ol li:first-child a')['href'] + end + end + + context 'path as Symbol with leading @' do + it 'calls controller instance variable of the same name' do + controller.instance_variable_set :@foo, '/foo' + controller.add_breadcrumb 'Foo', :@foo + + assert view.find('ol li:first-child').has_content?('Foo') + assert_equal '/foo', view.find('ol li:first-child a')['href'] + end + end + + context 'path containing a Symbol with leading @' do + it 'calls controller instance variable of the same name' do + controller.instance_variable_set :@root_path, :root + controller.add_breadcrumb 'Foo', :@root_path + + assert view.find('ol li:first-child').has_content?('Foo') + assert_equal '/', view.find('ol li:first-child a')['href'] + end + end + end +end diff --git a/test/version_test.rb b/test/version_test.rb new file mode 100644 index 00000000..5126cf8c --- /dev/null +++ b/test/version_test.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require 'test_helper' + +class Proscenium::VersionTest < ActiveSupport::TestCase + it 'has a version number' do # rubocop:disable Minitest/EmptyLineBeforeAssertionMethods + assert_not_nil Proscenium::VERSION + end +end diff --git a/test/view_component/css_modules_test.rb b/test/view_component/css_modules_test.rb new file mode 100644 index 00000000..1c85f26e --- /dev/null +++ b/test/view_component/css_modules_test.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require 'test_helper' + +class Proscenium::ViewComponent::CssModulesTest < ActiveSupport::TestCase + include ViewComponent::TestHelpers + + context 'css_module helper' do + it 'replaces with CSS module name' do + render_inline ViewComponent::CssModuleHelperComponent.new + + assert page.has_css?('h1.header-03d622d6', text: 'Hello') + end + + it 'side loads css module' do + render_inline ViewComponent::CssModuleHelperComponent.new + + assert_equal({ + '/app/components/view_component/css_module_helper_component.module.css' => { + sideloaded: true, digest: '03d622d6' + } + }, Proscenium::Importer.imported) + end + end +end diff --git a/test/proscenium/view_component/react_component.rb b/test/view_component/react_component_test.rb similarity index 57% rename from test/proscenium/view_component/react_component.rb rename to test/view_component/react_component_test.rb index 02da7a85..533f8d34 100644 --- a/test/proscenium/view_component/react_component.rb +++ b/test/view_component/react_component_test.rb @@ -1,15 +1,10 @@ # frozen_string_literal: true -require 'action_controller/test_case' +require 'test_helper' -describe Proscenium::ViewComponent::ReactComponent do +class Proscenium::ViewComponent::ReactComponentTest < ActiveSupport::TestCase include ViewComponent::TestHelpers - def before - Proscenium::Importer.reset - Proscenium::Resolver.reset - end - let(:selector) do '[data-proscenium-component-path="/app/components/view_component/second_react/component.jsx"]' end @@ -17,18 +12,18 @@ def before it 'has data-proscenium-component attribute' do render_inline ViewComponent::SecondReact::Component.new - expect(page.has_css?(selector)).to be == true + assert page.has_css?(selector) end it 'has empty props' do render_inline ViewComponent::SecondReact::Component.new - expect(page.find(selector)['data-proscenium-component-props']).to be == '{}' + assert_equal '{}', page.find(selector)['data-proscenium-component-props'] end it 'should pass through props' do render_inline ViewComponent::SecondReact::Component.new(props: { name: 'Joel' }) - expect(page.find(selector)['data-proscenium-component-props']).to be == %({"name":"Joel"}) + assert_equal %({"name":"Joel"}), page.find(selector)['data-proscenium-component-props'] end end diff --git a/test/view_component_test.rb b/test/view_component_test.rb new file mode 100644 index 00000000..fa9a1f11 --- /dev/null +++ b/test/view_component_test.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require 'test_helper' + +class Proscenium::ViewComponentTest < ActiveSupport::TestCase + include ViewComponent::TestHelpers + + test 'side loads component js and css' do + render_inline ViewComponent::FirstComponent.new + + assert_equal({ + '/app/components/view_component/first_component.js' => { sideloaded: true }, + '/app/components/view_component/first_component.css' => { sideloaded: true } + }, Proscenium::Importer.imported) + end +end