panda-core 0.9.2 → 0.9.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 +4 -4
- data/lib/panda/core/asset_loader.rb +10 -0
- data/lib/panda/core/engine.rb +18 -15
- data/lib/panda/core/module_registry.rb +93 -0
- data/lib/panda/core/testing/support/system/browser_console_logger.rb +49 -0
- data/lib/panda/core/testing/support/system/{capybara_setup.rb → capybara_config.rb} +2 -0
- data/lib/panda/core/testing/support/system/ci_capybara_config.rb +8 -2
- data/lib/panda/core/testing/support/system/cuprite_setup.rb +5 -2
- data/lib/panda/core/version.rb +2 -1
- metadata +34 -18
- data/lib/panda/core/testing/support/generator_spec_helper.rb +0 -97
- data/lib/tasks/assets.rake +0 -159
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz: '
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '04695145fc8e1f8c8bdd13d1ee35d91e1a3eb470070d265463f72e97f9ddd19c'
|
|
4
|
+
data.tar.gz: 509bd365e3b501f2f34d0add9617f29b757dcb1b2ca6db6d65c6f1dccfc2142f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3187a5adef002caceeda919102eb8f281aad08058bdc53a10be6297fcc78edaeecade4cdd6ead4246d6596f4fdbd1d37c44d1a2b280bb91023de20b6dc9a75c9
|
|
7
|
+
data.tar.gz: 6b7ee0a2d2dc9fcfd4c35bde5217a6390d700085dfdb52417189e702c9906d446d5a8d4f89efb21db8c14bd38962a4f23a7c4748b797f7cc235db349b6d7c7c8
|
|
@@ -248,6 +248,16 @@ module Panda
|
|
|
248
248
|
options[:src] = source.start_with?("/") ? source : "/assets/#{source}"
|
|
249
249
|
content_tag(:script, "", options)
|
|
250
250
|
end
|
|
251
|
+
|
|
252
|
+
def self.last_dummy_asset_report
|
|
253
|
+
# Only expose this in test/CI/development — never in production
|
|
254
|
+
return nil unless Rails.env.test? || ENV["CI"].present?
|
|
255
|
+
|
|
256
|
+
if defined?(Panda::Core::Assets::ReportRegistry) &&
|
|
257
|
+
Panda::Core::Assets::ReportRegistry.present?
|
|
258
|
+
Panda::Core::Assets::ReportRegistry.last
|
|
259
|
+
end
|
|
260
|
+
end
|
|
251
261
|
end
|
|
252
262
|
end
|
|
253
263
|
end
|
data/lib/panda/core/engine.rb
CHANGED
|
@@ -58,9 +58,10 @@ module Panda
|
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
# Static asset middleware for serving public files and JavaScript modules
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
# Must run before Propshaft to intercept /panda/* requests
|
|
62
|
+
initializer "panda.core.static_assets" do |app|
|
|
63
|
+
# Serve public assets (CSS, images, etc.)
|
|
64
|
+
app.config.middleware.insert_before Propshaft::Server, Rack::Static,
|
|
64
65
|
urls: ["/panda-core-assets"],
|
|
65
66
|
root: Panda::Core::Engine.root.join("public"),
|
|
66
67
|
header_rules: [
|
|
@@ -68,14 +69,9 @@ module Panda
|
|
|
68
69
|
[:all, {"Cache-Control" => Rails.env.development? ? "no-cache, no-store, must-revalidate" : "public, max-age=31536000"}]
|
|
69
70
|
]
|
|
70
71
|
|
|
71
|
-
#
|
|
72
|
-
#
|
|
73
|
-
app.config.middleware.
|
|
74
|
-
urls: ["/panda/core"],
|
|
75
|
-
root: Panda::Core::Engine.root.join("app/javascript"),
|
|
76
|
-
header_rules: [
|
|
77
|
-
[:all, {"Cache-Control" => Rails.env.development? ? "no-cache, no-store, must-revalidate" : "public, max-age=31536000"}]
|
|
78
|
-
]
|
|
72
|
+
# Use ModuleRegistry's custom middleware to serve JavaScript from all registered modules
|
|
73
|
+
# This middleware checks all modules and serves from the first matching location
|
|
74
|
+
app.config.middleware.insert_before Propshaft::Server, Panda::Core::ModuleRegistry::JavaScriptMiddleware
|
|
79
75
|
end
|
|
80
76
|
|
|
81
77
|
# Auto-compile CSS for test/development environments
|
|
@@ -96,11 +92,7 @@ module Panda
|
|
|
96
92
|
|
|
97
93
|
# Compile CSS with timestamp
|
|
98
94
|
require "open3"
|
|
99
|
-
require "fileutils"
|
|
100
95
|
|
|
101
|
-
FileUtils.mkdir_p(assets_dir)
|
|
102
|
-
|
|
103
|
-
# Get content paths from ModuleRegistry
|
|
104
96
|
content_paths = Panda::Core::ModuleRegistry.tailwind_content_paths
|
|
105
97
|
content_flags = content_paths.map { |path| "--content '#{path}'" }.join(" ")
|
|
106
98
|
|
|
@@ -125,3 +117,14 @@ module Panda
|
|
|
125
117
|
end
|
|
126
118
|
end
|
|
127
119
|
end
|
|
120
|
+
|
|
121
|
+
# Register Core module with ModuleRegistry for JavaScript serving
|
|
122
|
+
Panda::Core::ModuleRegistry.register(
|
|
123
|
+
gem_name: "panda-core",
|
|
124
|
+
engine: "Panda::Core::Engine",
|
|
125
|
+
paths: {
|
|
126
|
+
views: "app/views/panda/core/**/*.erb",
|
|
127
|
+
components: "app/components/panda/core/**/*.rb"
|
|
128
|
+
# JavaScript paths are auto-discovered from config/importmap.rb
|
|
129
|
+
}
|
|
130
|
+
)
|
|
@@ -277,6 +277,99 @@ module Panda
|
|
|
277
277
|
nil
|
|
278
278
|
end
|
|
279
279
|
end
|
|
280
|
+
|
|
281
|
+
# Custom Rack middleware to serve JavaScript modules from all registered Panda modules
|
|
282
|
+
#
|
|
283
|
+
# This middleware checks all registered modules' app/javascript/panda directories
|
|
284
|
+
# and serves the first matching file. This solves the problem of multiple Rack::Static
|
|
285
|
+
# instances blocking each other.
|
|
286
|
+
#
|
|
287
|
+
class JavaScriptMiddleware
|
|
288
|
+
def initialize(app)
|
|
289
|
+
@app = app
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def call(env)
|
|
293
|
+
request = Rack::Request.new(env)
|
|
294
|
+
path = request.path_info
|
|
295
|
+
|
|
296
|
+
# Only handle /panda/core/* and /panda/cms/* style JavaScript module requests
|
|
297
|
+
# Skip paths like /panda-core-assets/* (public assets handled by Rack::Static)
|
|
298
|
+
return @app.call(env) unless path.start_with?("/panda/")
|
|
299
|
+
|
|
300
|
+
# Strip /panda/ prefix to get relative path
|
|
301
|
+
# e.g., "/panda/cms/application.js" -> "cms/application.js"
|
|
302
|
+
relative_path = path.sub(%r{^/panda/}, "")
|
|
303
|
+
return @app.call(env) if relative_path.empty?
|
|
304
|
+
|
|
305
|
+
# Try to find the file in registered modules
|
|
306
|
+
file_path = find_javascript_file(relative_path)
|
|
307
|
+
|
|
308
|
+
if file_path && File.file?(file_path)
|
|
309
|
+
puts "[JavaScriptMiddleware] Serving: #{path} from #{file_path}" if ENV["RSPEC_DEBUG"]
|
|
310
|
+
serve_file(file_path, env)
|
|
311
|
+
else
|
|
312
|
+
puts "[JavaScriptMiddleware] Not found: #{path} (tried #{relative_path})" if ENV["RSPEC_DEBUG"]
|
|
313
|
+
@app.call(env)
|
|
314
|
+
end
|
|
315
|
+
rescue => e
|
|
316
|
+
# On error, log and pass to next middleware
|
|
317
|
+
puts "[JavaScriptMiddleware] Error: #{e.message}" if ENV["RSPEC_DEBUG"]
|
|
318
|
+
Rails.logger.error("[ModuleRegistry::JavaScriptMiddleware] Error: #{e.message}\n#{e.backtrace.join("\n")}") if defined?(Rails.logger)
|
|
319
|
+
@app.call(env)
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
private
|
|
323
|
+
|
|
324
|
+
def find_javascript_file(relative_path)
|
|
325
|
+
# Check each registered module's JavaScript directory
|
|
326
|
+
ModuleRegistry.modules.each do |gem_name, info|
|
|
327
|
+
next unless ModuleRegistry.send(:engine_available?, info[:engine])
|
|
328
|
+
|
|
329
|
+
root = ModuleRegistry.send(:engine_root, info[:engine])
|
|
330
|
+
next unless root
|
|
331
|
+
|
|
332
|
+
# Check in app/javascript/panda/
|
|
333
|
+
candidate = root.join("app/javascript/panda", relative_path)
|
|
334
|
+
return candidate.to_s if candidate.exist? && candidate.file?
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
nil
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def serve_file(file_path, env)
|
|
341
|
+
# Read file content
|
|
342
|
+
content = File.read(file_path)
|
|
343
|
+
|
|
344
|
+
# Determine content type
|
|
345
|
+
content_type = case File.extname(file_path)
|
|
346
|
+
when ".js"
|
|
347
|
+
"application/javascript; charset=utf-8"
|
|
348
|
+
when ".json"
|
|
349
|
+
"application/json; charset=utf-8"
|
|
350
|
+
else
|
|
351
|
+
"text/plain; charset=utf-8"
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
# Determine cache control
|
|
355
|
+
cache_control = if Rails.env.development? || Rails.env.test?
|
|
356
|
+
"no-cache, no-store, must-revalidate"
|
|
357
|
+
else
|
|
358
|
+
"public, max-age=31536000"
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
# Return response
|
|
362
|
+
[
|
|
363
|
+
200,
|
|
364
|
+
{
|
|
365
|
+
"Content-Type" => content_type,
|
|
366
|
+
"Content-Length" => content.bytesize.to_s,
|
|
367
|
+
"Cache-Control" => cache_control
|
|
368
|
+
},
|
|
369
|
+
[content]
|
|
370
|
+
]
|
|
371
|
+
end
|
|
372
|
+
end
|
|
280
373
|
end
|
|
281
374
|
end
|
|
282
375
|
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Capture and log browser console output for debugging CI failures
|
|
4
|
+
module BrowserConsoleLogger
|
|
5
|
+
def self.included(base)
|
|
6
|
+
base.after do |example|
|
|
7
|
+
# Only log console in CI when test fails
|
|
8
|
+
next unless ENV["CI"] && example.exception
|
|
9
|
+
|
|
10
|
+
if respond_to?(:page) && page.driver.is_a?(Capybara::Cuprite::Driver)
|
|
11
|
+
begin
|
|
12
|
+
console_logs = page.driver.browser.console_messages
|
|
13
|
+
|
|
14
|
+
if console_logs.any?
|
|
15
|
+
puts "\n" + "=" * 80
|
|
16
|
+
puts "BROWSER CONSOLE OUTPUT (#{console_logs.length} messages)"
|
|
17
|
+
puts "=" * 80
|
|
18
|
+
|
|
19
|
+
console_logs.each_with_index do |msg, index|
|
|
20
|
+
type_icon = case msg["type"]
|
|
21
|
+
when "error" then "❌"
|
|
22
|
+
when "warning" then "⚠️"
|
|
23
|
+
when "info" then "ℹ️"
|
|
24
|
+
else "📝"
|
|
25
|
+
end
|
|
26
|
+
|
|
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"]
|
|
31
|
+
puts ""
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
puts "=" * 80
|
|
35
|
+
else
|
|
36
|
+
puts "\n⚠️ No console messages captured (browser may not have started)"
|
|
37
|
+
end
|
|
38
|
+
rescue => e
|
|
39
|
+
puts "\n⚠️ Failed to capture console logs: #{e.message}"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Include in all system tests
|
|
47
|
+
RSpec.configure do |config|
|
|
48
|
+
config.include BrowserConsoleLogger, type: :system
|
|
49
|
+
end
|
|
@@ -32,6 +32,8 @@ Capybara.singleton_class.prepend(Module.new do
|
|
|
32
32
|
end)
|
|
33
33
|
|
|
34
34
|
# Configure server host and port
|
|
35
|
+
# This is loaded for all environments
|
|
36
|
+
# → you do not want fixed ports there
|
|
35
37
|
Capybara.server_host = "127.0.0.1"
|
|
36
38
|
Capybara.server_port = ENV["CAPYBARA_PORT"]&.to_i # Let Capybara choose if not specified
|
|
37
39
|
|
|
@@ -15,11 +15,17 @@ RSpec.configure do |config|
|
|
|
15
15
|
config.before(:suite) do
|
|
16
16
|
Capybara.server = :puma_ci
|
|
17
17
|
Capybara.default_max_wait_time = Integer(ENV.fetch("CAPYBARA_MAX_WAIT_TIME", 5))
|
|
18
|
+
|
|
19
|
+
port = Integer(ENV.fetch("CAPYBARA_PORT", 3001))
|
|
18
20
|
Capybara.server_host = "127.0.0.1"
|
|
21
|
+
Capybara.server_port = port
|
|
22
|
+
Capybara.app_host = "http://127.0.0.1:#{port}"
|
|
19
23
|
Capybara.always_include_port = true
|
|
20
24
|
|
|
21
25
|
puts "[CI Config] Capybara.server = #{Capybara.server.inspect}"
|
|
26
|
+
puts "[CI Config] Capybara.app_host = #{Capybara.app_host.inspect}"
|
|
22
27
|
puts "[CI Config] Capybara.server_host = #{Capybara.server_host.inspect}"
|
|
28
|
+
puts "[CI Config] Capybara.server_port = #{Capybara.server_port.inspect}"
|
|
23
29
|
puts "[CI Config] Capybara.max_wait = #{Capybara.default_max_wait_time}s"
|
|
24
30
|
end
|
|
25
31
|
end
|
|
@@ -35,8 +41,8 @@ Capybara.register_server :puma_ci do |app, port, host|
|
|
|
35
41
|
Port: port,
|
|
36
42
|
Threads: "#{min_threads}:#{max_threads}",
|
|
37
43
|
Workers: 0,
|
|
38
|
-
Silent:
|
|
39
|
-
Verbose:
|
|
44
|
+
Silent: !ENV["RSPEC_DEBUG"],
|
|
45
|
+
Verbose: ENV["RSPEC_DEBUG"],
|
|
40
46
|
PreloadApp: false
|
|
41
47
|
}
|
|
42
48
|
|
|
@@ -19,15 +19,18 @@ module Panda
|
|
|
19
19
|
module CupriteSetup
|
|
20
20
|
# Base Cuprite options shared across all drivers
|
|
21
21
|
def self.base_options
|
|
22
|
+
default_timeout = 2
|
|
23
|
+
default_process_timeout = 2
|
|
24
|
+
|
|
22
25
|
{
|
|
23
26
|
window_size: [1440, 1000],
|
|
24
27
|
inspector: ENV["INSPECTOR"].in?(%w[y 1 yes true]),
|
|
25
28
|
headless: !ENV["HEADLESS"].in?(%w[n 0 no false]),
|
|
26
29
|
slowmo: ENV["SLOWMO"]&.to_f || 0,
|
|
27
|
-
timeout: ENV["CUPRITE_TIMEOUT"]&.to_i ||
|
|
30
|
+
timeout: ENV["CUPRITE_TIMEOUT"]&.to_i || default_timeout,
|
|
28
31
|
js_errors: true, # IMPORTANT: Report JavaScript errors as test failures
|
|
29
32
|
ignore_default_browser_options: false,
|
|
30
|
-
process_timeout: ENV["CUPRITE_PROCESS_TIMEOUT"]&.to_i ||
|
|
33
|
+
process_timeout: ENV["CUPRITE_PROCESS_TIMEOUT"]&.to_i || default_process_timeout,
|
|
31
34
|
wait_for_network_idle: false, # Don't wait for all network requests
|
|
32
35
|
pending_connection_errors: false, # Don't fail on pending external connections
|
|
33
36
|
browser_options: {
|
data/lib/panda/core/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: panda-core
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.9.
|
|
4
|
+
version: 0.9.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Otaina Limited
|
|
8
8
|
- James Inman
|
|
9
|
+
autorequire:
|
|
9
10
|
bindir: bin
|
|
10
11
|
cert_chain: []
|
|
11
|
-
date:
|
|
12
|
+
date: 2025-11-15 00:00:00.000000000 Z
|
|
12
13
|
dependencies:
|
|
13
14
|
- !ruby/object:Gem::Dependency
|
|
14
15
|
name: image_processing
|
|
@@ -193,7 +194,21 @@ dependencies:
|
|
|
193
194
|
- !ruby/object:Gem::Version
|
|
194
195
|
version: '0'
|
|
195
196
|
- !ruby/object:Gem::Dependency
|
|
196
|
-
name:
|
|
197
|
+
name: webrick
|
|
198
|
+
requirement: !ruby/object:Gem::Requirement
|
|
199
|
+
requirements:
|
|
200
|
+
- - ">="
|
|
201
|
+
- !ruby/object:Gem::Version
|
|
202
|
+
version: '0'
|
|
203
|
+
type: :runtime
|
|
204
|
+
prerelease: false
|
|
205
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
206
|
+
requirements:
|
|
207
|
+
- - ">="
|
|
208
|
+
- !ruby/object:Gem::Version
|
|
209
|
+
version: '0'
|
|
210
|
+
- !ruby/object:Gem::Dependency
|
|
211
|
+
name: benchmark
|
|
197
212
|
requirement: !ruby/object:Gem::Requirement
|
|
198
213
|
requirements:
|
|
199
214
|
- - ">="
|
|
@@ -207,7 +222,7 @@ dependencies:
|
|
|
207
222
|
- !ruby/object:Gem::Version
|
|
208
223
|
version: '0'
|
|
209
224
|
- !ruby/object:Gem::Dependency
|
|
210
|
-
name: rails
|
|
225
|
+
name: rspec-rails
|
|
211
226
|
requirement: !ruby/object:Gem::Requirement
|
|
212
227
|
requirements:
|
|
213
228
|
- - ">="
|
|
@@ -221,7 +236,7 @@ dependencies:
|
|
|
221
236
|
- !ruby/object:Gem::Version
|
|
222
237
|
version: '0'
|
|
223
238
|
- !ruby/object:Gem::Dependency
|
|
224
|
-
name:
|
|
239
|
+
name: rails-controller-testing
|
|
225
240
|
requirement: !ruby/object:Gem::Requirement
|
|
226
241
|
requirements:
|
|
227
242
|
- - ">="
|
|
@@ -235,7 +250,7 @@ dependencies:
|
|
|
235
250
|
- !ruby/object:Gem::Version
|
|
236
251
|
version: '0'
|
|
237
252
|
- !ruby/object:Gem::Dependency
|
|
238
|
-
name:
|
|
253
|
+
name: capybara
|
|
239
254
|
requirement: !ruby/object:Gem::Requirement
|
|
240
255
|
requirements:
|
|
241
256
|
- - ">="
|
|
@@ -249,7 +264,7 @@ dependencies:
|
|
|
249
264
|
- !ruby/object:Gem::Version
|
|
250
265
|
version: '0'
|
|
251
266
|
- !ruby/object:Gem::Dependency
|
|
252
|
-
name:
|
|
267
|
+
name: cuprite
|
|
253
268
|
requirement: !ruby/object:Gem::Requirement
|
|
254
269
|
requirements:
|
|
255
270
|
- - ">="
|
|
@@ -263,7 +278,7 @@ dependencies:
|
|
|
263
278
|
- !ruby/object:Gem::Version
|
|
264
279
|
version: '0'
|
|
265
280
|
- !ruby/object:Gem::Dependency
|
|
266
|
-
name:
|
|
281
|
+
name: database_cleaner-active_record
|
|
267
282
|
requirement: !ruby/object:Gem::Requirement
|
|
268
283
|
requirements:
|
|
269
284
|
- - ">="
|
|
@@ -277,7 +292,7 @@ dependencies:
|
|
|
277
292
|
- !ruby/object:Gem::Version
|
|
278
293
|
version: '0'
|
|
279
294
|
- !ruby/object:Gem::Dependency
|
|
280
|
-
name:
|
|
295
|
+
name: shoulda-matchers
|
|
281
296
|
requirement: !ruby/object:Gem::Requirement
|
|
282
297
|
requirements:
|
|
283
298
|
- - ">="
|
|
@@ -291,7 +306,7 @@ dependencies:
|
|
|
291
306
|
- !ruby/object:Gem::Version
|
|
292
307
|
version: '0'
|
|
293
308
|
- !ruby/object:Gem::Dependency
|
|
294
|
-
name: simplecov
|
|
309
|
+
name: simplecov
|
|
295
310
|
requirement: !ruby/object:Gem::Requirement
|
|
296
311
|
requirements:
|
|
297
312
|
- - ">="
|
|
@@ -305,7 +320,7 @@ dependencies:
|
|
|
305
320
|
- !ruby/object:Gem::Version
|
|
306
321
|
version: '0'
|
|
307
322
|
- !ruby/object:Gem::Dependency
|
|
308
|
-
name:
|
|
323
|
+
name: simplecov-json
|
|
309
324
|
requirement: !ruby/object:Gem::Requirement
|
|
310
325
|
requirements:
|
|
311
326
|
- - ">="
|
|
@@ -319,7 +334,7 @@ dependencies:
|
|
|
319
334
|
- !ruby/object:Gem::Version
|
|
320
335
|
version: '0'
|
|
321
336
|
- !ruby/object:Gem::Dependency
|
|
322
|
-
name:
|
|
337
|
+
name: standard
|
|
323
338
|
requirement: !ruby/object:Gem::Requirement
|
|
324
339
|
requirements:
|
|
325
340
|
- - ">="
|
|
@@ -333,7 +348,7 @@ dependencies:
|
|
|
333
348
|
- !ruby/object:Gem::Version
|
|
334
349
|
version: '0'
|
|
335
350
|
- !ruby/object:Gem::Dependency
|
|
336
|
-
name:
|
|
351
|
+
name: brakeman
|
|
337
352
|
requirement: !ruby/object:Gem::Requirement
|
|
338
353
|
requirements:
|
|
339
354
|
- - ">="
|
|
@@ -347,7 +362,7 @@ dependencies:
|
|
|
347
362
|
- !ruby/object:Gem::Version
|
|
348
363
|
version: '0'
|
|
349
364
|
- !ruby/object:Gem::Dependency
|
|
350
|
-
name:
|
|
365
|
+
name: bundler-audit
|
|
351
366
|
requirement: !ruby/object:Gem::Requirement
|
|
352
367
|
requirements:
|
|
353
368
|
- - ">="
|
|
@@ -501,20 +516,19 @@ files:
|
|
|
501
516
|
- lib/panda/core/testing/rails_helper.rb
|
|
502
517
|
- lib/panda/core/testing/support/authentication_helpers.rb
|
|
503
518
|
- lib/panda/core/testing/support/authentication_test_helpers.rb
|
|
504
|
-
- lib/panda/core/testing/support/generator_spec_helper.rb
|
|
505
519
|
- lib/panda/core/testing/support/html_helpers.rb
|
|
506
520
|
- lib/panda/core/testing/support/omniauth_setup.rb
|
|
507
521
|
- lib/panda/core/testing/support/service_stubs.rb
|
|
508
522
|
- lib/panda/core/testing/support/setup.rb
|
|
509
523
|
- lib/panda/core/testing/support/system/better_system_tests.rb
|
|
510
|
-
- lib/panda/core/testing/support/system/
|
|
524
|
+
- lib/panda/core/testing/support/system/browser_console_logger.rb
|
|
525
|
+
- lib/panda/core/testing/support/system/capybara_config.rb
|
|
511
526
|
- lib/panda/core/testing/support/system/ci_capybara_config.rb
|
|
512
527
|
- lib/panda/core/testing/support/system/cuprite_helpers.rb
|
|
513
528
|
- lib/panda/core/testing/support/system/cuprite_setup.rb
|
|
514
529
|
- lib/panda/core/testing/support/system/database_connection_helpers.rb
|
|
515
530
|
- lib/panda/core/testing/support/system/system_test_helpers.rb
|
|
516
531
|
- lib/panda/core/version.rb
|
|
517
|
-
- lib/tasks/assets.rake
|
|
518
532
|
- lib/tasks/panda/core/migrations.rake
|
|
519
533
|
- lib/tasks/panda_core.rake
|
|
520
534
|
- lib/tasks/panda_core_tasks.rake
|
|
@@ -529,6 +543,7 @@ metadata:
|
|
|
529
543
|
homepage_uri: https://github.com/tastybamboo/panda-core
|
|
530
544
|
source_code_uri: https://github.com/tastybamboo/panda-core
|
|
531
545
|
changelog_uri: https://github.com/tastybamboo/panda-core/blob/main/CHANGELOG.md
|
|
546
|
+
post_install_message:
|
|
532
547
|
rdoc_options: []
|
|
533
548
|
require_paths:
|
|
534
549
|
- lib
|
|
@@ -543,7 +558,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
543
558
|
- !ruby/object:Gem::Version
|
|
544
559
|
version: '0'
|
|
545
560
|
requirements: []
|
|
546
|
-
rubygems_version: 3.
|
|
561
|
+
rubygems_version: 3.5.22
|
|
562
|
+
signing_key:
|
|
547
563
|
specification_version: 4
|
|
548
564
|
summary: Core libraries and development tools for Tasty Bamboo projects
|
|
549
565
|
test_files: []
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
require "rails/generators"
|
|
2
|
-
|
|
3
|
-
module GeneratorSpecHelper
|
|
4
|
-
extend ActiveSupport::Concern
|
|
5
|
-
|
|
6
|
-
included do
|
|
7
|
-
before(:each) do
|
|
8
|
-
prepare_destination
|
|
9
|
-
@original_stdout = $stdout
|
|
10
|
-
$stdout = File.new(File::NULL, "w")
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
after(:each) do
|
|
14
|
-
FileUtils.rm_rf(destination_root)
|
|
15
|
-
$stdout = @original_stdout
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def destination_root
|
|
20
|
-
@destination_root ||= File.expand_path("../../tmp/generators", __dir__)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def prepare_destination
|
|
24
|
-
FileUtils.rm_rf(destination_root)
|
|
25
|
-
FileUtils.mkdir_p(destination_root)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def run_generator(args = [])
|
|
29
|
-
args = Array(args)
|
|
30
|
-
# Use the generator namespace instead of class name
|
|
31
|
-
generator_name = described_class.namespace
|
|
32
|
-
Rails::Generators.invoke(generator_name, args, destination_root: destination_root)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def generator
|
|
36
|
-
@generator ||= described_class.new([], destination_root: destination_root)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def file_exists?(path)
|
|
40
|
-
File.exist?(File.join(destination_root, path))
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def read_file(path)
|
|
44
|
-
File.read(File.join(destination_root, path))
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
private
|
|
48
|
-
|
|
49
|
-
def capture(stream)
|
|
50
|
-
stream = stream.to_s
|
|
51
|
-
captured_stream = StringIO.new
|
|
52
|
-
|
|
53
|
-
# Map stream names to their global variables to avoid eval
|
|
54
|
-
streams = {
|
|
55
|
-
"stdout" => $stdout,
|
|
56
|
-
"stderr" => $stderr,
|
|
57
|
-
"stdin" => $stdin
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
original_stream = streams[stream]
|
|
61
|
-
case stream
|
|
62
|
-
when "stdout"
|
|
63
|
-
$stdout = captured_stream
|
|
64
|
-
when "stderr"
|
|
65
|
-
$stderr = captured_stream
|
|
66
|
-
when "stdin"
|
|
67
|
-
$stdin = captured_stream
|
|
68
|
-
else
|
|
69
|
-
raise ArgumentError, "Unsupported stream: #{stream}"
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
yield
|
|
73
|
-
captured_stream.string
|
|
74
|
-
ensure
|
|
75
|
-
case stream
|
|
76
|
-
when "stdout"
|
|
77
|
-
$stdout = original_stream
|
|
78
|
-
when "stderr"
|
|
79
|
-
$stderr = original_stream
|
|
80
|
-
when "stdin"
|
|
81
|
-
$stdin = original_stream
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
RSpec.configure do |config|
|
|
87
|
-
config.include GeneratorSpecHelper, type: :generator
|
|
88
|
-
|
|
89
|
-
# Ensure generator tests have a clean environment
|
|
90
|
-
config.before(:each, type: :generator) do
|
|
91
|
-
prepare_destination
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
config.after(:each, type: :generator) do
|
|
95
|
-
FileUtils.rm_rf(destination_root) if defined?(destination_root)
|
|
96
|
-
end
|
|
97
|
-
end
|
data/lib/tasks/assets.rake
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#
|
|
4
|
-
# Panda Core Asset Tasks
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
namespace :panda do
|
|
8
|
-
namespace :core do
|
|
9
|
-
namespace :assets do
|
|
10
|
-
# =========================================================
|
|
11
|
-
# 1) ENGINE CSS COMPILATION (unchanged, just cleaned)
|
|
12
|
-
# =========================================================
|
|
13
|
-
|
|
14
|
-
desc "Compile Panda Core CSS assets (development mode)"
|
|
15
|
-
task :compile do
|
|
16
|
-
puts "🐼 Compiling Panda Core CSS assets..."
|
|
17
|
-
|
|
18
|
-
output_dir = panda_engine_root.join("public", "panda-core-assets")
|
|
19
|
-
FileUtils.mkdir_p(output_dir)
|
|
20
|
-
|
|
21
|
-
compile_css_development(output_dir)
|
|
22
|
-
|
|
23
|
-
puts "🎉 CSS compiled → #{output_dir}"
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
desc "Compile and version Panda Core CSS assets for release"
|
|
27
|
-
task :release do
|
|
28
|
-
require_relative "../../panda/core/version"
|
|
29
|
-
|
|
30
|
-
version = Panda::Core::VERSION
|
|
31
|
-
output_dir = panda_engine_root.join("public", "panda-core-assets")
|
|
32
|
-
FileUtils.mkdir_p(output_dir)
|
|
33
|
-
|
|
34
|
-
compile_css_release(output_dir, version)
|
|
35
|
-
|
|
36
|
-
puts "🎉 Release assets compiled"
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
# =========================================================
|
|
40
|
-
# CSS INTERNAL HELPERS
|
|
41
|
-
# =========================================================
|
|
42
|
-
|
|
43
|
-
def panda_engine_root
|
|
44
|
-
Panda::Core::Engine.root
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def compile_css_development(output_dir)
|
|
48
|
-
input = panda_engine_root.join("app/assets/tailwind/application.css")
|
|
49
|
-
output = output_dir.join("panda-core.css")
|
|
50
|
-
|
|
51
|
-
content = Panda::Core::ModuleRegistry.tailwind_content_paths
|
|
52
|
-
flags = content.map { |p| "--content '#{p}'" }.join(" ")
|
|
53
|
-
|
|
54
|
-
cmd = "bundle exec tailwindcss -i #{input} -o #{output} #{flags} --minify"
|
|
55
|
-
abort("❌ CSS compile failed") unless system(cmd)
|
|
56
|
-
|
|
57
|
-
puts " ✓ #{output.basename} (#{File.size(output)} bytes)"
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def compile_css_release(output_dir, version)
|
|
61
|
-
input = panda_engine_root.join("app/assets/tailwind/application.css")
|
|
62
|
-
file = output_dir.join("panda-core-#{version}.css")
|
|
63
|
-
|
|
64
|
-
content = Panda::Core::ModuleRegistry.tailwind_content_paths
|
|
65
|
-
flags = content.map { |p| "--content '#{p}'" }.join(" ")
|
|
66
|
-
|
|
67
|
-
cmd = "bundle exec tailwindcss -i #{input} -o #{file} #{flags} --minify"
|
|
68
|
-
abort("❌ CSS release compile failed") unless system(cmd)
|
|
69
|
-
|
|
70
|
-
symlink = output_dir.join("panda-core.css")
|
|
71
|
-
FileUtils.rm_f(symlink)
|
|
72
|
-
FileUtils.ln_sf(file.basename, symlink)
|
|
73
|
-
|
|
74
|
-
puts " ✓ Versioned file + symlink created"
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
# =========================================================
|
|
78
|
-
# 2) DUMMY APP PREP (FOR SYSTEM TESTS + CI)
|
|
79
|
-
# =========================================================
|
|
80
|
-
|
|
81
|
-
desc "Compile Panda Core + dummy app assets into spec/dummy/public/assets"
|
|
82
|
-
task compile_dummy: :environment do
|
|
83
|
-
dummy_root = find_dummy_root
|
|
84
|
-
assets_root = dummy_root.join("public/assets")
|
|
85
|
-
|
|
86
|
-
puts "🚧 Compiling dummy assets..."
|
|
87
|
-
puts " → #{assets_root}"
|
|
88
|
-
FileUtils.mkdir_p(assets_root)
|
|
89
|
-
|
|
90
|
-
Dir.chdir(dummy_root) do
|
|
91
|
-
# IMPORTANT: this is now the correct task name
|
|
92
|
-
unless system("bundle exec rake panda:core:assets:compile")
|
|
93
|
-
abort("❌ panda:core:assets:compile failed in dummy app")
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
# Propshaft (Rails 8)
|
|
97
|
-
abort("❌ assets:precompile failed") \
|
|
98
|
-
unless system("bundle exec rake assets:precompile RAILS_ENV=#{Rails.env}")
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
puts "✅ Dummy assets compiled"
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
desc "Generate importmap.json for the dummy app"
|
|
105
|
-
task generate_dummy_importmap: :environment do
|
|
106
|
-
dummy_root = find_dummy_root
|
|
107
|
-
output = dummy_root.join("public/assets/importmap.json")
|
|
108
|
-
|
|
109
|
-
puts "🗺️ Generating importmap.json..."
|
|
110
|
-
FileUtils.mkdir_p(output.dirname)
|
|
111
|
-
|
|
112
|
-
Dir.chdir(dummy_root) do
|
|
113
|
-
json = Rails.application.importmap.to_json(
|
|
114
|
-
resolver: ActionController::Base.helpers
|
|
115
|
-
)
|
|
116
|
-
File.write(output, JSON.pretty_generate(json))
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
puts " ✓ importmap.json written"
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
desc "Verify dummy assets for CI (fail-fast)"
|
|
123
|
-
task verify_dummy: :environment do
|
|
124
|
-
dummy_root = find_dummy_root
|
|
125
|
-
assets = dummy_root.join("public/assets")
|
|
126
|
-
manifest = assets.join(".manifest.json")
|
|
127
|
-
importmap = assets.join("importmap.json")
|
|
128
|
-
|
|
129
|
-
abort("❌ Missing #{assets}") unless assets.exist?
|
|
130
|
-
abort("❌ Missing #{manifest}") unless manifest.exist?
|
|
131
|
-
abort("❌ Missing #{importmap}") unless importmap.exist?
|
|
132
|
-
|
|
133
|
-
begin
|
|
134
|
-
parsed = JSON.parse(File.read(manifest))
|
|
135
|
-
abort("❌ Empty .manifest.json") if parsed.empty?
|
|
136
|
-
rescue
|
|
137
|
-
abort("❌ Invalid .manifest.json")
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
puts "✅ Dummy assets verified"
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
# =========================================================
|
|
144
|
-
# INTERNAL UTILITIES
|
|
145
|
-
# =========================================================
|
|
146
|
-
|
|
147
|
-
def find_dummy_root
|
|
148
|
-
root = Rails.root
|
|
149
|
-
|
|
150
|
-
return root if root.basename.to_s == "dummy"
|
|
151
|
-
|
|
152
|
-
candidate = root.join("spec/dummy")
|
|
153
|
-
return candidate if candidate.exist?
|
|
154
|
-
|
|
155
|
-
abort("❌ Cannot find dummy root — expected #{candidate}")
|
|
156
|
-
end
|
|
157
|
-
end
|
|
158
|
-
end
|
|
159
|
-
end
|