panda-core 0.10.2 → 0.10.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f1ca00630306c920fd7c8b7a5c8703532d44c6f24e1960b5a070f5083ee9d467
4
- data.tar.gz: aaebba3e769ccba28420bb69680d461558b6371e88977c38846e8180d045acce
3
+ metadata.gz: b76ce50771d377d8b8434339c205494d1b3994bd45c1e381dfa700b71a2656bd
4
+ data.tar.gz: 2d0e4f33307b5dca31db3464ff17d8dc1dd70c35849e688cca30b904d30d2bec
5
5
  SHA512:
6
- metadata.gz: 9b4235040de9088b17e648d1085c3740129dd62a0d319ff4548d32e979bf425caf1b3fe80a4491898fef8a42d61517cb5fe2d3c55f1f949f707f8708e8eabf27
7
- data.tar.gz: c4afb07c75306e2e6a2a689f3fc24c89bc62c274b71f6d5d8490d6874f5776afd318fc4dbb74bcca4f67e74ab1555d4bbb72d5704daa640eb4e6b58167ba9231
6
+ metadata.gz: 2cf27e3c3bf6199a57320a5d6351125c704bf0d2a87de6790d698caf4dc86351661af8fa75164c3139deac6383ae0f63c5e527e896e56017ad8bff5eee5a9b39
7
+ data.tar.gz: f6f374ae824ce953f2ebbe100a9bcdfb5e11db6efe7154b098b1c0cb74475f0b1d77e99c6f6b491c519c216e731f8b96b6161bb6b1da5b38b6702b77d1411200
@@ -1,8 +1,13 @@
1
- import { Application } from "@hotwired/stimulus"
2
- import "@fortawesome/fontawesome-free"
1
+ import { Application } from '@hotwired/stimulus'
2
+ import '@fortawesome/fontawesome-free'
3
3
 
4
4
  const application = Application.start()
5
5
 
6
+ // Always propagate Stimulus controller errors for better debugging
7
+ application.handleError = (error, message, detail) => {
8
+ throw error
9
+ }
10
+
6
11
  // Configure Stimulus development experience
7
12
  application.debug = false
8
13
  window.Stimulus = application
@@ -14,4 +19,4 @@ export { application }
14
19
 
15
20
  // Tailwind Plus Elements can be loaded by importing "panda/core/tailwindplus-elements"
16
21
  // or by adding the script tag directly to your HTML:
17
- // <script src="https://cdn.jsdelivr.net/npm/@tailwindplus/elements@1" type="module"></script>
22
+ // <script src="https://cdn.jsdelivr.net/npm/@tailwindplus/elements@1" type="module"></script>
@@ -24,7 +24,6 @@ require_relative "shared/inflections_config"
24
24
  require_relative "shared/generator_config"
25
25
 
26
26
  # Load engine configuration modules
27
- require_relative "engine/test_config"
28
27
  require_relative "engine/autoload_config"
29
28
  require_relative "engine/middleware_config"
30
29
  require_relative "engine/importmap_config"
@@ -45,7 +44,6 @@ module Panda
45
44
  include Shared::GeneratorConfig
46
45
 
47
46
  # Include engine-specific configuration modules
48
- include TestConfig
49
47
  include AutoloadConfig
50
48
  include MiddlewareConfig
51
49
  include ImportmapConfig
@@ -61,8 +61,11 @@ RSpec.configure do |config|
61
61
  if ENV["GITHUB_ACTIONS"] == "true"
62
62
  require "rspec/github"
63
63
  config.add_formatter RSpec::Github::Formatter
64
- # Also add documentation formatter for colored real-time output in CI logs
65
- config.add_formatter RSpec::Core::Formatters::DocumentationFormatter, $stdout
64
+ # Also add documentation formatter for colored real-time output in CI logs,
65
+ # but avoid double-printing when one is already configured (e.g. via CLI).
66
+ unless config.formatters.any? { |formatter| formatter.is_a?(RSpec::Core::Formatters::DocumentationFormatter) }
67
+ config.add_formatter RSpec::Core::Formatters::DocumentationFormatter, $stdout
68
+ end
66
69
  end
67
70
 
68
71
  # Controller testing support (if rails-controller-testing is available)
@@ -134,13 +134,13 @@ module Panda
134
134
  visit "/admin/test_login/#{user.id}?return_to=#{return_path}"
135
135
 
136
136
  # Wait briefly for session to be set
137
- sleep 0.2
137
+ # sleep 0.2
138
138
 
139
139
  # Manually visit the destination since Cuprite doesn't reliably follow redirects
140
140
  if expect_success
141
141
  visit return_path
142
142
  # Wait for page to load
143
- sleep 0.2
143
+ # sleep 0.2
144
144
 
145
145
  # Verify we're on the expected path
146
146
  expect(page).to have_current_path(return_path, wait: 2)
@@ -114,7 +114,7 @@ module Panda
114
114
  end
115
115
 
116
116
  # Wait for DOM to be ready
117
- sleep 0.5
117
+ # sleep 0.5
118
118
 
119
119
  # Get comprehensive page info
120
120
  page_html = begin
@@ -9,7 +9,15 @@ module BrowserConsoleLogger
9
9
 
10
10
  if respond_to?(:page) && page.driver.is_a?(Capybara::Cuprite::Driver)
11
11
  begin
12
- console_logs = page.driver.browser.console_messages
12
+ # Access the console logger via the CupriteSetup module
13
+ console_logger = Panda::Core::Testing::CupriteSetup.console_logger
14
+
15
+ unless console_logger
16
+ puts "\n⚠️ Console logger not available"
17
+ next
18
+ end
19
+
20
+ console_logs = console_logger.logs
13
21
 
14
22
  if console_logs.any?
15
23
  puts "\n" + "=" * 80
@@ -17,17 +25,15 @@ module BrowserConsoleLogger
17
25
  puts "=" * 80
18
26
 
19
27
  console_logs.each_with_index do |msg, index|
20
- type_icon = case msg["type"]
28
+ level = msg.level.downcase
29
+ type_icon = case level
21
30
  when "error" then "❌"
22
31
  when "warning" then "⚠️"
23
32
  when "info" then "ℹ️"
24
33
  else "📝"
25
34
  end
26
35
 
27
- puts "#{index + 1}. [#{msg["type"].upcase}] #{type_icon}"
28
- puts " Message: #{msg["message"]}"
29
- puts " Source: #{msg["source"]}" if msg["source"]
30
- puts " Line: #{msg["line"]}" if msg["line"]
36
+ puts "#{index + 1}. #{type_icon} #{msg}"
31
37
  puts ""
32
38
  end
33
39
 
@@ -37,6 +43,7 @@ module BrowserConsoleLogger
37
43
  end
38
44
  rescue => e
39
45
  puts "\n⚠️ Failed to capture console logs: #{e.message}"
46
+ puts " #{e.class}: #{e.backtrace.first(3).join("\n ")}" if ENV["DEBUG"]
40
47
  end
41
48
  end
42
49
  end
@@ -47,13 +47,16 @@ Capybara.register_server :puma_ci do |app, port, host|
47
47
  raise e
48
48
  end
49
49
 
50
+ # Allow conditional silencing of server logs via CAPYBARA_SERVER_VERBOSE
51
+ verbose = ENV.fetch("CAPYBARA_SERVER_VERBOSE", "false") == "true"
52
+
50
53
  options = {
51
54
  Host: host,
52
55
  Port: port,
53
56
  Threads: "#{min_threads}:#{max_threads}",
54
57
  Workers: 0,
55
- Silent: false, # Always verbose in CI to catch startup errors
56
- Verbose: true,
58
+ Silent: !verbose,
59
+ Verbose: verbose,
57
60
  PreloadApp: false
58
61
  }
59
62
 
@@ -44,7 +44,7 @@ module Panda
44
44
  ready = page.evaluate_script("document.readyState")
45
45
  break if ready == "complete"
46
46
 
47
- sleep 0.1
47
+ # sleep 0.1
48
48
  end
49
49
  end
50
50
  rescue Timeout::Error
@@ -64,7 +64,7 @@ module Panda
64
64
  end
65
65
  break if loaded
66
66
 
67
- sleep 0.1
67
+ # sleep 0.1
68
68
  end
69
69
  end
70
70
  true
@@ -82,7 +82,7 @@ module Panda
82
82
  while Time.now - start_time < timeout
83
83
  return true if page.has_css?(selector, visible: true)
84
84
 
85
- sleep 0.1
85
+ # sleep 0.1
86
86
  end
87
87
  false
88
88
  end
@@ -96,7 +96,7 @@ module Panda
96
96
  while Time.now - start_time < timeout
97
97
  return true if page.has_text?(text)
98
98
 
99
- sleep 0.1
99
+ # sleep 0.1
100
100
  end
101
101
  false
102
102
  end
@@ -122,7 +122,7 @@ module Panda
122
122
  while Time.now - start_time < timeout
123
123
  return true if page.html != initial_dom
124
124
 
125
- sleep 0.1
125
+ # sleep 0.1
126
126
  end
127
127
  false
128
128
  end
@@ -147,7 +147,7 @@ module Panda
147
147
  def click_on_selectors(*css_selectors)
148
148
  css_selectors.each do |selector|
149
149
  find(selector).click
150
- sleep 0.1 # Add a small delay to allow JavaScript to run
150
+ # sleep 0.1 # Add a small delay to allow JavaScript to run
151
151
  end
152
152
  end
153
153
 
@@ -160,7 +160,7 @@ module Panda
160
160
  while Time.now - start_time < timeout
161
161
  return true if page.has_field?(field_name, with: value)
162
162
 
163
- sleep 0.1
163
+ # sleep 0.1
164
164
  end
165
165
  false
166
166
  end
@@ -179,7 +179,7 @@ module Panda
179
179
 
180
180
  if retries <= 2 && elapsed < max_duration && ENV["GITHUB_ACTIONS"]
181
181
  puts "[CI] Element not found on fill_in '#{locator}', retry #{retries}/2 (#{elapsed.round(1)}s elapsed)"
182
- sleep 0.5
182
+ # sleep 0.5
183
183
  retry
184
184
  else
185
185
  puts "[CI] Giving up on fill_in '#{locator}' after #{retries} retries and #{elapsed.round(1)}s" if ENV["GITHUB_ACTIONS"]
@@ -201,7 +201,7 @@ module Panda
201
201
 
202
202
  if retries <= 2 && elapsed < max_duration && ENV["GITHUB_ACTIONS"]
203
203
  puts "[CI] Element not found on select '#{value}' from '#{from}', retry #{retries}/2 (#{elapsed.round(1)}s elapsed)"
204
- sleep 0.5
204
+ # sleep 0.5
205
205
  retry
206
206
  else
207
207
  puts "[CI] Giving up on select '#{value}' from '#{from}' after #{retries} retries and #{elapsed.round(1)}s" if ENV["GITHUB_ACTIONS"]
@@ -223,7 +223,7 @@ module Panda
223
223
 
224
224
  if retries <= 2 && elapsed < max_duration && ENV["GITHUB_ACTIONS"]
225
225
  puts "[CI] Element not found on click_button '#{locator}', retry #{retries}/2 (#{elapsed.round(1)}s elapsed)"
226
- sleep 0.5
226
+ # sleep 0.5
227
227
  retry
228
228
  else
229
229
  puts "[CI] Giving up on click_button '#{locator}' after #{retries} retries and #{elapsed.round(1)}s" if ENV["GITHUB_ACTIONS"]
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "ferrum"
4
4
  require "capybara/cuprite"
5
+ require_relative "ferrum_console_logger"
5
6
 
6
7
  # Shared Cuprite driver configuration for all Panda gems
7
8
  # This provides standard Cuprite setup with sensible defaults that work across gems
@@ -17,6 +18,14 @@ module Panda
17
18
  module Core
18
19
  module Testing
19
20
  module CupriteSetup
21
+ # Class variable to store the console logger instance
22
+ # This allows tests to access console logs after they run
23
+ @console_logger = nil
24
+
25
+ class << self
26
+ attr_accessor :console_logger
27
+ end
28
+
20
29
  # Base Cuprite options shared across all drivers
21
30
  def self.base_options
22
31
  default_timeout = 2
@@ -67,8 +76,15 @@ module Panda
67
76
  # Add CI-specific options
68
77
  if ENV["GITHUB_ACTIONS"] == "true"
69
78
  options[:browser_options].merge!(ci_browser_options)
79
+
80
+ # Ensure CI uses xvfb to run the browser
81
+ options[:browser_options][:xvfb] = true
70
82
  end
71
83
 
84
+ # Create console logger for capturing browser console messages
85
+ self.console_logger = Panda::Core::Testing::Support::System::FerrumConsoleLogger.new
86
+ options[:logger] = console_logger
87
+
72
88
  Capybara.register_driver :cuprite do |app|
73
89
  Capybara::Cuprite::Driver.new(app, **options)
74
90
  end
@@ -83,6 +99,9 @@ module Panda
83
99
  options[:browser_options].merge!(ci_browser_options)
84
100
  end
85
101
 
102
+ # Use the same console logger instance for mobile driver
103
+ options[:logger] = console_logger if console_logger
104
+
86
105
  Capybara.register_driver :cuprite_mobile do |app|
87
106
  Capybara::Cuprite::Driver.new(app, **options)
88
107
  end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Panda
6
+ module Core
7
+ module Testing
8
+ module Support
9
+ module System
10
+ # Custom logger for capturing browser console messages via Ferrum/Cuprite
11
+ # Ferrum doesn't provide a direct API for console messages - instead it uses
12
+ # Chrome DevTools Protocol (CDP) events that are sent to a logger object
13
+ class FerrumConsoleLogger
14
+ attr_reader :logs
15
+
16
+ def initialize
17
+ @logs = []
18
+ end
19
+
20
+ # Ferrum calls this method with CDP protocol events
21
+ # Format: "SEND message_id {json}" or "RECV message_id {json}"
22
+ def puts(log_str)
23
+ return unless log_str.is_a?(String)
24
+
25
+ parts = log_str.strip.split(" ", 3)
26
+ return if parts.size < 3
27
+
28
+ # Parse the JSON data from CDP event
29
+ data = JSON.parse(parts[2])
30
+
31
+ # Only capture console-related events
32
+ if console_event?(data)
33
+ @logs << Message.new(log_str)
34
+ end
35
+ rescue JSON::ParserError
36
+ # Silently ignore malformed JSON
37
+ end
38
+
39
+ def clear
40
+ @logs.clear
41
+ end
42
+
43
+ private
44
+
45
+ def console_event?(data)
46
+ %w[
47
+ Runtime.exceptionThrown
48
+ Log.entryAdded
49
+ Runtime.consoleAPICalled
50
+ ].include?(data["method"])
51
+ end
52
+
53
+ # Wrapper for a single console message
54
+ class Message
55
+ def initialize(log_str)
56
+ parts = log_str.strip.split(" ", 3)
57
+ @raw = log_str
58
+ @data = JSON.parse(parts[2])
59
+ end
60
+
61
+ # Get the log level (error, warning, info, etc.)
62
+ def level
63
+ # Different CDP events structure level differently
64
+ @data.dig("params", "entry", "level") ||
65
+ @data.dig("params", "type") ||
66
+ "log"
67
+ end
68
+
69
+ # Get the message text
70
+ def message
71
+ # Handle different CDP event types
72
+ if @data["method"] == "Runtime.exceptionThrown"
73
+ exception = @data.dig("params", "exceptionDetails")
74
+ if exception
75
+ text = exception.dig("exception", "description") || exception["text"]
76
+ location = "#{exception.dig("url")}:#{exception.dig("lineNumber")}"
77
+ "#{text} (#{location})"
78
+ else
79
+ "Unknown exception"
80
+ end
81
+ elsif @data["method"] == "Log.entryAdded"
82
+ @data.dig("params", "entry", "text") || ""
83
+ elsif @data["method"] == "Runtime.consoleAPICalled"
84
+ # Console API calls have arguments that need to be extracted
85
+ args = @data.dig("params", "args") || []
86
+ args.map { |arg| format_argument(arg) }.join(" ")
87
+ else
88
+ @raw
89
+ end
90
+ end
91
+
92
+ # Format a CDP RemoteObject argument
93
+ def format_argument(arg)
94
+ case arg["type"]
95
+ when "string"
96
+ arg["value"]
97
+ when "number", "boolean"
98
+ arg["value"].to_s
99
+ when "undefined"
100
+ "undefined"
101
+ when "object"
102
+ if arg["subtype"] == "null"
103
+ "null"
104
+ else
105
+ arg["description"] || "[Object]"
106
+ end
107
+ else
108
+ arg["description"] || arg.inspect
109
+ end
110
+ end
111
+
112
+ def to_s
113
+ "[#{level.upcase}] #{message}"
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -102,7 +102,7 @@ RSpec.configure do |config|
102
102
  nil
103
103
  end
104
104
 
105
- sleep 0.5
105
+ # sleep 0.5
106
106
 
107
107
  # Get comprehensive page info
108
108
  page_html = begin
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Panda
4
4
  module Core
5
- # Version 0.10.2 - Fix JavaScript middleware fallback for CI environments
6
- VERSION = "0.10.2"
5
+ # Version 0.10.4 - Database configuration improvements for CI tests
6
+ VERSION = "0.10.4"
7
7
  end
8
8
  end
data/lib/panda/core.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rails"
4
+ # require "active_support/core_ext/module/attribute_accessors"
4
5
 
5
6
  module Panda
6
7
  module Core
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: panda-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.2
4
+ version: 0.10.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Otaina Limited
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2025-11-16 00:00:00.000000000 Z
12
+ date: 2025-11-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: image_processing
@@ -500,7 +500,6 @@ files:
500
500
  - lib/panda/core/engine/middleware_config.rb
501
501
  - lib/panda/core/engine/omniauth_config.rb
502
502
  - lib/panda/core/engine/phlex_config.rb
503
- - lib/panda/core/engine/test_config.rb
504
503
  - lib/panda/core/media.rb
505
504
  - lib/panda/core/module_registry.rb
506
505
  - lib/panda/core/notifications.rb
@@ -527,6 +526,7 @@ files:
527
526
  - lib/panda/core/testing/support/system/cuprite_helpers.rb
528
527
  - lib/panda/core/testing/support/system/cuprite_setup.rb
529
528
  - lib/panda/core/testing/support/system/database_connection_helpers.rb
529
+ - lib/panda/core/testing/support/system/ferrum_console_logger.rb
530
530
  - lib/panda/core/testing/support/system/system_test_helpers.rb
531
531
  - lib/panda/core/version.rb
532
532
  - lib/tasks/panda/core/migrations.rake
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Panda
4
- module Core
5
- class Engine < ::Rails::Engine
6
- # Test environment configuration
7
- module TestConfig
8
- extend ActiveSupport::Concern
9
-
10
- included do
11
- # For testing: Don't expose engine migrations since we use "copy to host app" strategy
12
- # In test environment, migrations should be copied to the host app
13
- if Rails.env.test?
14
- config.paths["db/migrate"] = []
15
- end
16
- end
17
- end
18
- end
19
- end
20
- end