diff --git a/Makefile b/Makefile index 0827b35d1b3..5371b099936 100644 --- a/Makefile +++ b/Makefile @@ -52,5 +52,8 @@ normalize_yaml: i18n-tasks normalize find ./config/locales -type f | xargs ./scripts/normalize-yaml +check_asset_strings: + find ./app/javascript -name "*.js*" | xargs ./scripts/check-assets + generate_deploy_checklist: ruby lib/release_management/generate_deploy_checklist.rb diff --git a/lib/asset_checker.rb b/lib/asset_checker.rb new file mode 100644 index 00000000000..74e7cb78b48 --- /dev/null +++ b/lib/asset_checker.rb @@ -0,0 +1,37 @@ +class AssetChecker + def self.check_files(argv) + assets_file = 'app/assets/javascripts/assets.js.erb' + translations_file = 'app/assets/javascripts/i18n-strings.js.erb' + @asset_strings = load_included_strings(assets_file) + @translation_strings = load_included_strings(translations_file) + argv.any? { |f| file_has_missing?(f) } + end + + def self.file_has_missing?(file) + data = File.open(file).read + missing_translations = find_missing(data, /\Wt\s?\(['"]([^'^"]*)['"]\)/, @translation_strings) + missing_assets = find_missing(data, /\WassetPath=["'](.*)['"]/, @asset_strings) + has_missing = (missing_translations.any? || missing_assets.any?) + if has_missing + warn file + missing_translations.each do |t| + warn "Missing translation, #{t}" + end + missing_assets.each do |a| + warn "Missing asset, #{a}" + end + end + has_missing + end + + def self.find_missing(file_data, pattern, source) + strings = (file_data.scan pattern).flatten + strings.reject { |s| source.include? s } + end + + def self.load_included_strings(file) + data = File.open(file).read + key_data = data.split('<% keys = [')[1].split('] %>')[0] + key_data.scan(/['"](.*)['"]/).flatten + end +end diff --git a/scripts/check-assets b/scripts/check-assets new file mode 100755 index 00000000000..255e6e76b19 --- /dev/null +++ b/scripts/check-assets @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby +$LOAD_PATH.unshift(File.expand_path('../lib', File.dirname(__FILE__))) +require 'asset_checker' + +exit(AssetChecker.check_files(ARGV) ? 1 : 0) + diff --git a/spec/lib/asset_checker_spec.rb b/spec/lib/asset_checker_spec.rb new file mode 100644 index 00000000000..8c9b2f5647f --- /dev/null +++ b/spec/lib/asset_checker_spec.rb @@ -0,0 +1,81 @@ +require 'asset_checker' + +def get_js_with_strings(asset, translation) + " + import React from 'react'; + import AcuantCapture from './acuant-capture'; + import DocumentTips from './document-tips'; + import Image from './image'; + import useI18n from '../hooks/use-i18n'; + + function DocumentCapture() { + const t = useI18n(); + + const sample = ( + \"Sample + ); + + return ( + <> +

{t('#{translation}')}

+ + + + ); + } + + export default DocumentCapture; + " +end + +RSpec.describe AssetChecker do + describe '.check_files' do + let(:translation_strings) { %w[first_translation second_translation] } + let(:asset_strings) { %w[first_asset.png second_asset.gif] } + + context 'with matching assets' do + let(:tempfile) { Tempfile.new } + + before do + File.open(tempfile.path, 'w') do |f| + f.puts(get_js_with_strings('first_asset.png', 'first_translation')) + end + end + + after { tempfile.unlink } + + it 'identifies no issues ' do + allow(AssetChecker).to receive(:load_included_strings).and_return(asset_strings, + translation_strings) + + expect(AssetChecker.check_files([tempfile.path])).to eq(false) + end + end + + context 'with an asset mismatch' do + let(:tempfile) { Tempfile.new } + + before do + File.open(tempfile.path, 'w') do |f| + f.puts(get_js_with_strings('wont_find.svg', 'not-found')) + end + end + + after { tempfile.unlink } + + it 'identifies issues' do + allow(AssetChecker).to receive(:load_included_strings).and_return(asset_strings, + translation_strings) + expect(AssetChecker).to receive(:warn).with(tempfile.path) + expect(AssetChecker).to receive(:warn).with('Missing translation, not-found') + expect(AssetChecker).to receive(:warn).with('Missing asset, wont_find.svg') + expect(AssetChecker.check_files([tempfile.path])).to eq(true) + end + end + end +end