2020-01-28-01 (#4719)

2020-01-28-01
This commit is contained in:
Pierre de La Morinerie 2020-01-28 16:28:31 +01:00 committed by GitHub
commit 3563aa10ab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 344 additions and 133 deletions

View file

@ -13,7 +13,10 @@ defaults: &defaults
bundle_restore_cache: &bundle_restore_cache
restore_cache:
name: Restore Bundler Package Cache
key: bundle-install-v9-{{ arch }}-{{ checksum "Gemfile.lock" }}
keys:
- bundle-install-v9-{{ arch }}-{{ checksum "Gemfile.lock" }}
- bundle-install-v9-{{ arch }}
- bundle-install-v9
bundle_save_cache: &bundle_save_cache
save_cache:
@ -30,19 +33,45 @@ bundle_install: &bundle_install
yarn_restore_cache: &yarn_restore_cache
restore_cache:
name: Restore Yarn Package Cache
key: yarn-install-v1-{{ arch }}-{{ checksum "yarn.lock" }}
keys:
- yarn-install-v3-{{ arch }}-{{ checksum "yarn.lock" }}
- yarn-install-v3-{{ arch }}
- yarn-install-v3
yarn_save_cache: &yarn_save_cache
save_cache:
name: Save Yarn Package Cache
key: yarn-install-v1-{{ arch }}-{{ checksum "yarn.lock" }}
key: yarn-install-v3-{{ arch }}-{{ checksum "yarn.lock" }}
paths:
- ~/.cache/yarn
yarn_install: &yarn_install
run:
name: Install JS Dependencies
command: yarn install --non-interactive || yarn install --non-interactive
command: yarn install --frozen-lockfile --non-interactive || yarn install --frozen-lockfile --non-interactive
webpacker_restore_cache: &webpacker_restore_cache
restore_cache:
name: Restore Webpacker Cache
keys:
- webpacker-v1-{{ .Branch }}-{{ .Revision }}
- webpacker-v1-{{ .Branch }}
- webpacker-v1
webpacker_save_cache: &webpacker_save_cache
save_cache:
name: Save Webpacker Cache
key: webpacker-v1-{{ .Branch }}-{{ .Revision }}
paths:
- public/packs-test
- tmp/cache/webpacker
webpacker_precompile: &webpacker_precompile
run:
environment:
RAILS_ENV: test
name: Precompile Webpack assets
command: bin/webpack
jobs:
build:
@ -53,8 +82,8 @@ jobs:
- *bundle_install
- *bundle_save_cache
- *yarn_restore_cache
- *yarn_save_cache
- *yarn_install
- *yarn_save_cache
test:
<<: *defaults
parallelism: 3
@ -64,16 +93,15 @@ jobs:
- *bundle_install
- *yarn_restore_cache
- *yarn_install
- *webpacker_restore_cache
- *webpacker_precompile
- *webpacker_save_cache
- run:
environment:
DATABASE_URL: "postgres://tps_test@localhost:5432/tps_test"
name: Create DB
name: Create Database
command: bundle exec rake db:create db:schema:load db:migrate RAILS_ENV=test
- run:
environment:
RAILS_ENV: test
name: Precompile Webpack assets
command: bin/webpack
- run:
environment:
DATABASE_URL: "postgres://tps_test@localhost:5432/tps_test"

View file

@ -1,7 +1,7 @@
on:
issue_comment:
types: [created]
name: Rebase automatique
name: Automatic Rebase
jobs:
rebase:
name: Rebase
@ -9,13 +9,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
with:
fetch-depth: 0
- name: Automatic Rebase
uses: cirrus-actions/rebase@master
uses: cirrus-actions/rebase@1.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# https://github.community/t5/GitHub-Actions/Workflow-is-failing-if-no-job-can-be-ran-due-to-condition/m-p/38186#M3250
always_job:
name: Aways run job
name: Always run job
runs-on: ubuntu-latest
steps:
- name: Always run

View file

@ -117,7 +117,7 @@ GEM
railties (>= 3.0)
brakeman (4.3.1)
browser (2.5.3)
builder (3.2.3)
builder (3.2.4)
byebug (10.0.2)
capybara (3.29.0)
addressable
@ -155,7 +155,7 @@ GEM
connection_pool (2.2.2)
crack (0.4.3)
safe_yaml (~> 1.0.0)
crass (1.0.5)
crass (1.0.6)
css_parser (1.6.0)
addressable
curb (0.9.10)
@ -242,7 +242,7 @@ GEM
graphiql-rails (1.7.0)
railties
sprockets-rails
graphql (1.9.15)
graphql (1.9.16)
graphql-batch (0.4.1)
graphql (>= 1.3, < 2)
promise.rb (~> 0.7.2)
@ -305,7 +305,7 @@ GEM
domain_name (~> 0.5)
http_parser.rb (0.6.0)
httpclient (2.8.3)
i18n (1.7.0)
i18n (1.8.2)
concurrent-ruby (~> 1.0)
ipaddress (0.8.3)
jaro_winkler (1.5.2)
@ -350,7 +350,7 @@ GEM
railties (>= 4)
request_store (~> 1.0)
logstash-event (1.2.02)
loofah (2.3.1)
loofah (2.4.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
lumberjack (1.0.13)
@ -369,7 +369,7 @@ GEM
mimemagic (0.3.3)
mini_mime (1.0.2)
mini_portile2 (2.4.0)
minitest (5.13.0)
minitest (5.14.0)
momentjs-rails (2.20.1)
railties (>= 3.1)
multi_json (1.14.1)
@ -379,7 +379,7 @@ GEM
nenv (0.3.0)
netrc (0.11.0)
nio4r (2.5.2)
nokogiri (1.10.5)
nokogiri (1.10.7)
mini_portile2 (~> 2.4.0)
notiffany (0.1.1)
nenv (~> 0.1)
@ -450,7 +450,7 @@ GEM
puma (3.12.2)
pundit (2.0.1)
activesupport (>= 3.0.0)
rack (2.0.8)
rack (2.1.2)
rack-attack (6.0.0)
rack (>= 1.0, < 3)
rack-mini-profiler (1.0.1)
@ -528,27 +528,27 @@ GEM
builder (>= 3.0)
rubyzip (>= 1.0)
rouge (3.9.0)
rspec (3.8.0)
rspec-core (~> 3.8.0)
rspec-expectations (~> 3.8.0)
rspec-mocks (~> 3.8.0)
rspec-core (3.8.0)
rspec-support (~> 3.8.0)
rspec-expectations (3.8.2)
rspec (3.9.0)
rspec-core (~> 3.9.0)
rspec-expectations (~> 3.9.0)
rspec-mocks (~> 3.9.0)
rspec-core (3.9.1)
rspec-support (~> 3.9.1)
rspec-expectations (3.9.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.8.0)
rspec-mocks (3.8.0)
rspec-support (~> 3.9.0)
rspec-mocks (3.9.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.8.0)
rspec-rails (3.8.1)
rspec-support (~> 3.9.0)
rspec-rails (3.9.0)
actionpack (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
rspec-core (~> 3.8.0)
rspec-expectations (~> 3.8.0)
rspec-mocks (~> 3.8.0)
rspec-support (~> 3.8.0)
rspec-support (3.8.0)
rspec-core (~> 3.9.0)
rspec-expectations (~> 3.9.0)
rspec-mocks (~> 3.9.0)
rspec-support (~> 3.9.0)
rspec-support (3.9.2)
rspec_junit_formatter (0.4.1)
rspec-core (>= 2, < 4, != 2.12.0)
rubocop (0.62.0)
@ -614,9 +614,9 @@ GEM
rack (~> 2.0)
rack-protection (= 2.0.5)
tilt (~> 2.0)
skylight (3.1.2)
skylight-core (= 3.1.2)
skylight-core (3.1.2)
skylight (4.2.1)
skylight-core (= 4.2.1)
skylight-core (4.2.1)
activesupport (>= 4.2.0)
smart_listing (1.2.2)
coffee-rails
@ -644,7 +644,7 @@ GEM
httpclient (>= 2.4)
sysexits (1.2.0)
temple (0.8.0)
thor (0.20.3)
thor (1.0.1)
thread_safe (0.3.6)
tilt (2.0.9)
timecop (0.9.1)
@ -654,7 +654,7 @@ GEM
turbolinks-source (5.2.0)
typhoeus (1.3.1)
ethon (>= 0.9.0)
tzinfo (1.2.5)
tzinfo (1.2.6)
thread_safe (~> 0.1)
unf (0.1.4)
unf_ext

View file

@ -1,6 +1,8 @@
@import "colors";
@import "constants";
$dossier-actions-bar-border-width: 1px;
.dossier-header {
.container {
padding-bottom: $default-padding;
@ -46,29 +48,40 @@
border-radius: 4px;
}
.send-dossier-actions-bar {
// scss-lint:disable VendorPrefix
.dossier-edit-sticky-footer {
// scss-lint:disable VendorPrefix DuplicateProperty
position: fixed; // Fallback for IE 11, and other browser that don't support sticky
position: -webkit-sticky; // This is needed on Safari (tested on 12.1)
// scss-lint:enable VendorPrefix
position: sticky;
// scss-lint:enable VendorPrefix DuplicateProperty
// IE 11 uses `position:fixed` and thus needs an explicit width, content-box for better layout, etc.
width: 100%;
max-width: $page-width + 2 * $default-padding;
box-sizing: content-box;
bottom: 0;
display: flex;
flex-direction: row;
align-items: center;
margin-top: $default-padding;
margin-left: -$default-padding;
margin-right: -$default-padding;
margin-bottom: 0;
padding-top: 0;
padding-bottom: $default-spacer;
padding-right: $default-padding;
padding-left: $default-padding;
padding-right: $default-padding - $dossier-actions-bar-border-width;
padding-left: $default-padding - $dossier-actions-bar-border-width;
background: #FFFFFF;
border: 1px solid #CCCCCC;
border: $dossier-actions-bar-border-width solid #CCCCCC;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
border-bottom: none;
}
.send-dossier-actions-bar {
display: flex;
flex-direction: row;
align-items: center;
.button:not(:small) {
min-height: 38px;
@ -81,13 +94,13 @@
}
// Normal layout
@media (min-width: 500px) {
@media (min-width: 620px) {
padding-top: $default-spacer * 2;
padding-bottom: $default-spacer * 2;
}
// Compact layout
@media (max-width: 500px) {
@media (max-width: 620px) {
padding-top: $default-spacer;
padding-bottom: $default-spacer;
}

View file

@ -1,5 +1,18 @@
module Manager
class UsersController < Manager::ApplicationController
def update
user = User.find(params[:id])
new_email = params[:user][:email]
user.skip_reconfirmation!
user.update(email: new_email)
if (user.valid?)
flash[:notice] = "L'email a été modifié en « #{new_email} » sans notification ni validation par email."
else
flash[:error] = "« #{new_email} » n'est pas une adresse valide."
end
redirect_to edit_manager_user_path(user)
end
def resend_confirmation_instructions
user = User.find(params[:id])
user.resend_confirmation_instructions

View file

@ -41,7 +41,9 @@ class UserDashboard < Administrate::BaseDashboard
# FORM_ATTRIBUTES
# an array of attributes that will be displayed
# on the model's form (`new` and `edit`) pages.
FORM_ATTRIBUTES = [].freeze
FORM_ATTRIBUTES = [
:email
].freeze
# Overwrite this method to customize how users are displayed
# across all pages of the admin dashboard.

View file

@ -59,19 +59,29 @@ class Api::V2::Schema < GraphQL::Schema
raise GraphQL::ExecutionError.new("An object of type #{error.type.graphql_name} was hidden due to permissions", extensions: { code: :unauthorized })
end
middleware(GraphQL::Schema::TimeoutMiddleware.new(max_seconds: 5) do |_, query|
Rails.logger.info("GraphQL Timeout: #{query.query_string}")
end)
use GraphQL::Execution::Interpreter
use GraphQL::Analysis::AST
use GraphQL::Schema::Timeout, max_seconds: 5
use GraphQL::Batch
use GraphQL::Backtrace
if Rails.env.development?
query_analyzer(GraphQL::Analysis::QueryComplexity.new do |_, complexity|
Rails.logger.info("[GraphQL Query Complexity] #{complexity}")
end)
query_analyzer(GraphQL::Analysis::QueryDepth.new do |_, depth|
Rails.logger.info("[GraphQL Query Depth] #{depth}")
end)
end
class LogQueryDepth < GraphQL::Analysis::AST::QueryDepth
def result
Rails.logger.info("[GraphQL Query Depth] #{super}")
end
end
use GraphQL::Batch
use GraphQL::Tracing::SkylightTracing
class LogQueryComplexity < GraphQL::Analysis::AST::QueryComplexity
def result
Rails.logger.info("[GraphQL Query Complexity] #{super}")
end
end
query_analyzer(LogQueryComplexity)
query_analyzer(LogQueryDepth)
else
query_analyzer(GraphQL::Analysis::AST::MaxQueryComplexity)
query_analyzer(GraphQL::Analysis::AST::MaxQueryDepth)
end
end

View file

@ -43,7 +43,9 @@ class Avis < ApplicationRecord
['Question / Introduction', :introduction],
['Réponse', :answer],
['Créé le', :created_at],
['Répondu le', :updated_at]
['Répondu le', :updated_at],
['Instructeur', claimant&.email],
['Expert', instructeur&.email]
]
end

View file

@ -45,8 +45,9 @@ class Champs::LinkedDropDownListChamp < Champ
value.present? ? { primary: primary_value, secondary: secondary_value } : nil
end
def mandatory_and_blank?
mandatory? && (primary_value.blank? || secondary_value.blank?)
def blank?
primary_value.blank? ||
(has_secondary_options_for_primary? && secondary_value.blank?)
end
def search_terms
@ -58,4 +59,8 @@ class Champs::LinkedDropDownListChamp < Champ
def pack_value(primary, secondary)
self.value = JSON.generate([primary, secondary])
end
def has_secondary_options_for_primary?
primary_value.present? && secondary_options[primary_value]&.any?(&:present?)
end
end

View file

@ -216,6 +216,7 @@ class Dossier < ApplicationRecord
validates :user, presence: true
validates :individual, presence: true, if: -> { procedure.for_individual? }
validates :groupe_instructeur, presence: true
def update_search_terms
self.search_terms = [

View file

@ -16,6 +16,7 @@ class Procedure < ApplicationRecord
has_one :attestation_template, dependent: :destroy
belongs_to :parent_procedure, class_name: 'Procedure'
belongs_to :canonical_procedure, class_name: 'Procedure'
belongs_to :service
has_many :administrateurs_procedures
@ -129,9 +130,10 @@ class Procedure < ApplicationRecord
other_procedure = other_procedure_with_path(path)
if other_procedure.present? && administrateur.owns?(other_procedure)
other_procedure.unpublish!
publish!(other_procedure.canonical_procedure || other_procedure)
else
publish!
end
publish!
end
end
@ -615,8 +617,8 @@ class Procedure < ApplicationRecord
update!(closed_at: nil, unpublished_at: nil)
end
def after_publish
update!(published_at: Time.zone.now)
def after_publish(canonical_procedure = nil)
update!(published_at: Time.zone.now, canonical_procedure: canonical_procedure)
end
def after_close

View file

@ -24,9 +24,11 @@ as well as a link to its edit page.
<%= content_for(:title) %>
</h1>
<div>
<%= button_to "modifier", edit_manager_user_path(page.resource), method: :get, class: "button" %>
</div>
<div>
<%= button_to "supprimer", delete_manager_user_path(page.resource), method: :delete, disabled: !page.resource.can_be_deleted?, class: "button", data: { confirm: "Confirmez-vous la suppression de l'utilisateur ?" }, title: page.resource.can_be_deleted? ? "Supprimer" : "Cet utilisateur a des dossiers dont l'instruction a commencé et ne peut être supprimé" %>
</div>
<div>
<% if !user.confirmed? %>

View file

@ -30,11 +30,22 @@
%hr
- if dossier.procedure.routee?
= f.label :groupe_instructeur_id, dossier.procedure.routing_criteria_name
= f.label :groupe_instructeur_id do
= dossier.procedure.routing_criteria_name
%span.mandatory *
-# The routing dropdown has 'include_blank: false', because otherwise a blank
-# value may nullify the groupe_instructeur and thus the link between the dossier
-# and its procedure.
-#
-# If, one day, we need to make clearer to the user that they must actually choose an
-# option, THINK TWICE before adding a blank option, and what would happen if the form is
-# saved when the blank option is selected.
-# Instead please consider other possibilities; like using CSS to gray out the default option,
-# or adding some "(please select an option)" wording aside the label of the default group.
-# CSS
= f.select :groupe_instructeur_id,
dossier.procedure.groupe_instructeurs.order(:label).map { |gi| [gi.label, gi.id] },
{},
required: true
{ include_blank: false }
= f.fields_for :champs, dossier.champs do |champ_form|
- champ = champ_form.object
@ -42,31 +53,32 @@
locals: { champ: champ, form: champ_form }
- if !apercu
.send-dossier-actions-bar
- if dossier.brouillon?
- if autosave_available?(dossier)
= render partial: 'users/dossiers/autosave'
.dossier-edit-sticky-footer
.send-dossier-actions-bar
- if dossier.brouillon?
- if autosave_available?(dossier)
= render partial: 'users/dossiers/autosave'
- else
= f.button 'Enregistrer le brouillon',
formnovalidate: true,
class: 'button send secondary',
data: { 'disable-with': "Envoi en cours…" }
- if dossier.can_transition_to_en_construction?
= f.button 'Déposer le dossier',
name: :submit_draft,
value: true,
class: 'button send primary',
disabled: !current_user.owns?(dossier),
data: { 'disable-with': "Envoi en cours…" }
- else
= f.button 'Enregistrer le brouillon',
formnovalidate: true,
class: 'button send secondary',
data: { 'disable-with': "Envoi en cours…" }
- if dossier.can_transition_to_en_construction?
= f.button 'Déposer le dossier',
name: :submit_draft,
value: true,
= f.button 'Enregistrer les modifications du dossier',
class: 'button send primary',
disabled: !current_user.owns?(dossier),
data: { 'disable-with': "Envoi en cours…" }
- else
= f.button 'Enregistrer les modifications du dossier',
class: 'button send primary',
data: { 'disable-with': "Envoi en cours…" }
- if dossier.brouillon? && !current_user.owns?(dossier)
.send-notice.invite-cannot-submit
En tant quinvité, vous pouvez remplir ce formulaire mais le titulaire du dossier doit le déposer lui-même.
- if dossier.brouillon? && !current_user.owns?(dossier)
.send-notice.invite-cannot-submit
En tant quinvité, vous pouvez remplir ce formulaire mais le titulaire du dossier doit le déposer lui-même.
= render partial: "shared/dossiers/submit_is_over", locals: { dossier: dossier }
= render partial: "shared/dossiers/submit_is_over", locals: { dossier: dossier }

View file

@ -49,5 +49,7 @@ module TPS
debounce_delay: 3000,
status_visible_duration: 6000
}
config.skylight.probes += [:graphql]
end
end

View file

@ -2,7 +2,6 @@
Browser.modern_rules.clear
Browser.modern_rules << -> b { b.chrome? && b.version.to_i >= 50 && !b.platform.ios? }
Browser.modern_rules << -> b { b.edge? && b.version.to_i >= 14 && !b.compatibility_view? }
Browser.modern_rules << -> b { b.ie? && b.version.to_i >= 11 && !b.compatibility_view? }
Browser.modern_rules << -> b { b.firefox? && b.version.to_i >= 50 && !b.platform.ios? }
Browser.modern_rules << -> b { b.opera? && b.version.to_i >= 40 }
Browser.modern_rules << -> b { b.safari? && b.version.to_i >= 8 }

View file

@ -24,7 +24,7 @@ Rails.application.routes.draw do
delete 'delete', on: :member
end
resources :users, only: [:index, :show] do
resources :users, only: [:index, :show, :edit, :update] do
delete 'delete', on: :member
post 'resend_confirmation_instructions', on: :member
put 'enable_feature', on: :member

View file

@ -0,0 +1,5 @@
class AddCanonicalProcedureIdToProcedures < ActiveRecord::Migration[5.2]
def change
add_column :procedures, :canonical_procedure_id, :bigint
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2019_12_18_103727) do
ActiveRecord::Schema.define(version: 2020_01_14_113700) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -502,6 +502,7 @@ ActiveRecord::Schema.define(version: 2019_12_18_103727) do
t.boolean "ods_export_queued"
t.datetime "closed_at"
t.datetime "unpublished_at"
t.bigint "canonical_procedure_id"
t.index ["declarative_with_state"], name: "index_procedures_on_declarative_with_state"
t.index ["hidden_at"], name: "index_procedures_on_hidden_at"
t.index ["parent_procedure_id"], name: "index_procedures_on_parent_procedure_id"

View file

@ -21,6 +21,7 @@ describe ApplicationController, type: :controller do
let(:payload) { {} }
before do
allow(@controller).to receive(:content_type).and_return('')
allow(@controller).to receive(:current_user).and_return(current_user)
expect(@controller).to receive(:current_instructeur).and_return(current_instructeur)
expect(@controller).to receive(:current_administrateur).and_return(current_administrateur)
@ -42,6 +43,8 @@ describe ApplicationController, type: :controller do
payload.delete(key)
end
expect(payload).to eq({
sk_rendered_format: nil,
sk_variant: [],
user_agent: 'Rails Testing',
user_roles: 'Guest'
})
@ -61,6 +64,8 @@ describe ApplicationController, type: :controller do
payload.delete(key)
end
expect(payload).to eq({
sk_rendered_format: nil,
sk_variant: [],
user_agent: 'Rails Testing',
user_id: current_user.id,
user_email: current_user.email,
@ -85,6 +90,8 @@ describe ApplicationController, type: :controller do
payload.delete(key)
end
expect(payload).to eq({
sk_rendered_format: nil,
sk_variant: [],
user_agent: 'Rails Testing',
user_id: current_user.id,
user_email: current_user.email,

View file

@ -4,6 +4,7 @@ describe Manager::ApplicationController, type: :controller do
let(:payload) { {} }
before do
allow(@controller).to receive(:content_type).and_return('')
allow(@controller).to receive(:current_user).and_return(current_user)
@controller.send(:append_info_to_payload, payload)
end
@ -13,6 +14,8 @@ describe Manager::ApplicationController, type: :controller do
payload.delete(key)
end
expect(payload).to eq({
sk_rendered_format: nil,
sk_variant: [],
user_agent: 'Rails Testing',
user_id: current_user.id,
user_email: current_user.email

View file

@ -1,6 +1,36 @@
describe Manager::UsersController, type: :controller do
let(:administration) { create(:administration) }
describe '#update' do
let!(:user) { create(:user, email: 'ancien.email@domaine.fr') }
before {
sign_in administration
}
subject { patch :update, params: { id: user.id, user: { email: nouvel_email } } }
describe 'with a valid email' do
let(:nouvel_email) { 'nouvel.email@domaine.fr' }
it 'updates the user email' do
subject
expect(User.find_by(id: user.id).email).to eq(nouvel_email)
end
end
describe 'with an invalid email' do
let(:nouvel_email) { 'plop' }
it 'does not update the user email' do
subject
expect(User.find_by(id: user.id).email).not_to eq(nouvel_email)
expect(flash[:error]).to match("« #{nouvel_email} » n'est pas une adresse valide.")
end
end
end
describe '#delete' do
let!(:user) { create(:user) }

View file

@ -3,29 +3,29 @@ require 'spec_helper'
feature 'Outdated browsers support:' do
context 'when the user browser is outdated' do
before(:each) do
ie_10_user_agent = 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)'
Capybara.page.driver.header('user-agent', ie_10_user_agent)
ie_11_user_agent = 'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko'
Capybara.page.driver.header('user-agent', ie_11_user_agent)
end
scenario 'a banner is displayed' do
visit new_user_session_path
expect(page).to have_content('Internet Explorer 10 est trop ancien')
expect(page).to have_content('Internet Explorer 11 est trop ancien')
end
scenario 'the banner can be dismissed' do
visit new_user_session_path
expect(page).to have_content('Internet Explorer 10 est trop ancien')
expect(page).to have_content('Internet Explorer 11 est trop ancien')
# The banner is hidden immediately
within '#outdated-browser-banner' do
click_on 'Ignorer'
end
expect(page).not_to have_content('Internet Explorer 10 est trop ancien')
expect(page).not_to have_content('Internet Explorer 11 est trop ancien')
expect(page).to have_current_path(new_user_session_path)
# The banner is hidden after a refresh
page.refresh
expect(page).not_to have_content('Internet Explorer 10 est trop ancien')
expect(page).not_to have_content('Internet Explorer 11 est trop ancien')
end
end
end

View file

@ -91,7 +91,14 @@ describe Champs::LinkedDropDownListChamp do
end
context 'when there is a secondary value' do
before { subject.secondary_value = 'Primary' }
before { subject.secondary_value = 'Secondary' }
it { is_expected.not_to be_mandatory_and_blank }
end
context 'when there is nothing to select for the secondary value' do
let(:drop_down_list) { build(:drop_down_list, value: "--A--\nAbbott\nAbelard\n--B--\n--C--\nCynthia") }
before { subject.primary_value = 'B' }
it { is_expected.not_to be_mandatory_and_blank }
end

View file

@ -1,4 +1,7 @@
require 'spec_helper'
describe Exercice do
describe 'validations' do
it { is_expected.to validate_presence_of(:ca) }
end
end

View file

@ -534,41 +534,101 @@ describe Procedure do
let(:procedure) { create(:procedure, path: 'example-path') }
let(:now) { Time.zone.now.beginning_of_minute }
after { Timecop.return }
context "without parent procedure" do
context 'when publishing a new procedure' do
before do
Timecop.freeze(now)
procedure.publish!
Timecop.freeze(now) do
procedure.publish!
end
end
it do
it 'no reference to the canonical procedure on the published procedure' do
expect(procedure.canonical_procedure).to be_nil
end
it 'changes the procedure state to published' do
expect(procedure.closed_at).to be_nil
expect(procedure.published_at).to eq(now)
expect(Procedure.find_by(path: "example-path")).to eq(procedure)
expect(Procedure.find_by(path: "example-path").administrateurs).to eq(procedure.administrateurs)
end
end
context 'when publishing over a previous canonical procedure' do
let(:canonical_procedure) { create(:procedure, :published) }
before do
Timecop.freeze(now) do
procedure.publish!(canonical_procedure)
end
end
it 'references the canonical procedure on the published procedure' do
expect(procedure.canonical_procedure).to eq(canonical_procedure)
end
it 'changes the procedure state to published' do
expect(procedure.closed_at).to be_nil
expect(procedure.published_at).to eq(now)
end
end
end
describe "#publish_or_reopen!" do
let(:published_procedure) { create(:procedure, :published) }
let(:administrateur) { published_procedure.administrateurs.first }
let(:canonical_procedure) { create(:procedure, :published) }
let(:administrateur) { canonical_procedure.administrateurs.first }
let(:procedure) { create(:procedure, administrateurs: [administrateur]) }
let(:now) { Time.zone.now.beginning_of_minute }
context "without parent procedure" do
context 'when publishing over a previous canonical procedure' do
before do
Timecop.freeze(now)
procedure.path = published_procedure.path
procedure.publish_or_reopen!(administrateur)
procedure.path = canonical_procedure.path
Timecop.freeze(now) do
procedure.publish_or_reopen!(administrateur)
end
canonical_procedure.reload
end
it do
it 'references the canonical procedure on the published procedure' do
expect(procedure.canonical_procedure).to eq(canonical_procedure)
end
it 'changes the procedure state to published' do
expect(procedure.closed_at).to be_nil
expect(procedure.published_at).to eq(now)
end
it 'unpublishes the canonical procedure' do
expect(canonical_procedure.unpublished_at).to eq(now)
end
end
context 'when publishing over a previous procedure with canonical procedure' do
let(:canonical_procedure) { create(:procedure, :closed) }
let(:parent_procedure) { create(:procedure, :published, administrateurs: [administrateur]) }
before do
parent_procedure.update!(path: canonical_procedure.path, canonical_procedure: canonical_procedure)
procedure.path = canonical_procedure.path
Timecop.freeze(now) do
procedure.publish_or_reopen!(administrateur)
end
parent_procedure.reload
end
it 'references the canonical procedure on the published procedure' do
expect(procedure.canonical_procedure).to eq(canonical_procedure)
end
it 'changes the procedure state to published' do
expect(procedure.canonical_procedure).to eq(canonical_procedure)
expect(procedure.closed_at).to be_nil
expect(procedure.published_at).to eq(now)
end
it 'unpublishes parent procedure' do
expect(parent_procedure.unpublished_at).to eq(now)
end
end
end
@ -577,10 +637,10 @@ describe Procedure do
let(:now) { Time.zone.now.beginning_of_minute }
before do
Timecop.freeze(now)
procedure.unpublish!
Timecop.freeze(now) do
procedure.unpublish!
end
end
after { Timecop.return }
it {
expect(procedure.closed_at).to eq(nil)
@ -653,11 +713,11 @@ describe Procedure do
let(:procedure) { create(:procedure, :published) }
let(:now) { Time.zone.now.beginning_of_minute }
before do
Timecop.freeze(now)
procedure.close!
Timecop.freeze(now) do
procedure.close!
end
procedure.reload
end
after { Timecop.return }
it { expect(procedure.close?).to be_truthy }
it { expect(procedure.closed_at).to eq(now) }

View file

@ -295,7 +295,9 @@ describe ProcedureExportService do
"Question / Introduction",
"Réponse",
"Créé le",
"Répondu le"
"Répondu le",
"Instructeur",
"Expert"
])
end