fix(champ): do not reset data on champs without fetch_external_data
This commit is contained in:
parent
6ef969a7d9
commit
702b9dd8b3
8 changed files with 589 additions and 142 deletions
|
@ -1,3 +1,3 @@
|
||||||
= render Dsfr::ComboboxComponent.new form: @form, url: data_sources_data_source_adresse_path, selected: @champ.value, allows_custom_value: true, input_html_options: { name: :value, id: @champ.input_id, class: 'fr-select', describedby: @champ.describedby_id } do
|
= render Dsfr::ComboboxComponent.new form: @form, url: data_sources_data_source_adresse_path, selected: @champ.value, allows_custom_value: true, input_html_options: { name: :value, id: @champ.input_id, class: 'fr-select', describedby: @champ.describedby_id } do
|
||||||
= @form.hidden_field :external_id, data: { value_slot: 'value' }
|
= @form.hidden_field :external_id, data: { value_slot: 'value' }
|
||||||
= @form.hidden_field :feature, data: { value_slot: 'data' }
|
= @form.hidden_field :feature, value: '', data: { value_slot: 'data' }
|
||||||
|
|
|
@ -44,6 +44,8 @@ export class ComboboxUI implements EventListenerObject {
|
||||||
#allowsCustomValue: boolean;
|
#allowsCustomValue: boolean;
|
||||||
#limit?: number;
|
#limit?: number;
|
||||||
|
|
||||||
|
#selectedData: Option['data'] = null;
|
||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
input,
|
input,
|
||||||
selectedValueInput,
|
selectedValueInput,
|
||||||
|
@ -293,7 +295,8 @@ export class ComboboxUI implements EventListenerObject {
|
||||||
private dispatchChange(cb: () => Option['data']): void {
|
private dispatchChange(cb: () => Option['data']): void {
|
||||||
const value = this.#selectedValueInput.value;
|
const value = this.#selectedValueInput.value;
|
||||||
const data = cb();
|
const data = cb();
|
||||||
if (value != this.#selectedValueInput.value) {
|
if (value != this.#selectedValueInput.value || data != this.#selectedData) {
|
||||||
|
this.#selectedData = data;
|
||||||
for (const input of this.#valueSlots) {
|
for (const input of this.#valueSlots) {
|
||||||
switch (input.dataset.valueSlot) {
|
switch (input.dataset.valueSlot) {
|
||||||
case 'value':
|
case 'value':
|
||||||
|
|
|
@ -282,7 +282,7 @@ class Champ < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def cleanup_if_empty
|
def cleanup_if_empty
|
||||||
if persisted? && external_id_changed?
|
if fetch_external_data? && persisted? && external_id_changed?
|
||||||
self.data = nil
|
self.data = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,10 +3,6 @@ class Champs::AddressChamp < Champs::TextChamp
|
||||||
data.present?
|
data.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
def feature
|
|
||||||
''
|
|
||||||
end
|
|
||||||
|
|
||||||
def feature=(value)
|
def feature=(value)
|
||||||
return if value.blank?
|
return if value.blank?
|
||||||
feature = JSON.parse(value)
|
feature = JSON.parse(value)
|
||||||
|
|
557
spec/fixtures/cassettes/The_user/fill_a_dossier.yml
vendored
Normal file
557
spec/fixtures/cassettes/The_user/fill_a_dossier.yml
vendored
Normal file
File diff suppressed because one or more lines are too long
113
spec/fixtures/cassettes/communes.yml
vendored
113
spec/fixtures/cassettes/communes.yml
vendored
|
@ -1,113 +0,0 @@
|
||||||
---
|
|
||||||
http_interactions:
|
|
||||||
- request:
|
|
||||||
method: get
|
|
||||||
uri: https://geo.api.gouv.fr/communes?boost=population&codePostal=60&limit=20&type=commune-actuelle,arrondissement-municipal
|
|
||||||
body:
|
|
||||||
encoding: US-ASCII
|
|
||||||
string: ''
|
|
||||||
headers:
|
|
||||||
User-Agent:
|
|
||||||
- demarches-simplifiees.fr
|
|
||||||
Expect:
|
|
||||||
- ''
|
|
||||||
response:
|
|
||||||
status:
|
|
||||||
code: 200
|
|
||||||
message: ''
|
|
||||||
headers:
|
|
||||||
Server:
|
|
||||||
- nginx/1.10.3 (Ubuntu)
|
|
||||||
Date:
|
|
||||||
- Tue, 13 Feb 2024 11:25:44 GMT
|
|
||||||
Content-Type:
|
|
||||||
- application/json; charset=utf-8
|
|
||||||
Content-Length:
|
|
||||||
- '2'
|
|
||||||
X-Powered-By:
|
|
||||||
- Express
|
|
||||||
Vary:
|
|
||||||
- Origin
|
|
||||||
Etag:
|
|
||||||
- W/"2-l9Fw4VUO7kr8CvBlt4zaMCqXZ0w"
|
|
||||||
Strict-Transport-Security:
|
|
||||||
- max-age=15552000
|
|
||||||
body:
|
|
||||||
encoding: ASCII-8BIT
|
|
||||||
string: "[]"
|
|
||||||
recorded_at: Tue, 13 Feb 2024 11:25:44 GMT
|
|
||||||
- request:
|
|
||||||
method: get
|
|
||||||
uri: https://geo.api.gouv.fr/communes?boost=population&codePostal=6040&limit=20&type=commune-actuelle,arrondissement-municipal
|
|
||||||
body:
|
|
||||||
encoding: US-ASCII
|
|
||||||
string: ''
|
|
||||||
headers:
|
|
||||||
User-Agent:
|
|
||||||
- demarches-simplifiees.fr
|
|
||||||
Expect:
|
|
||||||
- ''
|
|
||||||
response:
|
|
||||||
status:
|
|
||||||
code: 200
|
|
||||||
message: ''
|
|
||||||
headers:
|
|
||||||
Server:
|
|
||||||
- nginx/1.10.3 (Ubuntu)
|
|
||||||
Date:
|
|
||||||
- Tue, 13 Feb 2024 11:25:44 GMT
|
|
||||||
Content-Type:
|
|
||||||
- application/json; charset=utf-8
|
|
||||||
Content-Length:
|
|
||||||
- '2'
|
|
||||||
X-Powered-By:
|
|
||||||
- Express
|
|
||||||
Vary:
|
|
||||||
- Origin
|
|
||||||
Etag:
|
|
||||||
- W/"2-l9Fw4VUO7kr8CvBlt4zaMCqXZ0w"
|
|
||||||
Strict-Transport-Security:
|
|
||||||
- max-age=15552000
|
|
||||||
body:
|
|
||||||
encoding: ASCII-8BIT
|
|
||||||
string: "[]"
|
|
||||||
recorded_at: Tue, 13 Feb 2024 11:25:44 GMT
|
|
||||||
- request:
|
|
||||||
method: get
|
|
||||||
uri: https://geo.api.gouv.fr/communes?boost=population&codePostal=60400&limit=20&type=commune-actuelle,arrondissement-municipal
|
|
||||||
body:
|
|
||||||
encoding: US-ASCII
|
|
||||||
string: ''
|
|
||||||
headers:
|
|
||||||
User-Agent:
|
|
||||||
- demarches-simplifiees.fr
|
|
||||||
Expect:
|
|
||||||
- ''
|
|
||||||
response:
|
|
||||||
status:
|
|
||||||
code: 200
|
|
||||||
message: ''
|
|
||||||
headers:
|
|
||||||
Server:
|
|
||||||
- nginx/1.10.3 (Ubuntu)
|
|
||||||
Date:
|
|
||||||
- Tue, 13 Feb 2024 11:25:44 GMT
|
|
||||||
Content-Type:
|
|
||||||
- application/json; charset=utf-8
|
|
||||||
Content-Length:
|
|
||||||
- '3234'
|
|
||||||
Vary:
|
|
||||||
- Accept-Encoding
|
|
||||||
- Origin
|
|
||||||
X-Powered-By:
|
|
||||||
- Express
|
|
||||||
Etag:
|
|
||||||
- W/"ca2-8NmtTi6sTg1Smpmu0voZ0sZ2g4c"
|
|
||||||
Strict-Transport-Security:
|
|
||||||
- max-age=15552000
|
|
||||||
body:
|
|
||||||
encoding: ASCII-8BIT
|
|
||||||
string: !binary |-
|
|
||||||
W3sibm9tIjoiQXBwaWxseSIsImNvZGUiOiI2MDAyMSIsImNvZGVEZXBhcnRlbWVudCI6IjYwIiwic2lyZW4iOiIyMTYwMDAyMTYiLCJjb2RlRXBjaSI6IjI0NjAwMDc1NiIsImNvZGVSZWdpb24iOiIzMiIsImNvZGVzUG9zdGF1eCI6WyI2MDQwMCJdLCJwb3B1bGF0aW9uIjo1Mjl9LHsibm9tIjoiQmFixZN1ZiIsImNvZGUiOiI2MDAzNyIsImNvZGVEZXBhcnRlbWVudCI6IjYwIiwic2lyZW4iOiIyMTYwMDAzNjQiLCJjb2RlRXBjaSI6IjI0NjAwMDc1NiIsImNvZGVSZWdpb24iOiIzMiIsImNvZGVzUG9zdGF1eCI6WyI2MDQwMCJdLCJwb3B1bGF0aW9uIjo1MTB9LHsibm9tIjoiQmVhdXJhaW5zLWzDqHMtTm95b24iLCJjb2RlIjoiNjAwNTUiLCJjb2RlRGVwYXJ0ZW1lbnQiOiI2MCIsInNpcmVuIjoiMjE2MDAwNTQ3IiwiY29kZUVwY2kiOiIyNDYwMDA3NTYiLCJjb2RlUmVnaW9uIjoiMzIiLCJjb2Rlc1Bvc3RhdXgiOlsiNjA0MDAiXSwicG9wdWxhdGlvbiI6MzQwfSx7Im5vbSI6IkLDqWjDqXJpY291cnQiLCJjb2RlIjoiNjAwNTkiLCJjb2RlRGVwYXJ0ZW1lbnQiOiI2MCIsInNpcmVuIjoiMjE2MDAwNTg4IiwiY29kZUVwY2kiOiIyNDYwMDA3NTYiLCJjb2RlUmVnaW9uIjoiMzIiLCJjb2Rlc1Bvc3RhdXgiOlsiNjA0MDAiXSwicG9wdWxhdGlvbiI6MjAyfSx7Im5vbSI6IkJyw6l0aWdueSIsImNvZGUiOiI2MDEwNSIsImNvZGVEZXBhcnRlbWVudCI6IjYwIiwic2lyZW4iOiIyMTYwMDEwNTciLCJjb2RlRXBjaSI6IjI0NjAwMDc1NiIsImNvZGVSZWdpb24iOiIzMiIsImNvZGVzUG9zdGF1eCI6WyI2MDQwMCJdLCJwb3B1bGF0aW9uIjo0Mjd9LHsibm9tIjoiQnVzc3kiLCJjb2RlIjoiNjAxMTciLCJjb2RlRGVwYXJ0ZW1lbnQiOiI2MCIsInNpcmVuIjoiMjE2MDAxMTcyIiwiY29kZUVwY2kiOiIyNDYwMDA3NTYiLCJjb2RlUmVnaW9uIjoiMzIiLCJjb2Rlc1Bvc3RhdXgiOlsiNjA0MDAiXSwicG9wdWxhdGlvbiI6MzEwfSx7Im5vbSI6IkNhaXNuZXMiLCJjb2RlIjoiNjAxMTgiLCJjb2RlRGVwYXJ0ZW1lbnQiOiI2MCIsInNpcmVuIjoiMjE2MDAxMTgwIiwiY29kZUVwY2kiOiIyNDYwMDA3NTYiLCJjb2RlUmVnaW9uIjoiMzIiLCJjb2Rlc1Bvc3RhdXgiOlsiNjA0MDAiXSwicG9wdWxhdGlvbiI6NTA3fSx7Im5vbSI6IkNyaXNvbGxlcyIsImNvZGUiOiI2MDE4MSIsImNvZGVEZXBhcnRlbWVudCI6IjYwIiwic2lyZW4iOiIyMTYwMDE4MDAiLCJjb2RlRXBjaSI6IjI0NjAwMDc1NiIsImNvZGVSZWdpb24iOiIzMiIsImNvZGVzUG9zdGF1eCI6WyI2MDQwMCJdLCJwb3B1bGF0aW9uIjo5MDh9LHsibm9tIjoiQ3V0cyIsImNvZGUiOiI2MDE4OSIsImNvZGVEZXBhcnRlbWVudCI6IjYwIiwic2lyZW4iOiIyMTYwMDE4ODMiLCJjb2RlRXBjaSI6IjI0NjAwMDc1NiIsImNvZGVSZWdpb24iOiIzMiIsImNvZGVzUG9zdGF1eCI6WyI2MDQwMCJdLCJwb3B1bGF0aW9uIjo5Njd9LHsibm9tIjoiR2VudnJ5IiwiY29kZSI6IjYwMjcwIiwiY29kZURlcGFydGVtZW50IjoiNjAiLCJzaXJlbiI6IjIxNjAwMjY3NSIsImNvZGVFcGNpIjoiMjQ2MDAwNzU2IiwiY29kZVJlZ2lvbiI6IjMyIiwiY29kZXNQb3N0YXV4IjpbIjYwNDAwIl0sInBvcHVsYXRpb24iOjMzNX0seyJub20iOiJHcmFuZHLDuyIsImNvZGUiOiI2MDI4NyIsImNvZGVEZXBhcnRlbWVudCI6IjYwIiwic2lyZW4iOiIyMTYwMDI4NDAiLCJjb2RlRXBjaSI6IjI0NjAwMDc1NiIsImNvZGVSZWdpb24iOiIzMiIsImNvZGVzUG9zdGF1eCI6WyI2MDQwMCJdLCJwb3B1bGF0aW9uIjozNjB9LHsibm9tIjoiTGFyYnJveWUiLCJjb2RlIjoiNjAzNDgiLCJjb2RlRGVwYXJ0ZW1lbnQiOiI2MCIsInNpcmVuIjoiMjE2MDAzNDY3IiwiY29kZUVwY2kiOiIyNDYwMDA3NTYiLCJjb2RlUmVnaW9uIjoiMzIiLCJjb2Rlc1Bvc3RhdXgiOlsiNjA0MDAiXSwicG9wdWxhdGlvbiI6NTE2fSx7Im5vbSI6Ik1vbmRlc2NvdXJ0IiwiY29kZSI6IjYwNDEwIiwiY29kZURlcGFydGVtZW50IjoiNjAiLCJzaXJlbiI6IjIxNjAwNDA2OSIsImNvZGVFcGNpIjoiMjQ2MDAwNzU2IiwiY29kZVJlZ2lvbiI6IjMyIiwiY29kZXNQb3N0YXV4IjpbIjYwNDAwIl0sInBvcHVsYXRpb24iOjI0OX0seyJub20iOiJNb3JsaW5jb3VydCIsImNvZGUiOiI2MDQzMSIsImNvZGVEZXBhcnRlbWVudCI6IjYwIiwic2lyZW4iOiIyMTYwMDQyNjciLCJjb2RlRXBjaSI6IjI0NjAwMDc1NiIsImNvZGVSZWdpb24iOiIzMiIsImNvZGVzUG9zdGF1eCI6WyI2MDQwMCJdLCJwb3B1bGF0aW9uIjo1Mzh9LHsibm9tIjoiTmFtcGNlbCIsImNvZGUiOiI2MDQ0NSIsImNvZGVEZXBhcnRlbWVudCI6IjYwIiwic2lyZW4iOiIyMTYwMDQ0MDgiLCJjb2RlRXBjaSI6IjI0NjAwMDc0OSIsImNvZGVSZWdpb24iOiIzMiIsImNvZGVzUG9zdGF1eCI6WyI2MDQwMCJdLCJwb3B1bGF0aW9uIjozMTB9LHsibm9tIjoiTm95b24iLCJjb2RlIjoiNjA0NzEiLCJjb2RlRGVwYXJ0ZW1lbnQiOiI2MCIsInNpcmVuIjoiMjE2MDA0NjU1IiwiY29kZUVwY2kiOiIyNDYwMDA3NTYiLCJjb2RlUmVnaW9uIjoiMzIiLCJjb2Rlc1Bvc3RhdXgiOlsiNjA0MDAiXSwicG9wdWxhdGlvbiI6MTMxOTd9LHsibm9tIjoiUGFzc2VsIiwiY29kZSI6IjYwNDg4IiwiY29kZURlcGFydGVtZW50IjoiNjAiLCJzaXJlbiI6IjIxNjAwNDgyMCIsImNvZGVFcGNpIjoiMjQ2MDAwNzU2IiwiY29kZVJlZ2lvbiI6IjMyIiwiY29kZXNQb3N0YXV4IjpbIjYwNDAwIl0sInBvcHVsYXRpb24iOjI3Mn0seyJub20iOiJQb250LWwnw4l2w6pxdWUiLCJjb2RlIjoiNjA1MDYiLCJjb2RlRGVwYXJ0ZW1lbnQiOiI2MCIsInNpcmVuIjoiMjE2MDA1MDA5IiwiY29kZUVwY2kiOiIyNDYwMDA3NTYiLCJjb2RlUmVnaW9uIjoiMzIiLCJjb2Rlc1Bvc3RhdXgiOlsiNjA0MDAiXSwicG9wdWxhdGlvbiI6Njc5fSx7Im5vbSI6IlBvbnRvaXNlLWzDqHMtTm95b24iLCJjb2RlIjoiNjA1MDciLCJjb2RlRGVwYXJ0ZW1lbnQiOiI2MCIsInNpcmVuIjoiMjE2MDA1MDE3IiwiY29kZUVwY2kiOiIyNDYwMDA3NTYiLCJjb2RlUmVnaW9uIjoiMzIiLCJjb2Rlc1Bvc3RhdXgiOlsiNjA0MDAiXSwicG9wdWxhdGlvbiI6NDUzfSx7Im5vbSI6IlBvcnF1w6lyaWNvdXJ0IiwiY29kZSI6IjYwNTExIiwiY29kZURlcGFydGVtZW50IjoiNjAiLCJzaXJlbiI6IjIxNjAwNTA1OCIsImNvZGVFcGNpIjoiMjQ2MDAwNzU2IiwiY29kZVJlZ2lvbiI6IjMyIiwiY29kZXNQb3N0YXV4IjpbIjYwNDAwIl0sInBvcHVsYXRpb24iOjQwMX1d
|
|
||||||
recorded_at: Tue, 13 Feb 2024 11:25:44 GMT
|
|
||||||
recorded_with: VCR 6.1.0
|
|
|
@ -551,7 +551,7 @@ describe Champ do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "fetch_external_data" do
|
describe "fetch_external_data" do
|
||||||
let(:champ) { create(:champ_text, data: 'some data') }
|
let(:champ) { create(:champ_rnf, data: 'some data') }
|
||||||
|
|
||||||
context "cleanup_if_empty" do
|
context "cleanup_if_empty" do
|
||||||
it "remove data if external_id changes" do
|
it "remove data if external_id changes" do
|
||||||
|
@ -565,8 +565,7 @@ describe Champ do
|
||||||
let(:data) { 'some other data' }
|
let(:data) { 'some other data' }
|
||||||
|
|
||||||
it "fill data from external source" do
|
it "fill data from external source" do
|
||||||
expect(champ).to receive(:fetch_external_data?) { true }
|
expect_any_instance_of(Champs::RNFChamp).to receive(:fetch_external_data) { data }
|
||||||
expect_any_instance_of(Champs::TextChamp).to receive(:fetch_external_data) { data }
|
|
||||||
|
|
||||||
perform_enqueued_jobs do
|
perform_enqueued_jobs do
|
||||||
champ.update(external_id: 'external_id')
|
champ.update(external_id: 'external_id')
|
||||||
|
@ -574,25 +573,25 @@ describe Champ do
|
||||||
expect(champ.reload.data).to eq data
|
expect(champ.reload.data).to eq data
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context "#input_name" do
|
describe "#input_name" do
|
||||||
let(:champ) { create(:champ_text) }
|
let(:champ) { create(:champ_text) }
|
||||||
|
it { expect(champ.input_name).to eq "dossier[champs_public_attributes][#{champ.id}]" }
|
||||||
|
|
||||||
|
context "when private" do
|
||||||
|
let(:champ) { create(:champ_text, private: true) }
|
||||||
|
it { expect(champ.input_name).to eq "dossier[champs_private_attributes][#{champ.id}]" }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when has parent" do
|
||||||
|
let(:champ) { create(:champ_text, parent: create(:champ_text)) }
|
||||||
it { expect(champ.input_name).to eq "dossier[champs_public_attributes][#{champ.id}]" }
|
it { expect(champ.input_name).to eq "dossier[champs_public_attributes][#{champ.id}]" }
|
||||||
|
end
|
||||||
|
|
||||||
context "when private" do
|
context "when has private parent" do
|
||||||
let(:champ) { create(:champ_text, private: true) }
|
let(:champ) { create(:champ_text, private: true, parent: create(:champ_text, private: true)) }
|
||||||
it { expect(champ.input_name).to eq "dossier[champs_private_attributes][#{champ.id}]" }
|
it { expect(champ.input_name).to eq "dossier[champs_private_attributes][#{champ.id}]" }
|
||||||
end
|
|
||||||
|
|
||||||
context "when has parent" do
|
|
||||||
let(:champ) { create(:champ_text, parent: create(:champ_text)) }
|
|
||||||
it { expect(champ.input_name).to eq "dossier[champs_public_attributes][#{champ.id}]" }
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when has private parent" do
|
|
||||||
let(:champ) { create(:champ_text, private: true, parent: create(:champ_text, private: true)) }
|
|
||||||
it { expect(champ.input_name).to eq "dossier[champs_private_attributes][#{champ.id}]" }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ describe 'The user' do
|
||||||
let(:user_dossier) { user.dossiers.first }
|
let(:user_dossier) { user.dossiers.first }
|
||||||
let!(:dossier_to_link) { create(:dossier) }
|
let!(:dossier_to_link) { create(:dossier) }
|
||||||
|
|
||||||
scenario 'fill a dossier', js: true, vcr: { cassette_name: 'communes' } do
|
scenario 'fill a dossier', js: true, vcr: true do
|
||||||
log_in(user, procedure)
|
log_in(user, procedure)
|
||||||
|
|
||||||
fill_individual
|
fill_individual
|
||||||
|
@ -36,12 +36,17 @@ describe 'The user' do
|
||||||
select('Australie', from: form_id_for('pays'))
|
select('Australie', from: form_id_for('pays'))
|
||||||
select('Martinique', from: form_id_for('regions'))
|
select('Martinique', from: form_id_for('regions'))
|
||||||
select('02 – Aisne', from: form_id_for('departements'))
|
select('02 – Aisne', from: form_id_for('departements'))
|
||||||
|
|
||||||
fill_in('communes', with: '60400')
|
fill_in('communes', with: '60400')
|
||||||
find('li', text: 'Brétigny (60400)').click
|
find('li', text: 'Brétigny (60400)').click
|
||||||
|
|
||||||
# communes needs more time to be updated
|
|
||||||
wait_until { champ_value_for('communes') == "Brétigny" }
|
wait_until { champ_value_for('communes') == "Brétigny" }
|
||||||
|
|
||||||
|
fill_in('address', with: '78 Rue du Grés 30310 Vergè')
|
||||||
|
find('li', text: '78 Rue du Grés 30310 Vergèze').click
|
||||||
|
wait_until { champ_value_for('address') == '78 Rue du Grés 30310 Vergèze' }
|
||||||
|
expect(champ_for('address').full_address?).to be_truthy
|
||||||
|
expect(champ_for('address').departement_code_and_name).to eq('30 – Gard')
|
||||||
|
|
||||||
fill_in('dossier_link', with: '123')
|
fill_in('dossier_link', with: '123')
|
||||||
find('.editable-champ-piece_justificative input[type=file]').attach_file(Rails.root + 'spec/fixtures/files/file.pdf')
|
find('.editable-champ-piece_justificative input[type=file]').attach_file(Rails.root + 'spec/fixtures/files/file.pdf')
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue