Merge branch 'master' of git.workaround.org:chaas/debshots
This commit is contained in:
commit
773548afc3
22 changed files with 303 additions and 75 deletions
128
README.rdoc
128
README.rdoc
|
|
@ -114,23 +114,125 @@ https://www.phusionpassenger.com/library/walkthroughs/deploy/ruby/ownserver/ngin
|
|||
|
||||
Example nginx vhost config:
|
||||
|
||||
##
|
||||
# You should look at the following URL's in order to grasp a solid understanding
|
||||
# of Nginx configuration files in order to fully unleash the power of Nginx.
|
||||
# http://wiki.nginx.org/Pitfalls
|
||||
# http://wiki.nginx.org/QuickStart
|
||||
# http://wiki.nginx.org/Configuration
|
||||
#
|
||||
# Generally, you will want to move this file somewhere, and start with a clean
|
||||
# file but keep this around for reference. Or just disable in sites-enabled.
|
||||
#
|
||||
# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
|
||||
##
|
||||
|
||||
#proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=assets:768m use_temp_path=off;
|
||||
|
||||
#add_header X-Cache-Status $upstream_cache_status;
|
||||
#add_header X-Runtime 42;
|
||||
more_clear_headers 'X-Runtime';
|
||||
more_clear_headers 'X-Powered-By';
|
||||
more_clear_headers 'Server';
|
||||
server_tokens off;
|
||||
|
||||
#passenger_show_version_in_header off;
|
||||
|
||||
#log_format proxy '[$time_local] Cache: $upstream_cache_status $upstream_addr $upstream_response_time $status $bytes_sent $proxy_add_x_forwarded_for $request_uri "$http_referer" "$http_user_agent"';
|
||||
#access_log /var/log/nginx/debshots-access.log proxy;
|
||||
|
||||
# Default server configuration
|
||||
#
|
||||
#passenger_log_level 5;
|
||||
|
||||
server {
|
||||
listen 85.25.83.22:80 default_server;
|
||||
listen 85.25.83.22:80 default_server;
|
||||
#listen 80 default_server;
|
||||
#listen [::]:80 default_server;
|
||||
|
||||
# SSL configuration
|
||||
#
|
||||
# listen 443 ssl default_server;
|
||||
# listen [::]:443 ssl default_server;
|
||||
#
|
||||
# Self signed certs generated by the ssl-cert package
|
||||
# Don't use them in a production server!
|
||||
#
|
||||
# include snippets/snakeoil.conf;
|
||||
|
||||
passenger_enabled on;
|
||||
passenger_ruby /home/debshots/.rbenv/versions/2.3.1/bin/ruby;
|
||||
root /home/debshots/debshots/public;
|
||||
server_name _;
|
||||
#passenger_pass_header X-Accel-Redirect;
|
||||
|
||||
#proxy_cache_methods GET HEAD;
|
||||
|
||||
root /home/debshots/debshots/public;
|
||||
|
||||
# Add index.php to the list if you are using PHP
|
||||
#index index.html index.htm index.nginx-debian.html;
|
||||
|
||||
server_name _;
|
||||
#proxy_cache assets;
|
||||
#add_header X-Cache-Status $upstream_cache_status;
|
||||
#proxy_ignore_headers Cache-Control;
|
||||
|
||||
#add_header X-Debian rocks;
|
||||
|
||||
location /assets/ {
|
||||
alias /home/debshots/debshots/public/assets/;
|
||||
expires 1h;
|
||||
#include debshots_params;
|
||||
add_header X-Coffee assets;
|
||||
add_header Cache-Control public;
|
||||
}
|
||||
location /logo/ {
|
||||
alias /home/debshots/debshots/public/logo/;
|
||||
expires 1h;
|
||||
add_header X-Coffee logo;
|
||||
add_header Cache-Control public;
|
||||
}
|
||||
location /screenshots/ {
|
||||
alias /home/debshots/debshots/public/screenshots/;
|
||||
expires max;
|
||||
add_header X-Coffee screenshots;
|
||||
add_header Cache-Control public;
|
||||
}
|
||||
location /images/ {
|
||||
alias /home/debshots/debshots/public/images/;
|
||||
expires 1h;
|
||||
add_header X-Coffee images;
|
||||
add_header Cache-Control public;
|
||||
}
|
||||
|
||||
# Send thumbnails using X-Sendfile / X-Accel-Redirect
|
||||
# The correct thumbnail is computed by the Rails application so it cannot be served directly.
|
||||
location /thumbnail/ {
|
||||
expires 1d;
|
||||
add_header X-Coffee thumbnail;
|
||||
add_header Cache-Control public;
|
||||
|
||||
passenger_set_header X-Sendfile-Type "X-Accel-Redirect";
|
||||
passenger_env_var HTTP_X_ACCEL_MAPPING /home/debshots/debshots/public/=/__send_file_accel/;
|
||||
passenger_pass_header X-Accel-Redirect;
|
||||
}
|
||||
|
||||
# Send public assets using X-Sendfile / X-Accel-Redirect (e.g. public/images/dummy/...)
|
||||
location /public/ {
|
||||
expires 1h;
|
||||
add_header X-Coffee public;
|
||||
add_header Cache-Control public;
|
||||
|
||||
passenger_set_header X-Sendfile-Type "X-Accel-Redirect";
|
||||
passenger_env_var HTTP_X_ACCEL_MAPPING /home/debshots/debshots/public/=/__send_file_accel/;
|
||||
passenger_pass_header X-Accel-Redirect;
|
||||
}
|
||||
|
||||
location /__send_file_accel/ {
|
||||
internal;
|
||||
alias /home/debshots/debshots/public/;
|
||||
}
|
||||
}
|
||||
|
||||
=== Enable nginx caching
|
||||
|
||||
mkdir /var/cache/nginx
|
||||
|
||||
chown www-data.www-data /var/cache/nginx
|
||||
|
||||
nginx config…
|
||||
|
||||
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m use_temp_path=off;
|
||||
|
||||
== Supported URL paths (aka routes)
|
||||
|
||||
|
|
@ -215,7 +317,7 @@ proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=10g
|
|||
|
||||
/screenshot/:package
|
||||
/screenshot-404/:package (deprecated)
|
||||
* Return a small (320x240 pixel) PNG screenshot file of a package
|
||||
* Return a large (320x240 pixel) PNG screenshot file of a package
|
||||
* If the package does not have any screenshots then return a 404 code
|
||||
|
||||
/screenshot-with-version/:package/:version
|
||||
|
|
|
|||
|
|
@ -953,5 +953,8 @@ a.black
|
|||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.description-verbatim-lines
|
||||
{
|
||||
line-height: 120%;
|
||||
// white-space: pre-line;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
// Place all the styles related to the Moderate controller here.
|
||||
// They will automatically be included in application.css.
|
||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||
21
app/controllers/json_controller.rb
Normal file
21
app/controllers/json_controller.rb
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
class JsonController < ApplicationController
|
||||
# JSON information on a single package
|
||||
def package
|
||||
@p = Package.find_by_name! params[:name]
|
||||
end
|
||||
|
||||
# JSON information on all packages
|
||||
def packages
|
||||
@p = Package.all
|
||||
end
|
||||
|
||||
# JSON information on all screenshots
|
||||
def screenshots
|
||||
@s = Screenshot.includes(:package)
|
||||
end
|
||||
|
||||
# JSON list of packages that do not have screenshots
|
||||
def packages_without_screenshots
|
||||
@p = Package.without_screenshots.all
|
||||
end
|
||||
end
|
||||
|
|
@ -3,15 +3,11 @@ class PackagesController < ApplicationController
|
|||
protect_from_forgery :except => :legacy_uploadfile
|
||||
|
||||
def list
|
||||
@packages = query_packages
|
||||
@packages = @packages.paginate(page: params[:page], per_page: 6)
|
||||
render 'packages/index-list.slim'
|
||||
@packages = query_packages.paginate(page: params[:page], per_page: 6)
|
||||
end
|
||||
|
||||
def grid
|
||||
@packages = query_packages
|
||||
@packages = @packages.paginate(page: params[:page], per_page: 24)
|
||||
render 'packages/index-grid.slim'
|
||||
@packages = query_packages.paginate(page: params[:page], per_page: 24)
|
||||
end
|
||||
|
||||
def details
|
||||
|
|
@ -111,7 +107,14 @@ class PackagesController < ApplicationController
|
|||
thumbnail404
|
||||
return
|
||||
end
|
||||
@screenshot = @package.screenshots.first
|
||||
|
||||
# Called as /thumbnail-with-version/:name/:version
|
||||
if params[:version]
|
||||
@screenshot = @package.best_screenshot_for_version(params[:version])
|
||||
# Called as /thumbnail/:name
|
||||
else
|
||||
@screenshot = @package.screenshots.first
|
||||
end
|
||||
|
||||
# Return a 404 if the package has no screenshots or the image was not found
|
||||
unless @screenshot and @screenshot.image.path
|
||||
|
|
@ -119,11 +122,39 @@ class PackagesController < ApplicationController
|
|||
return
|
||||
end
|
||||
|
||||
# Send the thumbnail
|
||||
# TODO: Make sure it uses X-Sendfile correctly in production
|
||||
# Send the thumbnail (uses X-Sendfile or similar if possible)
|
||||
send_file @screenshot.image.path(:thumb), type: "image/png", disposition: 'inline'
|
||||
end
|
||||
|
||||
# Returns a large screenshot image if posssible.
|
||||
# If the package is not found it returns a dummy image along with status 404.
|
||||
# If the package is found but has no screenshots then it also returns a
|
||||
# dummy image along with status 404.
|
||||
def screenshot
|
||||
@package = Package.find_by(name: params[:name])
|
||||
unless @package
|
||||
screenshot404
|
||||
return
|
||||
end
|
||||
|
||||
# Called as /thumbnail-with-version/:name/:version
|
||||
if params[:version]
|
||||
@screenshot = @package.best_screenshot_for_version(params[:version])
|
||||
# Called as /thumbnail/:name
|
||||
else
|
||||
@screenshot = @package.screenshots.first
|
||||
end
|
||||
|
||||
# Return a 404 if the package has no screenshots or the image was not found
|
||||
unless @screenshot and @screenshot.image.path
|
||||
screenshot404
|
||||
return
|
||||
end
|
||||
|
||||
# Send the thumbnail (uses X-Sendfile or similar if possible)
|
||||
send_file @screenshot.image.path(:large), type: "image/png", disposition: 'inline'
|
||||
end
|
||||
|
||||
# Receives a form with a simple text field 'description' so that users can update
|
||||
# the description of their screenshot.
|
||||
def update_screenshot_description
|
||||
|
|
@ -147,9 +178,15 @@ class PackagesController < ApplicationController
|
|||
private
|
||||
|
||||
# Send a dummy thumbnail reading "No screenshot available. Sorry."
|
||||
# TODO: Make sure it uses X-Sendfile correctly in production
|
||||
def thumbnail404
|
||||
send_file Rails.root.join('public/images/dummy/no-screenshots-available.png'),
|
||||
send_file Rails.root.join('public/images/dummy/thumbnail404.png'),
|
||||
type: "image/png",
|
||||
disposition: 'inline',
|
||||
status: 404
|
||||
end
|
||||
|
||||
def screenshot404
|
||||
send_file Rails.root.join('public/images/dummy/screenshot404.png'),
|
||||
type: "image/png",
|
||||
disposition: 'inline',
|
||||
status: 404
|
||||
|
|
|
|||
|
|
@ -53,6 +53,12 @@ class Package < ActiveRecord::Base
|
|||
end
|
||||
|
||||
# Return the newest screenshot that is not newer than the given version.
|
||||
# This algorithm collects all image
|
||||
# versions of a package and determines the (second) newest version.
|
||||
# E.g. if there are version 1.0 and 2.0 and the user is looking for
|
||||
# a screenshot of version 1.5 then the 1.0 version is returned.
|
||||
# This way the user does not see a screenshot of version 2.0 because
|
||||
# 2.0 might contain features that were not there in version 1.5.
|
||||
def best_screenshot_for_version(version)
|
||||
sorted_screenshots = self.screenshots_sorted_by_version
|
||||
sorted_screenshots.each do |ss|
|
||||
|
|
@ -65,6 +71,8 @@ class Package < ActiveRecord::Base
|
|||
private
|
||||
|
||||
def version_compare(x,y)
|
||||
x ||= '0'
|
||||
y ||= '0'
|
||||
version_x = DebImporter::Version.new(x)
|
||||
version_y = DebImporter::Version.new(y)
|
||||
if version_x<version_y
|
||||
|
|
|
|||
7
app/views/json/package.json.jbuilder
Normal file
7
app/views/json/package.json.jbuilder
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
json.package @p.name
|
||||
|
||||
json.screenshots @p.screenshots do |s|
|
||||
json.small_image_url "#{request.protocol}#{request.host_with_port}#{s.image.url(:thumb, timestamp: false)}"
|
||||
json.large_image_url "#{request.protocol}#{request.host_with_port}#{s.image.url(:large, timestamp: false)}"
|
||||
json.version s.version
|
||||
end
|
||||
6
app/views/json/packages.json.jbuilder
Normal file
6
app/views/json/packages.json.jbuilder
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
json.cache! ['json_packages'], expires_in: 1.hour do
|
||||
json.packages @p do |p|
|
||||
json.extract! p, :maintainer, :name, :section, :maintainer_email, :homepage, :description
|
||||
json.url package_url(p.name)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
json.cache! ['json_packages'], expires_in: 1.hour do
|
||||
json.packages @p do |p|
|
||||
json.extract! p, :name
|
||||
end
|
||||
end
|
||||
10
app/views/json/screenshots.json.jbuilder
Normal file
10
app/views/json/screenshots.json.jbuilder
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
json.cache! ['json_screenshots'], expires_in: 1.hour do
|
||||
json.screenshots @s do |s|
|
||||
p = s.package
|
||||
json.extract! s, :version
|
||||
json.extract! p, :maintainer, :name, :section, :maintainer_email, :homepage, :description
|
||||
json.url package_url(p.name)
|
||||
json.small_image_url "#{request.protocol}#{request.host_with_port}#{s.image.url(:thumb, timestamp: false)}"
|
||||
json.large_image_url "#{request.protocol}#{request.host_with_port}#{s.image.url(:large, timestamp: false)}"
|
||||
end
|
||||
end
|
||||
19
app/views/packages/_long_description.slim
Normal file
19
app/views/packages/_long_description.slim
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/ Converts long descriptions from Debian control files into
|
||||
/ proper HTML.
|
||||
/ See: https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Description
|
||||
|
||||
- text.lines.each do |line|
|
||||
/ pre +++#{line}---
|
||||
- case line.rstrip
|
||||
- when '.'
|
||||
/ Empty line
|
||||
/ TODO: Technically wrong because this creates an empty paragraph:
|
||||
p
|
||||
- when /^(\s+)(.+)/
|
||||
/ Verbatim text
|
||||
div.description-verbatim-lines
|
||||
= (' ' * $1.to_s.length).html_safe
|
||||
= $2
|
||||
- else
|
||||
/ Normal text
|
||||
= line
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
'Manage your screenshot
|
||||
.dropdown-pane data-dropdown=true id="admin-info-#{screenshot.id}"
|
||||
a.button.small.alert[
|
||||
href=delete_screenshot_path(@package.name, screenshot.id)
|
||||
href=delete_screenshot_path(screenshot.id)
|
||||
onclick="return confirm('Really delete the screenshot?');"
|
||||
] Delete screenshot
|
||||
p Write a description for this screenshot:
|
||||
|
|
|
|||
|
|
@ -13,14 +13,14 @@
|
|||
.text-center
|
||||
a.black.fancybox href=screenshot.image.url(:large, timestamp: false) rel='fancybox-thumb' title=screenshot.caption
|
||||
= image_tag(screenshot.image.url(:large, timestamp: false), alt: screenshot.caption)
|
||||
- if screenshot.description
|
||||
.imgcaption =screenshot.description
|
||||
- if screenshot.version.present?
|
||||
.imgcaption
|
||||
em '(from version #{screenshot.version})
|
||||
- if user_signed_in?
|
||||
.imgcaption
|
||||
em =screenshot.adminstatus
|
||||
div
|
||||
- if screenshot.description
|
||||
.imgcaption =screenshot.description
|
||||
- if screenshot.version.present?
|
||||
em.imgcaption
|
||||
'(from version #{screenshot.version})
|
||||
- if user_signed_in?
|
||||
em.imgcaption Admin status: #{screenshot.adminstatus}
|
||||
|
||||
// Is the user an admin?
|
||||
- if user_signed_in?
|
||||
|
|
@ -44,7 +44,8 @@
|
|||
p.subtitle Description
|
||||
// TODO: split long_description by <p> tags
|
||||
- if @package.long_description.present?
|
||||
p =@package.long_description
|
||||
= render(partial: 'long_description', locals: {text: @package.long_description})
|
||||
/ p =@package.long_description
|
||||
- else
|
||||
p Sorry - no more information available.
|
||||
- if @package.homepage.present?
|
||||
|
|
|
|||
|
|
@ -9,9 +9,13 @@ Debshots::Application.configure do
|
|||
# Do not eager load code on boot.
|
||||
config.eager_load = false
|
||||
|
||||
# Show full error reports and disable caching.
|
||||
# Show full error reports
|
||||
config.consider_all_requests_local = true
|
||||
|
||||
# Disable caching (except for working on caching)
|
||||
config.action_controller.perform_caching = false
|
||||
# config.action_controller.perform_caching = true
|
||||
# config.cache_store = :file_store, "#{Rails.root.to_s}/tmp/cache/filestore"
|
||||
|
||||
# Don't care if the mailer can't send.
|
||||
config.action_mailer.raise_delivery_errors = false
|
||||
|
|
|
|||
|
|
@ -13,9 +13,12 @@ Debshots::Application.configure do
|
|||
# ActiveRecord errors propagate normally (forward deprecation to Rails 5)
|
||||
config.active_record.raise_in_transactional_callbacks = true
|
||||
|
||||
# Full error reports are disabled and caching is turned on.
|
||||
# Full error reports are disabled
|
||||
config.consider_all_requests_local = false
|
||||
|
||||
# Caching turned on
|
||||
config.action_controller.perform_caching = true
|
||||
config.cache_store = :file_store, "#{Rails.root.to_s}/tmp/cache/filestore"
|
||||
|
||||
# Enable Rack::Cache to put a simple HTTP cache in front of your application
|
||||
# Add `rack-cache` to your Gemfile before enabling this.
|
||||
|
|
|
|||
|
|
@ -28,6 +28,18 @@ Debshots::Application.routes.draw do
|
|||
get 'approve_screenshot/:id' => 'packages#approve_screenshot', as: :approve_screenshot
|
||||
get 'about' => 'welcome#about'
|
||||
get 'thumbnail/:name' => 'packages#thumbnail', as: :thumbnail_image, name: /[^\/]+/
|
||||
get 'thumbnail-with-version/:name/:version' => 'packages#thumbnail', name: /[^\/]+/, version: /\S+/
|
||||
get 'screenshot/:name' => 'packages#screenshot', as: :screenshot_image, name: /[^\/]+/
|
||||
get 'screenshot-with-version/:name/:version' => 'packages#screenshot', name: /[^\/]+/, version: /\S+/
|
||||
|
||||
# Legacy URLs
|
||||
get 'with_screenshots', to: redirect('/packages?show=with')
|
||||
get 'without_screenshots', to: redirect('/packages?show=without')
|
||||
|
||||
get 'json/package/:name' => 'json#package', as: :json_package, defaults: { format: :json }
|
||||
get 'json/packages' => 'json#packages', as: :json_packages, defaults: { format: :json }
|
||||
get 'json/screenshots' => 'json#screenshots', as: :json_screenshots, defaults: { format: :json }
|
||||
get 'json/packages-without-screenshots' => 'json#packages_without_screenshots', defaults: { format: :json }
|
||||
|
||||
# The priority is based upon order of creation: first created -> highest priority.
|
||||
# See how all your routes lay out with "rake routes".
|
||||
|
|
|
|||
|
|
@ -101,14 +101,14 @@ module DebImporter
|
|||
name=value=''
|
||||
data.each_line do |line|
|
||||
case line
|
||||
when /^(.+?): (.+)/ # "Key: Value"
|
||||
when /^(\S+?): (.+)/ # "Key: Value"
|
||||
fields[name.to_sym]=value unless value.empty?
|
||||
name,value=$1,$2
|
||||
when /^(.+?):$/ # "Key:" (start of multi-line entry without value in line)
|
||||
when /^(\S+?):$/ # "Key:" (start of multi-line entry without value in line)
|
||||
fields[name.to_sym]=value unless value.empty?
|
||||
name=$1
|
||||
value=''
|
||||
when /^\s+(.+)/ # " Indented multi-line value"
|
||||
when /^\s(.+)/ # " Indented multi-line value"
|
||||
# Add a newline for multi-line entries ("Key: Value\n Foo\n Bar")
|
||||
unless value.empty?
|
||||
value << "\n"
|
||||
|
|
@ -188,36 +188,5 @@ module DebImporter
|
|||
def dpkg_compare_version(v1, v2, op)
|
||||
system("dpkg --compare-versions #{v1} #{op} #{v2}")
|
||||
end
|
||||
|
||||
# def ___version_compare(x,y)
|
||||
# # Compare a version string (like the upstream_version or
|
||||
# # debian_revision string) against another version string.
|
||||
# # The algorithm works like this:
|
||||
# #
|
||||
# # The strings are compared from left to right.
|
||||
# # First the initial part of each string consisting entirely of non-digit
|
||||
# # characters is determined. These two parts (one of which may be empty)
|
||||
# # are compared lexically. If a difference is found it is returned.
|
||||
# # The lexical comparison is a comparison of ASCII values modified so
|
||||
# # that all the letters sort earlier than all the non-letters and so that
|
||||
# # a tilde sorts before anything, even the end of a part.
|
||||
# # For example, the following parts are in sorted order from
|
||||
# # earliest to latest: ~~, ~~a, ~, the empty part, a.[37]
|
||||
# #
|
||||
# # Then the initial part of the remainder of each string which
|
||||
# # consists entirely of digit characters is determined. The
|
||||
# # numerical values of these two parts are compared, and any
|
||||
# # difference found is returned as the result of the comparison.
|
||||
# # For these purposes an empty string (which can only occur at
|
||||
# # the end of one or both version strings being compared) counts as zero.
|
||||
# #
|
||||
# # These two steps (comparing and removing initial non-digit strings
|
||||
# # and initial digit strings) are repeated until a difference is
|
||||
# # found or both strings are exhausted.
|
||||
|
||||
# (x.chars).zip(y.chars) do |xchar,ychar|
|
||||
# puts xchar, ychar
|
||||
# end
|
||||
# end
|
||||
end # /class
|
||||
end # module
|
||||
|
|
|
|||
BIN
public/images/dummy/screenshot404.png
Normal file
BIN
public/images/dummy/screenshot404.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
24
test/controllers/json_controller_test.rb
Normal file
24
test/controllers/json_controller_test.rb
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
require "test_helper"
|
||||
|
||||
class JsonControllerTest < ActionController::TestCase
|
||||
def test_package
|
||||
get :package
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
def test_packages
|
||||
get :packages
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
def test_screenshots
|
||||
get :screenshots
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
def test_packages-without-screenshots
|
||||
get :packages-without-screenshots
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue