nextgen 0.1.0
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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +143 -0
- data/config/generators.yml +146 -0
- data/exe/nextgen +13 -0
- data/lib/nextgen/actions/bundler.rb +62 -0
- data/lib/nextgen/actions/git.rb +56 -0
- data/lib/nextgen/actions/yarn.rb +32 -0
- data/lib/nextgen/actions.rb +155 -0
- data/lib/nextgen/cli.rb +19 -0
- data/lib/nextgen/commands/create.rb +312 -0
- data/lib/nextgen/ext/prompt/list.rb +13 -0
- data/lib/nextgen/ext/prompt/multilist.rb +16 -0
- data/lib/nextgen/generators/action_mailer.rb +26 -0
- data/lib/nextgen/generators/annotate.rb +2 -0
- data/lib/nextgen/generators/base.rb +60 -0
- data/lib/nextgen/generators/basic_auth.rb +5 -0
- data/lib/nextgen/generators/brakeman.rb +2 -0
- data/lib/nextgen/generators/bundler_audit.rb +2 -0
- data/lib/nextgen/generators/capybara_lockstep.rb +4 -0
- data/lib/nextgen/generators/clean_gemfile.rb +1 -0
- data/lib/nextgen/generators/dotenv.rb +3 -0
- data/lib/nextgen/generators/erb_lint.rb +11 -0
- data/lib/nextgen/generators/eslint.rb +24 -0
- data/lib/nextgen/generators/factory_bot_rails.rb +16 -0
- data/lib/nextgen/generators/git_safe.rb +4 -0
- data/lib/nextgen/generators/github_actions.rb +2 -0
- data/lib/nextgen/generators/good_migrations.rb +1 -0
- data/lib/nextgen/generators/home_controller.rb +3 -0
- data/lib/nextgen/generators/initial_git_commit.rb +5 -0
- data/lib/nextgen/generators/initial_migrations.rb +11 -0
- data/lib/nextgen/generators/letter_opener.rb +8 -0
- data/lib/nextgen/generators/node.rb +5 -0
- data/lib/nextgen/generators/open_browser_on_start.rb +12 -0
- data/lib/nextgen/generators/overcommit.rb +1 -0
- data/lib/nextgen/generators/pgcli_rails.rb +1 -0
- data/lib/nextgen/generators/rack_canonical_host.rb +8 -0
- data/lib/nextgen/generators/rack_mini_profiler.rb +2 -0
- data/lib/nextgen/generators/rspec_rails.rb +19 -0
- data/lib/nextgen/generators/rspec_system_testing.rb +5 -0
- data/lib/nextgen/generators/rubocop.rb +32 -0
- data/lib/nextgen/generators/shoulda.rb +6 -0
- data/lib/nextgen/generators/sidekiq.rb +30 -0
- data/lib/nextgen/generators/stylelint.rb +24 -0
- data/lib/nextgen/generators/thor.rb +2 -0
- data/lib/nextgen/generators/tomo.rb +10 -0
- data/lib/nextgen/generators/vite.rb +103 -0
- data/lib/nextgen/generators.rb +87 -0
- data/lib/nextgen/rails.rb +39 -0
- data/lib/nextgen/rails_options.rb +191 -0
- data/lib/nextgen/thor_extensions.rb +48 -0
- data/lib/nextgen/tidy_gemfile.rb +71 -0
- data/lib/nextgen/version.rb +3 -0
- data/lib/nextgen.rb +17 -0
- data/template/.editorconfig +14 -0
- data/template/.env.sample +2 -0
- data/template/.erb-lint.yml.tt +25 -0
- data/template/.eslintrc.cjs +26 -0
- data/template/.github/workflows/ci.yml.tt +142 -0
- data/template/.overcommit.yml.tt +86 -0
- data/template/.prettierrc.cjs +6 -0
- data/template/.rubocop.yml.tt +269 -0
- data/template/.stylelintrc.cjs +52 -0
- data/template/DEPLOYMENT.md +10 -0
- data/template/Procfile.tt +4 -0
- data/template/README.md.tt +52 -0
- data/template/Thorfile +7 -0
- data/template/app/controllers/concerns/basic_auth.rb +20 -0
- data/template/app/controllers/home_controller.rb +4 -0
- data/template/app/frontend/controllers/index.js +5 -0
- data/template/app/frontend/images/example.svg +3 -0
- data/template/app/frontend/stylesheets/base.css +8 -0
- data/template/app/frontend/stylesheets/index.css +3 -0
- data/template/app/frontend/stylesheets/reset.css +36 -0
- data/template/app/helpers/inline_svg_helper.rb +9 -0
- data/template/app/views/home/index.html.erb.tt +5 -0
- data/template/bin/setup +107 -0
- data/template/config/initializers/generators.rb +5 -0
- data/template/config/initializers/rack_mini_profiler.rb +4 -0
- data/template/config/initializers/sidekiq.rb +32 -0
- data/template/config/sidekiq.yml +5 -0
- data/template/lib/puma/plugin/open.rb +14 -0
- data/template/lib/tasks/auto_annotate_models.rake +52 -0
- data/template/lib/tasks/erblint.rake +11 -0
- data/template/lib/tasks/eslint.rake +11 -0
- data/template/lib/tasks/rubocop.rake +4 -0
- data/template/lib/tasks/stylelint.rake +11 -0
- data/template/lib/templates/rspec/system/system_spec.rb +10 -0
- data/template/lib/vite_inline_svg_file_loader.rb +25 -0
- data/template/package.json +6 -0
- data/template/postcss.config.js +3 -0
- data/template/spec/support/factory_bot.rb +3 -0
- data/template/spec/support/mailer.rb +5 -0
- data/template/spec/support/shoulda.rb +6 -0
- data/template/spec/support/system.rb +17 -0
- data/template/test/application_system_test_case.rb +18 -0
- data/template/test/helpers/inline_svg_helper_test.rb +23 -0
- data/template/test/support/factory_bot.rb +3 -0
- data/template/test/support/mailer.rb +3 -0
- data/template/test/support/shoulda.rb +6 -0
- data/template/test/support/vite.rb +5 -0
- metadata +220 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/*
|
|
2
|
+
This file is for base element styles, like:
|
|
3
|
+
|
|
4
|
+
- Any @font-face declarations needed custom web fonts.
|
|
5
|
+
- Default font-family on the body element.
|
|
6
|
+
- Default foreground, background, and link colors.
|
|
7
|
+
- Global CSS variables (declared on :root), such as color palette.
|
|
8
|
+
*/
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
line-height: 1.5;
|
|
3
|
+
-webkit-font-smoothing: antialiased;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
h1,
|
|
7
|
+
h2,
|
|
8
|
+
h3,
|
|
9
|
+
h4,
|
|
10
|
+
h5,
|
|
11
|
+
figure,
|
|
12
|
+
p,
|
|
13
|
+
ol,
|
|
14
|
+
ul {
|
|
15
|
+
margin: 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
ol,
|
|
19
|
+
ul {
|
|
20
|
+
list-style: none;
|
|
21
|
+
padding-inline: 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
h1,
|
|
25
|
+
h2,
|
|
26
|
+
h3,
|
|
27
|
+
h4,
|
|
28
|
+
h5 {
|
|
29
|
+
font-size: inherit;
|
|
30
|
+
font-weight: inherit;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
img {
|
|
34
|
+
display: block;
|
|
35
|
+
max-inline-size: 100%;
|
|
36
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
module InlineSvgHelper
|
|
2
|
+
def inline_svg_tag(filename, title: nil)
|
|
3
|
+
svg = ViteInlineSvgFileLoader.named(filename)
|
|
4
|
+
svg = svg.sub(/\A<svg/, '<svg role="img"')
|
|
5
|
+
svg = svg.sub(/\A<svg.*?>/, safe_join(['\0', "\n", tag.title(title)])) if title.present?
|
|
6
|
+
|
|
7
|
+
svg.strip.html_safe # rubocop:disable Rails/OutputSafety
|
|
8
|
+
end
|
|
9
|
+
end
|
data/template/bin/setup
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# This script is a way to set up or update your development environment automatically.
|
|
4
|
+
# This script is idempotent, so that you can run it at any time and get an expectable outcome.
|
|
5
|
+
# Add necessary setup steps to this method.
|
|
6
|
+
def setup!
|
|
7
|
+
run "bundle install" if bundle_needed?
|
|
8
|
+
run "overcommit --install" if overcommit_installable?
|
|
9
|
+
run "bin/rails db:prepare" if database_present?
|
|
10
|
+
run "yarn install" if yarn_needed?
|
|
11
|
+
run "bin/rails tmp:create" if tmp_missing?
|
|
12
|
+
env ".env", from: ".env.sample"
|
|
13
|
+
run "bin/rails restart"
|
|
14
|
+
|
|
15
|
+
if git_safe_needed?
|
|
16
|
+
say_status :notice,
|
|
17
|
+
"Remember to run #{colorize("mkdir -p .git/safe", :yellow)} to trust the binstubs in this project",
|
|
18
|
+
:magenta
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
say_status :Ready!,
|
|
22
|
+
"Use #{colorize("bin/rails s", :yellow)} to start the app, " \
|
|
23
|
+
"or #{colorize("bin/rake", :yellow)} to run tests"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def run(command, echo: true, silent: false, exception: true)
|
|
27
|
+
say_status(:run, command, :blue) if echo
|
|
28
|
+
with_original_bundler_env do
|
|
29
|
+
options = silent ? {out: File::NULL, err: File::NULL} : {}
|
|
30
|
+
system(command, exception: exception, **options)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def run?(command)
|
|
35
|
+
run command, silent: true, echo: false, exception: false
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def bundle_needed?
|
|
39
|
+
!run("bundle check", silent: true, exception: false)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def overcommit_installable?
|
|
43
|
+
File.exist?(".overcommit.yml") && !File.exist?(".git/hooks/overcommit-hook") && run?("overcommit -v")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def database_present?
|
|
47
|
+
File.exist?("config/database.yml")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def yarn_needed?
|
|
51
|
+
File.exist?("yarn.lock") && !run("yarn check --check-files", silent: true, exception: false)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def tmp_missing?
|
|
55
|
+
!Dir.exist?("tmp/pids")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def git_safe_needed?
|
|
59
|
+
ENV["PATH"].include?(".git/safe/../../bin") && !Dir.exist?(".git/safe")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def with_original_bundler_env(&block)
|
|
63
|
+
return yield unless defined?(Bundler)
|
|
64
|
+
|
|
65
|
+
Bundler.with_original_env(&block)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def env(env_file, from:)
|
|
69
|
+
return unless File.exist?(from)
|
|
70
|
+
|
|
71
|
+
unless File.exist?(env_file)
|
|
72
|
+
say_status(:copy, "#{from} → #{env_file}", :magenta)
|
|
73
|
+
require "fileutils"
|
|
74
|
+
FileUtils.cp(from, env_file)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
keys = ->(f) { File.readlines(f).filter_map { |l| l[/^([^#\s][^=\s]*)/, 1] } }
|
|
78
|
+
|
|
79
|
+
missing = keys[from] - keys[env_file]
|
|
80
|
+
return if missing.empty?
|
|
81
|
+
|
|
82
|
+
say_status(:WARNING, "Your #{env_file} file is missing #{missing.join(", ")}. Refer to #{from} for details.", :red)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def say_status(label, message, color = :green)
|
|
86
|
+
label = label.to_s.rjust(12)
|
|
87
|
+
puts [colorize(label, color), message].join(" ")
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def colorize(str, color)
|
|
91
|
+
return str unless color_supported?
|
|
92
|
+
|
|
93
|
+
code = {red: 31, green: 32, yellow: 33, blue: 34, magenta: 35}.fetch(color)
|
|
94
|
+
"\e[0;#{code};49m#{str}\e[0m"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def color_supported?
|
|
98
|
+
if ENV["TERM"] == "dumb" || !ENV["NO_COLOR"].to_s.empty?
|
|
99
|
+
false
|
|
100
|
+
else
|
|
101
|
+
[$stdout, $stderr].all? { |io| io.respond_to?(:tty?) && io.tty? }
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
Dir.chdir(File.expand_path("..", __dir__)) do
|
|
106
|
+
setup!
|
|
107
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
return unless defined?(Sidekiq)
|
|
2
|
+
|
|
3
|
+
# Disable SSL certificate verification if using Heroku Redis
|
|
4
|
+
# redis_opts = {
|
|
5
|
+
# ssl_params: {
|
|
6
|
+
# verify_mode: OpenSSL::SSL::VERIFY_NONE
|
|
7
|
+
# }
|
|
8
|
+
# }
|
|
9
|
+
# Sidekiq.configure_server do |config|
|
|
10
|
+
# config.redis = redis_opts
|
|
11
|
+
# end
|
|
12
|
+
# Sidekiq.configure_client do |config|
|
|
13
|
+
# config.redis = redis_opts
|
|
14
|
+
# end
|
|
15
|
+
|
|
16
|
+
# Enable Rails CurrentAttributes to flow transparently through to Sidekiq jobs
|
|
17
|
+
# require "sidekiq/middleware/current_attributes"
|
|
18
|
+
# Sidekiq::CurrentAttributes.persist(Myapp::Current)
|
|
19
|
+
|
|
20
|
+
require "sidekiq/web"
|
|
21
|
+
|
|
22
|
+
Sidekiq::Web.app_url = "/"
|
|
23
|
+
|
|
24
|
+
sidekiq_username = ENV.fetch("SIDEKIQ_WEB_USERNAME", nil)
|
|
25
|
+
sidekiq_password = ENV.fetch("SIDEKIQ_WEB_PASSWORD", nil)
|
|
26
|
+
|
|
27
|
+
Sidekiq::Web.use(Rack::Auth::Basic, "Sidekiq") do |username, password|
|
|
28
|
+
if sidekiq_username.present? && sidekiq_password.present?
|
|
29
|
+
ActiveSupport::SecurityUtils.secure_compare(username, sidekiq_username) &
|
|
30
|
+
ActiveSupport::SecurityUtils.secure_compare(password, sidekiq_password)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require "puma/plugin"
|
|
2
|
+
|
|
3
|
+
Puma::Plugin.create do
|
|
4
|
+
def start(launcher)
|
|
5
|
+
return unless defined?(Rails) && defined?(Launchy)
|
|
6
|
+
return unless Rails.env.development?
|
|
7
|
+
|
|
8
|
+
binding = launcher.options[:binds].grep(/^tcp|ssl/).first
|
|
9
|
+
return if binding.nil?
|
|
10
|
+
|
|
11
|
+
url = binding.sub(/^tcp/, "http").sub(/^ssl/, "https").sub("0.0.0.0", "localhost")
|
|
12
|
+
Launchy.open(url)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
return unless Rails.env.development?
|
|
2
|
+
|
|
3
|
+
require "annotate"
|
|
4
|
+
task :set_annotation_options do # rubocop:disable Rake/Desc, Metrics/BlockLength
|
|
5
|
+
# You can override any of these by setting an environment variable of the same name.
|
|
6
|
+
Annotate.set_defaults(
|
|
7
|
+
"routes" => "false",
|
|
8
|
+
"models" => "true",
|
|
9
|
+
"position_in_routes" => "before",
|
|
10
|
+
"position_in_class" => "before",
|
|
11
|
+
"position_in_test" => "before",
|
|
12
|
+
"position_in_fixture" => "before",
|
|
13
|
+
"position_in_factory" => "before",
|
|
14
|
+
"position_in_serializer" => "before",
|
|
15
|
+
"show_foreign_keys" => "true",
|
|
16
|
+
"show_complete_foreign_keys" => "false",
|
|
17
|
+
"show_indexes" => "true",
|
|
18
|
+
"simple_indexes" => "false",
|
|
19
|
+
"model_dir" => "app/models",
|
|
20
|
+
"root_dir" => "",
|
|
21
|
+
"include_version" => "false",
|
|
22
|
+
"require" => "",
|
|
23
|
+
"exclude_tests" => "true",
|
|
24
|
+
"exclude_fixtures" => "false",
|
|
25
|
+
"exclude_factories" => "false",
|
|
26
|
+
"exclude_serializers" => "false",
|
|
27
|
+
"exclude_scaffolds" => "true",
|
|
28
|
+
"exclude_controllers" => "true",
|
|
29
|
+
"exclude_helpers" => "true",
|
|
30
|
+
"exclude_sti_subclasses" => "false",
|
|
31
|
+
"ignore_model_sub_dir" => "false",
|
|
32
|
+
"ignore_columns" => nil,
|
|
33
|
+
"ignore_routes" => nil,
|
|
34
|
+
"ignore_unknown_models" => "false",
|
|
35
|
+
"hide_limit_column_types" => "integer,bigint,boolean",
|
|
36
|
+
"hide_default_column_types" => "json,jsonb,hstore",
|
|
37
|
+
"skip_on_db_migrate" => "false",
|
|
38
|
+
"format_bare" => "true",
|
|
39
|
+
"format_rdoc" => "false",
|
|
40
|
+
"format_markdown" => "false",
|
|
41
|
+
"sort" => "false",
|
|
42
|
+
"force" => "false",
|
|
43
|
+
"frozen" => "false",
|
|
44
|
+
"classified_sort" => "true",
|
|
45
|
+
"trace" => "false",
|
|
46
|
+
"wrapper_open" => nil,
|
|
47
|
+
"wrapper_close" => nil,
|
|
48
|
+
"with_comment" => "true"
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
Annotate.load_tasks
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module ViteInlineSvgFileLoader
|
|
2
|
+
class << self
|
|
3
|
+
def named(filename)
|
|
4
|
+
vite = ViteRuby.instance
|
|
5
|
+
vite_asset_path = vite.manifest.path_for(filename)
|
|
6
|
+
|
|
7
|
+
if vite.dev_server_running?
|
|
8
|
+
fetch_from_dev_server(vite_asset_path)
|
|
9
|
+
else
|
|
10
|
+
Rails.public_path.join(vite_asset_path.sub(%r{^/}, "")).read
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def fetch_from_dev_server(path)
|
|
17
|
+
config = ViteRuby.config
|
|
18
|
+
dev_server_uri = URI("#{config.protocol}://#{config.host_with_port}#{path}")
|
|
19
|
+
response = Net::HTTP.get_response(dev_server_uri)
|
|
20
|
+
raise "Failed to load inline SVG from #{dev_server_uri}" unless response.is_a?(Net::HTTPSuccess)
|
|
21
|
+
|
|
22
|
+
response.body
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require "capybara/rails"
|
|
2
|
+
require "capybara/rspec"
|
|
3
|
+
|
|
4
|
+
Capybara.default_max_wait_time = 2
|
|
5
|
+
Capybara.disable_animation = true
|
|
6
|
+
|
|
7
|
+
RSpec.configure do |config|
|
|
8
|
+
config.before(:each, type: :system) do
|
|
9
|
+
driven_by :selenium,
|
|
10
|
+
using: (ENV["SHOW_BROWSER"] ? :chrome : :headless_chrome),
|
|
11
|
+
screen_size: [1400, 1400] do |options|
|
|
12
|
+
# Allows running in Docker
|
|
13
|
+
options.add_argument("--disable-dev-shm-usage")
|
|
14
|
+
options.add_argument("--no-sandbox")
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
require "capybara/rails"
|
|
3
|
+
|
|
4
|
+
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
|
|
5
|
+
driven_by :selenium,
|
|
6
|
+
using: (ENV["SHOW_BROWSER"] ? :chrome : :headless_chrome),
|
|
7
|
+
screen_size: [1400, 1400] do |options|
|
|
8
|
+
# Allows running in Docker
|
|
9
|
+
options.add_argument("--disable-dev-shm-usage")
|
|
10
|
+
options.add_argument("--no-sandbox")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
setup do
|
|
14
|
+
Capybara.default_max_wait_time = 2
|
|
15
|
+
Capybara.disable_animation = true
|
|
16
|
+
Capybara.server = :puma, {Silent: true}
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
class InlineSvgHelperTest < ActionView::TestCase
|
|
4
|
+
test "inline_svg_tag returns contents of svg file as html_safe string" do
|
|
5
|
+
svg_result = inline_svg_tag("images/example.svg")
|
|
6
|
+
assert_predicate(svg_result, :html_safe?)
|
|
7
|
+
assert_equal(<<~EXPECTED_SVG.strip, svg_result)
|
|
8
|
+
<svg role="img" width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
9
|
+
<rect x="2" y="2" width="176" height="176" rx="6" fill="white" stroke="#CCCCCC" stroke-width="4" stroke-miterlimit="0" stroke-linecap="round"/>
|
|
10
|
+
</svg>
|
|
11
|
+
EXPECTED_SVG
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
test "inline_svg_tag adds a <title> to the svg if specified" do
|
|
15
|
+
svg_result = inline_svg_tag("images/example.svg", title: "rounded box")
|
|
16
|
+
assert_equal(<<~EXPECTED_SVG.strip, svg_result)
|
|
17
|
+
<svg role="img" width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
18
|
+
<title>rounded box</title>
|
|
19
|
+
<rect x="2" y="2" width="176" height="176" rx="6" fill="white" stroke="#CCCCCC" stroke-width="4" stroke-miterlimit="0" stroke-linecap="round"/>
|
|
20
|
+
</svg>
|
|
21
|
+
EXPECTED_SVG
|
|
22
|
+
end
|
|
23
|
+
end
|