Skip to content

Commit

Permalink
Merge branch 'main' into chore/update-rubocop
Browse files Browse the repository at this point in the history
  • Loading branch information
goulvench authored Feb 26, 2025
2 parents 939e743 + 1937b62 commit 88e4b3b
Show file tree
Hide file tree
Showing 46 changed files with 1,023 additions and 149 deletions.
1 change: 1 addition & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Metrics/BlockLength:
- "config/environments/*.rb"
- "config/routes.rb"
- "db/*.rb"
- "spec/**/*.rb"

# Allow numbers in variable names in specs
Naming/VariableNumber:
Expand Down
2 changes: 1 addition & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.4.1
3.4.2
11 changes: 10 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,19 @@ gem "tzinfo-data", platforms: [:windows, :jruby]
gem "solid_cache"
gem "solid_queue"
gem "solid_cable"
# Add a web interface to control jobs
gem "mission_control-jobs", "~> 1.0"

# Reduces boot times through caching; required in config/boot.rb
gem "bootsnap", require: false

# Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images]
# gem "image_processing", "~> 1.2"

# Monitor errors and performance
gem "sentry-ruby"
gem "sentry-rails"

# Add optimized Active Record association methods
gem "activerecord-has_some_of_many"

Expand Down Expand Up @@ -92,8 +98,11 @@ group :test do
# Simplify testing common Rails functionality
gem "shoulda-matchers"

# Allow stubbing requests during tests
gem "webmock"

# Allow testing accessibility using Axe-core. Only available in JS feature tests
gem "axe-core-capybara", "~> 4.9"
gem "axe-core-rspec", "~> 4.8"
gem "selenium-webdriver", "~> 4.27"
gem "selenium-webdriver", "~> 4.29"
end
32 changes: 30 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ GEM
descendants_tracker (~> 0.0.1)
concurrent-ruby (1.3.5)
connection_pool (2.5.0)
crack (1.0.0)
bigdecimal
rexml
crass (1.0.6)
cucumber (9.2.1)
builder (~> 3.2)
Expand Down Expand Up @@ -238,6 +241,7 @@ GEM
guard (~> 2.1)
guard-compat (~> 1.1)
rspec (>= 2.99.0, < 4.0)
hashdiff (1.1.2)
html-attributes-utils (1.0.2)
activesupport (>= 6.1.4.4)
i18n (1.14.7)
Expand Down Expand Up @@ -274,6 +278,16 @@ GEM
mini_mime (1.1.5)
mini_portile2 (2.8.8)
minitest (5.25.4)
mission_control-jobs (1.0.2)
actioncable (>= 7.1)
actionpack (>= 7.1)
activejob (>= 7.1)
activerecord (>= 7.1)
importmap-rails (>= 1.2.1)
irb (~> 1.13)
railties (>= 7.1)
stimulus-rails
turbo-rails
msgpack (1.8.0)
multi_test (1.1.0)
nenv (0.3.0)
Expand Down Expand Up @@ -475,6 +489,12 @@ GEM
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0)
sentry-rails (5.22.4)
railties (>= 5.0)
sentry-ruby (~> 5.22.4)
sentry-ruby (5.22.4)
bigdecimal
concurrent-ruby (~> 1.0, >= 1.0.2)
shellany (0.0.1)
shoulda-matchers (6.4.0)
activesupport (>= 5.2.0)
Expand Down Expand Up @@ -520,6 +540,10 @@ GEM
axiom-types (~> 0.1)
coercible (~> 1.0)
descendants_tracker (~> 0.0, >= 0.0.3)
webmock (3.25.0)
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
websocket (1.2.11)
websocket-driver (0.7.7)
base64
Expand Down Expand Up @@ -569,6 +593,7 @@ DEPENDENCIES
guard-cucumber
guard-rspec
importmap-rails
mission_control-jobs (~> 1.0)
pagy
pg (~> 1.1)
propshaft
Expand All @@ -584,7 +609,9 @@ DEPENDENCIES
rubocop-rails-omakase
rubocop-rspec
rubocop-rspec_rails
selenium-webdriver (~> 4.27)
selenium-webdriver (~> 4.29)
sentry-rails
sentry-ruby
shoulda-matchers
solid_cable
solid_cache
Expand All @@ -593,10 +620,11 @@ DEPENDENCIES
turbo-rails
tzinfo-data
view_component
webmock
whiny_validation

RUBY VERSION
ruby 3.4.1p0
ruby 3.4.2p28

BUNDLED WITH
2.6.3
32 changes: 28 additions & 4 deletions app/assets/stylesheets/application.scss
Original file line number Diff line number Diff line change
@@ -1,11 +1,35 @@
// Avoid "the notch" on iPhones and Pixel phones
@supports (width: env(safe-area-inset-left)) {
@media only screen and (orientation: landscape) {
body main {
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
}
}
}

// Rend les formulaires button_to "transparents" pour les styles
// Permet d'utiliser des liens et des boutons dans un btn-group
form.button_to {
display: contents;
}

// Centre l'icône quand le badge n'a pas de texte visible
.fr-badge[role=tooltip]::before {
margin-right: -.125rem;
cursor: help;
.fr-badge {
// Ajoute un outline quand il contient un lien caché qui a le focus
&:has(a:focus) {
outline-color: #0a76f6;
outline-style: solid;
outline-width: 2px;
a {
outline: none;
}
}
&[role=tooltip] {
outline-offset: 0;
// Centre l'icône quand le badge n'a pas de texte visible
&::before {
margin-right: -.125rem;
cursor: help;
}
}
}
6 changes: 3 additions & 3 deletions app/controllers/sites_controller.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
class SitesController < ApplicationController
before_action :set_site, except: :index
before_action :set_site, except: [:index, :create]
before_action :redirect_old_slugs, except: [:index, :new, :create], if: :get_request?

# GET /sites
Expand All @@ -19,7 +19,7 @@ def edit; end
# POST /sites
def create
@site = Site.find_or_create_by_url(site_params)
if @site.save
if @site.persisted?
redirect_to @site, notice: t(".notice")
else
render :new, status: :unprocessable_entity
Expand All @@ -44,7 +44,7 @@ def destroy
private

def set_site
@site = params[:id].present? ? Site.friendly.find(params.expect(:id)) : Site.new_with_audit
@site = params[:id].present? ? Site.friendly.find(params.expect(:id)) : Site.new
end

def redirect_old_slugs
Expand Down
14 changes: 10 additions & 4 deletions app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,16 @@ def paginate
render "shared/paginate", pagy: @pagy if @pagy && @pagy.pages > 1
end

def badge(status, text = nil, tooltip: false, &block)
status, text = *status if status.is_a?(Array)
def badge(status, text = nil, link: nil, tooltip: false, &block)
status, text, link = *status if status.is_a?(Array)
text ||= yield(block)
html_attributes = tooltip ? { role: :tooltip, tabindex: 0, title: text } : {}
dsfr_badge(status:, html_attributes:) { tooltip ? tag.span(class: "fr-sr-only") { text } : text }
case
when tooltip && link
dsfr_badge(status:, html_attributes: { role: :tooltip, title: text }) { link_to text, link, class: "fr-sr-only", target: :_blank, rel: :noopenner }
when tooltip
dsfr_badge(status:, html_attributes: { role: :tooltip, tabindex: 0, title: text }) { tag.span(class: "fr-sr-only") { text } }
when link then dsfr_badge(status:) { link_to text, link }
else dsfr_badge(status:) { text }
end
end
end
3 changes: 3 additions & 0 deletions app/javascript/application.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
import "@hotwired/turbo-rails"
import "@hotwired/stimulus"
import "@gouvfr/dsfr"
import "controllers"
import "application"
7 changes: 0 additions & 7 deletions app/javascript/controllers/hello_controller.js

This file was deleted.

9 changes: 5 additions & 4 deletions app/jobs/run_check_job.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
class RunCheckJob < ApplicationJob
def perform
check = params.values_at(:check)
return unless check.runnable?
def perform(check)
return unless check.due?

check.run

UpdateAuditStatusJob.with(check.audit).perform_later
check.audit.update(checked_at: Time.zone.now)

UpdateAuditStatusJob.perform_later(check.audit)
end
end
4 changes: 2 additions & 2 deletions app/jobs/schedule_checks_job.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
class ScheduleChecksJob< ApplicationJob
class ScheduleChecksJob < ApplicationJob
def perform
Check.to_schedule.find_each do |check|
Check.transaction do
RunCheckJob.with(check).set(wait_until: check.run_at).perform_later
RunCheckJob.set(wait_until: check.run_at).perform_later(check)
check.update(scheduled: true)
end
end
Expand Down
3 changes: 1 addition & 2 deletions app/jobs/update_audit_status_job.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
class UpdateAuditStatusJob < ApplicationJob
def perform
audit = params.values_at(:audit)
def perform(audit)
audit.derive_status_from_checks
end
end
100 changes: 100 additions & 0 deletions app/models/analyzers/accessibility_page.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
module Analyzers
class AccessibilityPage
DECLARATION = /Déclaration d('|')accessibilité( RGAA)?/i
ARTICLE = /(?:art(?:icle)?\.? 47|article 47) (?:de la )?loi (?:n[°˚]|num(?:éro)?\.?) ?2005-102 du 11 (?:février|fevrier) 2005/i
DATE_PATTERN = /(?:réalisé(?:e)?(?:\s+le)?|du|en)\s+(?:(\d{1,2})(?:\s+|er\s+)?)?([a-zéû]+)\s+(\d{4})/i
COMPLIANCE_PATTERN = /(?:(?:avec (?:un |une )?)?taux de conformité|conforme à|révèle que).*?(\d+(?:[.,]\d+)?)(?:\s*%| pour cent)/i
STANDARD_PATTERN = /(?:au |des critères )?(?:(RGAA(?:[. ](?:version|v)?[. ]?\d+(?:\.\d+(?:\.\d+)?)?)?|(WCAG)))/i
AUDITOR_PATTERN = /(?:par(?:\s+la)?(?:\s+société)?|par)\s+([^,]+?)(?:,| révèle| sur)/i

attr_reader :page
delegate :url, :title, :text, to: :page

class << self
def analyze(crawler)
new(crawler:).data
end
end

def initialize(crawler: nil, page: nil)
@crawler = crawler
@page = page || (find_page if crawler) # Allow passing a page to simplify testing
end
private_class_method :new

def data = page ? { url:, title:, audit_date:, compliance_rate:, standard:, auditor: } : {}

def audit_date
return unless (match = text.match(DATE_PATTERN))

day = (match[1] || "1").to_i
month = month_names[match[2].downcase]
year = match[3].to_i

Date.new(year, month, day)
rescue Date::Error
nil
end

def compliance_rate
return unless (match = text.match(COMPLIANCE_PATTERN))

rate = match[1].tr(",", ".").to_f
rate % 1 == 0 ? rate.to_i : rate
end

def standard
text.scan(STANDARD_PATTERN).flatten.compact.sort_by(&:length).last
end

def auditor
text.match(AUDITOR_PATTERN)&.[](1)&.strip
end

def month_names
@month_names ||= begin
names = I18n.t("date.month_names")[1..] + I18n.t("date.abbr_month_names")[1..]
names.each_with_object({}) do |name, hash|
next if name.blank?

normalized = name.downcase.tr("éû", "eu")
hash[name.downcase] = names.index(name) % 12 + 1
hash[normalized] = names.index(name) % 12 + 1
end
end
end

# Used by sort_by, most relevant links need to have a negative score to come first in the queue
def likelihood_of(link)
return unless link.is_a?(Link)

[
link.text.match?(DECLARATION),
link.href.match?("(declaration-)?accessibilite"),
link.text.match?(Checks::AccessibilityMention::MENTION_REGEX)
].count(&:itself).then { |n| n.zero? ? 1 : -n + 1 }
end

private

attr_reader :crawler

def find_page
crawler.find do |current_page, queue|
return current_page if accessibility_page?(current_page)

sort_queue_by_likelihood(queue)
end
end

def accessibility_page?(current_page)
current_page.title.match?(DECLARATION) ||
current_page.headings.any?(DECLARATION) ||
current_page.text.match?(ARTICLE)
end

def sort_queue_by_likelihood(queue)
queue.sort_by! { |link| likelihood_of(link) }
end
end
end
4 changes: 2 additions & 2 deletions app/models/audit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class Audit < ApplicationRecord
end

validates :url, presence: true, url: true
normalizes :url, with: ->(url) { URI.parse(url).normalize.to_s }
normalizes :url, with: ->(url) { URI.parse(url.strip).normalize.to_s }

enum :status, [
"pending", # Initial state, no checks started
Expand All @@ -16,7 +16,7 @@ class Audit < ApplicationRecord

scope :sort_by_newest, -> { order(created_at: :desc) }
scope :sort_by_url, -> { order(Arel.sql("REGEXP_REPLACE(audits.url, '^https?://(www\.)?', '') ASC")) }
scope :past, -> { where(status: [:passed, :failed]) }
scope :past, -> { where.not(status: :pending) }

delegate :hostname, :path, to: :parsed_url

Expand Down
Loading

0 comments on commit 88e4b3b

Please sign in to comment.