mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-05-03 00:20:04 +08:00
This PR implements stricter deprecation handling that enforces deprecation-free tests for core and preinstalled plugins, while allowing custom (non-preinstalled) plugins and themes to have deprecations without causing test failures. ### Key Changes #### CI Workflow Improvements - Split plugin system tests into separate CI targets: `core-plugins`, `official-plugins`, and `chat` - Enhance `bin/turbo_rspec` to accept comma-separated exclude patterns via `--exclude-pattern` - Simplify workflow configuration with default `shell: bash` and consolidated environment variables #### Plugin Classification & Detection - Centralize official plugins list in `config/official_plugins.json` for unified backend and frontend access - Detect preinstalled plugins by checking for absence of `.git` directory - Add `isOfficial` and `isPreinstalled` metadata flags to plugin info - Add `data-preinstalled` and `data-official` attributes to all plugin and theme script tags for runtime identification #### Deprecation Source Tracking - Track deprecation sources (core, plugin, or theme) through template map and resolver to attribute deprecations correctly - Improve `source-identifier.js` to detect admin UI plugin files in both development and production environments - Add source information to deprecation messages for better debugging #### Test Infrastructure - Modify `raise-on-deprecation` test helper to skip errors for custom (non-preinstalled) plugins and themes - Add `EMBER_RAISE_ON_DEPRECATION` environment variable to control deprecation throwing behavior in Rails tests - Automatically set `EMBER_RAISE_ON_DEPRECATION` for core and preinstalled plugin/theme specs in `rails_helper.rb` - Improve deprecation summary output for system specs with test/spec origin tracking #### Deprecation Workflow Enhancements - Add `dont-throw` handler for selective deprecation bypassing in test fixtures without raising errors - Add `dont-count` handler for preventing deprecation counting in specific scenarios (e.g., test fixtures) #### Deprecation Fixes - Fix pending deprecations across core plugins (chat, data-explorer, discourse-subscriptions, gamification, house-ads, reactions, rss-polling, styleguide) - Update import paths and remove deprecated patterns - Migrate deprecated Handlebars templates to JavaScript API ### Testing Strategy With these changes: - **Core and preinstalled plugins** must pass all tests without any deprecations - **Custom plugins and themes** can have deprecations without failing tests - Test fixtures can use `dont-throw` and `dont-count` handlers when testing deprecation behavior itself - System specs automatically configure deprecation enforcement based on test file location --------- Co-authored-by: David Taylor <david@taylorhq.com> Co-authored-by: Jarek Radosz <jradosz@gmail.com>
125 lines
4.2 KiB
Ruby
125 lines
4.2 KiB
Ruby
# frozen_string_literal: true
|
|
RSpec::Support.require_rspec_core "formatters/base_text_formatter"
|
|
RSpec::Support.require_rspec_core "formatters/console_codes"
|
|
|
|
module TurboTests
|
|
class BaseFormatter < RSpec::Core::Formatters::BaseTextFormatter
|
|
RSpec::Core::Formatters.register(self, :dump_summary)
|
|
|
|
def dump_summary(notification, timings)
|
|
output_slowest_examples(timings) if timings.present?
|
|
|
|
totals_by_id, totals_by_origin = aggregate_js_deprecations(notification.examples)
|
|
output_js_deprecations(totals_by_id, totals_by_origin) if totals_by_id.present?
|
|
|
|
super(notification)
|
|
end
|
|
|
|
private
|
|
|
|
def output_slowest_examples(timings)
|
|
output.puts "\nTop #{timings.size} Slowest examples:"
|
|
timings.each do |(full_description, source_location, duration)|
|
|
output.puts " #{full_description}"
|
|
output.puts " #{RSpec::Core::Formatters::ConsoleCodes.wrap(duration.to_s + "ms", :bold)} #{source_location}"
|
|
end
|
|
end
|
|
|
|
def aggregate_js_deprecations(examples)
|
|
totals_by_id = Hash.new(0)
|
|
totals_by_origin = Hash.new { |h, k| h[k] = Hash.new(0) }
|
|
|
|
examples.each do |example|
|
|
origin = extract_origin_from_example(example) || "unknown"
|
|
|
|
example.metadata[:js_deprecations]&.each do |id, count|
|
|
totals_by_id[id] += count
|
|
totals_by_origin[origin][id] += count
|
|
end
|
|
end
|
|
|
|
[totals_by_id, totals_by_origin]
|
|
end
|
|
|
|
def output_js_deprecations(totals_by_id, totals_by_origin)
|
|
output.puts "\n[Deprecation Counter] Test run completed with deprecations:\n\n"
|
|
|
|
deprecations_table = generate_deprecations_table(totals_by_id)
|
|
output.puts deprecations_table
|
|
|
|
origin_table = nil
|
|
if totals_by_origin.any?
|
|
origin_table = generate_deprecations_by_origin_table(totals_by_origin)
|
|
output.puts "\nDeprecations by spec origin:\n\n"
|
|
output.puts origin_table
|
|
end
|
|
|
|
write_github_summary(deprecations_table, origin_table)
|
|
end
|
|
|
|
def generate_deprecations_table(totals_by_id)
|
|
max_id_length = totals_by_id.keys.map(&:length).max
|
|
|
|
headers = ["id".ljust(max_id_length), "count".rjust(5)]
|
|
rows = totals_by_id.map { |id, count| [id.ljust(max_id_length), count.to_s.rjust(5)] }
|
|
|
|
build_markdown_table(headers, rows)
|
|
end
|
|
|
|
def generate_deprecations_by_origin_table(totals_by_origin)
|
|
all_ids = totals_by_origin.values.flat_map(&:keys).uniq
|
|
max_id_length = all_ids.map(&:length).max
|
|
origins = totals_by_origin.keys.sort
|
|
max_origin_length = [origins.map(&:length).max, 6].max
|
|
|
|
headers = ["origin".ljust(max_origin_length), "id".ljust(max_id_length), "count".rjust(5)]
|
|
rows = []
|
|
|
|
origins.each do |origin|
|
|
origin_deprecations = totals_by_origin[origin]
|
|
sorted_ids = origin_deprecations.keys.sort
|
|
|
|
sorted_ids.each do |id|
|
|
count = origin_deprecations[id]
|
|
rows += [[origin.ljust(max_origin_length), id.ljust(max_id_length), count.to_s.rjust(5)]]
|
|
end
|
|
end
|
|
|
|
build_markdown_table(headers, rows)
|
|
end
|
|
|
|
def build_markdown_table(headers, rows)
|
|
table = "| #{headers.join(" | ")} |\n"
|
|
table += "| #{headers.map { |h| "-" * h.length }.join(" | ")} |\n"
|
|
rows.each { |row| table += "| #{row.join(" | ")} |\n" }
|
|
table
|
|
end
|
|
|
|
def write_github_summary(deprecations_table, origin_table)
|
|
return unless ENV["GITHUB_ACTIONS"] && ENV["GITHUB_STEP_SUMMARY"]
|
|
|
|
summary = "### ⚠️ JS Deprecations\n\nTest run completed with deprecations:\n\n"
|
|
summary += deprecations_table
|
|
summary += "\n\nDeprecations by spec origin:\n\n#{origin_table}" if origin_table
|
|
summary += "\n\n"
|
|
|
|
File.write(ENV["GITHUB_STEP_SUMMARY"], summary)
|
|
end
|
|
|
|
def extract_origin_from_example(example)
|
|
example_file_path = example.metadata[:rerun_file_path]
|
|
return nil unless example_file_path
|
|
|
|
expanded_example_file_path = Pathname.new(example_file_path).expand_path
|
|
return nil unless expanded_example_file_path.to_s.start_with?(Rails.root.to_s)
|
|
|
|
extension_match = example_file_path.match(%r{/(plugins|themes)/([^/]+)/})
|
|
if extension_match
|
|
_type_dir, extension_name = extension_match.captures
|
|
extension_name
|
|
else
|
|
"core"
|
|
end
|
|
end
|
|
end
|
|
end
|