diff --git a/.gitignore b/.gitignore index 54fa3ab9d..c136b0d03 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ *.xcworkspace/ .DS_Store .android-test-* +.bundle/* .ccache/* !.ccache/ccache.conf .cxx/ diff --git a/packages/app/.rubocop.yml b/.rubocop.yml similarity index 93% rename from packages/app/.rubocop.yml rename to .rubocop.yml index 9c2403a3c..f35cabf2d 100644 --- a/packages/app/.rubocop.yml +++ b/.rubocop.yml @@ -62,8 +62,8 @@ Style/HashTransformValues: Style/OneClassPerFile: Exclude: - - ios/test_app.rb - - test/test_test_app.rb + - packages/app/ios/test_app.rb + - packages/app/test/test_test_app.rb Style/TrailingCommaInArrayLiteral: EnforcedStyleForMultiline: comma diff --git a/packages/app/package.json b/packages/app/package.json index 9ee534f9b..1cfc18f4f 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -83,7 +83,7 @@ "test": "node scripts/internal/test.mts", "test:js": "node --no-warnings --test $(git ls-files '*.test.mts')", "test:matrix": "node scripts/testing/test-matrix.mts", - "test:rb": "bundle exec ruby -Ilib:test -e \"Dir.glob('./test/test_*.rb').each { |file| require(file) }\"" + "test:rb": "bundle exec ruby -Ilib:test -rgithub_annotations -e \"Dir.glob('./test/test_*.rb').each { |file| require(file) }\"" }, "dependencies": { "@rnx-kit/react-native-host": "^0.5.21", diff --git a/packages/app/test/github_annotations.rb b/packages/app/test/github_annotations.rb new file mode 100644 index 000000000..5f722076a --- /dev/null +++ b/packages/app/test/github_annotations.rb @@ -0,0 +1,55 @@ +require('minitest') + +# Reports test failures as GitHub Actions annotations. Unlike problem matchers, +# workflow commands support multiline messages (newlines are encoded as `%0A`). +# +# See https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-commands +module GitHubAnnotations + class Reporter < Minitest::AbstractReporter + def initialize(io) + super() + @io = io + @annotations = [] + end + + def record(result) + return if result.passed? || result.skipped? + + failure = result.failure + file, line = parse_location(failure.location) + title = "#{result.class_name}##{result.name}" + @annotations << "::error file=#{escape_property(file)},line=#{line}," \ + "title=#{escape_property(title)}::#{escape_data(failure.message.rstrip)}" + end + + # Workflow commands must start on a new line, so we cannot print them + # while progress is being reported. + def report + @io.puts(@annotations) unless @annotations.empty? + end + + private + + def escape_data(value) + value.to_s.gsub('%', '%25').gsub("\r", '%0D').gsub("\n", '%0A') + end + + def escape_property(value) + escape_data(value).gsub(':', '%3A').gsub(',', '%2C') + end + + # The runner makes absolute paths under `GITHUB_WORKSPACE` relative to it. + def parse_location(location) + file, line = location.match(/\A(.*):(\d+)\z/)&.captures || [location, 1] + [File.expand_path(file), line] + end + end + + def self.minitest_plugin_init(options) + return unless ENV['GITHUB_ACTIONS'] == 'true' + + Minitest.reporter << Reporter.new(options[:io]) + end +end + +Minitest.register_plugin(GitHubAnnotations)