betagouv-cucumber-steps 0.0.1 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d25b49caf9af571492d08dbcecbd139f9af1f36810670b9b08784df0e78fd771
4
- data.tar.gz: 7fbb8d38d7fa338732b3c61232c67f9aadb3277a8317cc1261019e0c8f78da56
3
+ metadata.gz: ac48905d0b5c9b447895d58d165a7bd8b3f0a71bc6a92df2dcf91cd601f99711
4
+ data.tar.gz: 296aba285ee3e81a8895d18123c7d57d32f60b489aed9fbe128065dd18daad86
5
5
  SHA512:
6
- metadata.gz: 4dd9572c4a3d7656b6c4639bb2d2ab3bdf3fb365b57c23efe2ca82cf0a4e3adaf19ba1f0f717afe1172029b6e3a3a7ed649f56487aa86bf38f33e358288d9591
7
- data.tar.gz: cf1b729b5e9c001674c48d3bf78a05f4e68e7a1b553b688a63c1251415a76f16836ad485494f45ba577c9de98da8c7990b38d66360cfb03d5b376312c0775a05
6
+ metadata.gz: f9980b077f998acf2bb3907e973044af516e9aba615daac45d23dab5ce36368b2fde867b913d5578a6aece172f9d9472c9883ab6b89f4e8601eb8a977fadfa30
7
+ data.tar.gz: 9a985477efe543f91d8add119559c8dd866e88c4809e112e77ae93426ecf61bb9c2a763cd58f9a07fec2311f1ce197f1fb83e720bdc6ed1aac6522bf487c8c01
data/.rubocop.yml CHANGED
@@ -1,3 +1,13 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ require:
4
+ - ./lib/rubocop/cop/cucumber/step_naming
5
+
6
+ plugins:
7
+ - rubocop-rake
8
+ - rubocop-rspec
9
+ - rubocop-capybara
10
+
1
11
  AllCops:
2
12
  TargetRubyVersion: 3.1
3
13
 
@@ -6,3 +16,6 @@ Style/StringLiterals:
6
16
 
7
17
  Style/StringLiteralsInInterpolation:
8
18
  EnforcedStyle: double_quotes
19
+
20
+ Cucumber/StepNaming:
21
+ Enabled: true
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,18 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2025-08-18 21:58:35 UTC using RuboCop version 1.78.0.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 1
10
+ # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
11
+ Metrics/MethodLength:
12
+ Max: 14
13
+
14
+ # Offense count: 1
15
+ # Configuration parameters: AsciiConstants.
16
+ Naming/AsciiIdentifiers:
17
+ Exclude:
18
+ - 'test/support/env.rb'
data/README.md CHANGED
@@ -5,7 +5,19 @@ Cucumber](https://cucumber.io/docs/gherkin/reference#steps)
5
5
  génériques, surcouche française des matchers Capybara qui vous permet
6
6
  de contruire votre propre librairie d'actions pour vos tests Cucumber.
7
7
 
8
- Par exemple :
8
+ Cette gem est fraîchement extraite de plusieurs projets Beta
9
+ ([APLyPro](https://github.com/betagouv/aplypro/blob/main/features/step_definitions/web_steps.rb),
10
+ [Datapass](https://github.com/etalab/data_pass/blob/develop/features/step_definitions/web_steps.rb),
11
+ [Auto-audit](https://github.com/betagouv/auto-audit/blob/main/features/step_definitions/web_steps.rb))
12
+ et s'attend à une utilisation du DSFR.
13
+
14
+ ## Aperçu
15
+
16
+ La librairie propose une [batterie
17
+ d'actions](./lib/betagouv/cucumber/steps.rb), qui vous permet de
18
+ composer vos tests.
19
+
20
+ ### Exemple bête mais pas méchant
9
21
 
10
22
  ```ruby
11
23
  Alors("la page contient {string}") do |content|
@@ -29,6 +41,26 @@ Scénario: la page d'accueil contient le nom du produit
29
41
  Alors la page contient "foobar"
30
42
  ```
31
43
 
44
+ ### Exemple avancé : composition d'une étape à vous (métier)
45
+
46
+ ```ruby
47
+ Quand("je rajoute un utilisateur {string}") do |nom|
48
+ steps %(
49
+ Quand je me rends sur la page d'accueil
50
+ Et que je clique sur "Rajoutez un utilisateur" dans le menu principal
51
+ Et que je remplis "Nom" avec "#{nom}"
52
+ Et que je clique sur "Enregistrer"
53
+ )
54
+ end
55
+ ```
56
+
57
+ ```feature
58
+ Fonctionnalité: Gestion des utilisateurs
59
+ Scénario: Je peux rajouter un utilisateur
60
+ Quand je rajoute un utilisateur "Marie Curie" # et paf
61
+ Alors la page contient "Marie Curie a bien été rajouté(e)"
62
+ ```
63
+
32
64
  ## Installation
33
65
 
34
66
  ```ruby
@@ -49,9 +81,6 @@ steps](./lib/betagouv/cucumber/steps.rb).
49
81
 
50
82
  ## Debug / Contribution
51
83
 
52
- Cette gem est fraîchement extraite de plusieurs projets Beta et
53
- nécessite probablement plus de travail.
54
-
55
84
  Si un step ne fonctionne pas vous pouvez :
56
85
 
57
86
  * utilisez le step `Alors debugger` pour debugger votre page en live
data/Rakefile CHANGED
@@ -2,11 +2,24 @@
2
2
 
3
3
  require "bundler/gem_tasks"
4
4
  require "rspec/core/rake_task"
5
+ require "cucumber/rake/task"
5
6
 
6
7
  RSpec::Core::RakeTask.new(:spec)
7
8
 
9
+ Cucumber::Rake::Task.new(:cucumber) do |task|
10
+ task.cucumber_opts = [
11
+ "--require",
12
+ "test/support",
13
+ "test/features/",
14
+ "--publish-quiet"
15
+ ]
16
+ end
17
+
8
18
  require "rubocop/rake_task"
9
19
 
10
20
  RuboCop::RakeTask.new
11
21
 
22
+ desc "Run all tests"
23
+ task test: %i[spec cucumber]
24
+
12
25
  task default: %i[spec rubocop]
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Cucumber/StepNaming
4
+ Quand("debugger") do
5
+ debugger # rubocop:disable Lint/Debugger
6
+ end
7
+
8
+ Quand("print the page") do
9
+ log page.body
10
+ end
11
+ # rubocop:enable Cucumber/StepNaming
@@ -3,7 +3,7 @@
3
3
  module Betagouv
4
4
  module Cucumber
5
5
  module Steps
6
- VERSION = "0.0.1"
6
+ VERSION = "0.1.0"
7
7
  end
8
8
  end
9
9
  end
@@ -2,176 +2,5 @@
2
2
 
3
3
  require_relative "steps/version"
4
4
 
5
- Quand("je me rends sur la page d'accueil") do
6
- visit "/"
7
- end
8
-
9
- Alors("la page contient {string}") do |content|
10
- expect(page).to have_content(content)
11
- end
12
-
13
- Alors("l'en-tête contient {string}") do |content|
14
- within(".fr-header") do
15
- step(%(la page contient "#{content}"))
16
- end
17
- end
18
-
19
- Quand("print the page") do
20
- log page.body
21
- end
22
-
23
- Quand("je clique sur {string}") do |label|
24
- click_link_or_button label
25
- end
26
-
27
- Quand("je clique sur le premier lien ou bouton {string}") do |label|
28
- click_link_or_button(label, match: :first)
29
- end
30
-
31
- Alors("la page contient un bouton {string}") do |label|
32
- expect(page).to have_button(label)
33
- end
34
-
35
- Alors("la page ne contient pas de bouton {string}") do |title|
36
- expect(page).not_to have_button(title)
37
- end
38
-
39
- Alors("la page contient un bouton {string} désactivé") do |content|
40
- expect(page).to have_button(content, disabled: true)
41
- end
42
-
43
- Alors("la page ne contient pas {string}") do |content|
44
- expect(page).to have_no_content(content)
45
- end
46
-
47
- Alors("la page contient un lien {string}") do |text|
48
- expect(page).to have_link(text)
49
- end
50
-
51
- Alors("le titre de la page contient {string}") do |text|
52
- expect(page.title.gsub(" ", " ")).to include text
53
- end
54
-
55
- Alors("il y a un titre de premier niveau contenant {string}") do |text|
56
- # le titre est soit le premier h1 ou la légende du premier tableau
57
- element = page.first("h1") || page.first("table caption")
58
-
59
- expect(element.text).to include(text)
60
- end
61
-
62
- Alors("la page est titrée {string}") do |text|
63
- steps %(
64
- Alors il y a un titre de premier niveau contenant "#{text}"
65
- Et le titre de la page contient "#{text}"
66
- )
67
- end
68
-
69
- Quand("je remplis {string} avec {string}") do |label, value|
70
- fill_in label, with: value
71
- end
72
-
73
- Quand("je remplis le champ {string} avec {string} dans les champs de {string}") do |label, value, fieldset_legend|
74
- within_fieldset(fieldset_legend) do
75
- fill_in label, with: value
76
- end
77
- end
78
-
79
- Quand("je coche {string}") do |label|
80
- check label
81
- end
82
-
83
- Quand("je décoche {string}") do |label|
84
- uncheck label
85
- end
86
-
87
- Quand('je coche toutes les cases') do
88
- page
89
- .all('input[type="checkbox"]')
90
- .map { |node| node.sibling("label").text }
91
- .each { |label| step(%(je coche "#{label}")) }
92
- end
93
-
94
-
95
- Quand("je clique sur {string} dans la rangée {string}") do |link, row|
96
- within("tr", text: row) do
97
- click_link_or_button(link)
98
- end
99
- end
100
-
101
- Alors("la rangée {string} contient {string}") do |row, content|
102
- within("tr", text: row) do
103
- expect(page).to have_content(content)
104
- end
105
- end
106
-
107
- Quand("je clique sur {string} dans la dernière rangée") do |link|
108
- within(all("tr").last) do
109
- click_link_or_button(link)
110
- end
111
- end
112
-
113
- Quand("je clique sur {string} dans la classe {string}") do |link, title|
114
- within("section", text: title) do
115
- click_link_or_button(link)
116
- end
117
- end
118
-
119
- Quand("je remplis le champ {string} dans la rangée {string} avec {string}") do |locator, row, value|
120
- within("tr", text: row) do
121
- fill_in locator, with: value
122
- end
123
- end
124
-
125
- Quand("je sélectionne {string} pour {string}") do |option, name|
126
- select option, from: name
127
- end
128
-
129
- Quand("je choisis {string}") do |option|
130
- choose option
131
- end
132
-
133
- Quand("je choisis {string} pour {string}") do |option, fieldset|
134
- within("fieldset", text: fieldset) do
135
- choose option
136
- end
137
- end
138
-
139
- Quand("j'attache le fichier {string} pour le champ {string}") do |path, field|
140
- attach_file(field, path)
141
- end
142
-
143
- Alors("je peux voir dans le tableau {string}") do |caption, table|
144
- expect(page).to have_table(caption, with_rows: table.rows)
145
- end
146
-
147
- Alors("je peux voir dans le tableau {string} dans cet ordre :") do |caption, table_data|
148
- within_table(caption) do
149
- table_data.rows.each_with_index do |row, row_number|
150
- row.each_with_index do |cel, cel_number|
151
- expect(find("tr[#{row_number + 1}]/td[#{cel_number + 1}]")).to have_text(cel)
152
- end
153
- end
154
- end
155
- end
156
-
157
- Alors("debugger") do
158
- debugger # rubocop:disable Lint/Debugger
159
- end
160
-
161
- Quand("je rafraîchis la page") do
162
- visit current_path
163
- end
164
-
165
- Alors("le fil d'Ariane affiche {string}") do |path|
166
- components = path.split(" > ")
167
-
168
- breadcrumbs = page.all("nav.fr-breadcrumb li").map(&:text)
169
-
170
- expect(breadcrumbs).to eq components
171
- end
172
-
173
- Quand("je clique sur {string} dans le menu principal") do |item|
174
- within("nav#main-nav") do
175
- click_link_or_button(item)
176
- end
177
- end
5
+ require_relative "debug_steps"
6
+ require_relative "web_steps"
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ Quand("je me rends sur la page d'accueil") do
4
+ visit "/"
5
+ end
6
+
7
+ Alors("la page contient {string}") do |content|
8
+ expect(page).to have_content(content)
9
+ end
10
+
11
+ Alors("l'en-tête contient {string}") do |content|
12
+ within(".fr-header") do
13
+ step(%(la page contient "#{content}"))
14
+ end
15
+ end
16
+
17
+ Quand("je clique sur {string}") do |label|
18
+ click_link_or_button label
19
+ end
20
+
21
+ Quand("je clique sur le premier lien ou bouton {string}") do |label|
22
+ click_link_or_button(label, match: :first)
23
+ end
24
+
25
+ Alors("la page contient un bouton {string}") do |label|
26
+ expect(page).to have_button(label)
27
+ end
28
+
29
+ Alors("la page ne contient pas de bouton {string}") do |title|
30
+ expect(page).not_to have_button(title)
31
+ end
32
+
33
+ Alors("la page contient un bouton {string} désactivé") do |content|
34
+ expect(page).to have_button(content, disabled: true)
35
+ end
36
+
37
+ Alors("la page ne contient pas {string}") do |content|
38
+ expect(page).to have_no_content(content)
39
+ end
40
+
41
+ Alors("la page contient un lien {string}") do |text|
42
+ expect(page).to have_link(text)
43
+ end
44
+
45
+ Alors("le titre de la page contient {string}") do |text|
46
+ expect(page.title.gsub(" ", " ")).to include text
47
+ end
48
+
49
+ Alors("il y a un titre de premier niveau contenant {string}") do |text|
50
+ # le titre est soit le premier h1 ou la légende du premier tableau
51
+ element = page.first("h1") || page.first("table caption")
52
+
53
+ expect(element.text).to include(text)
54
+ end
55
+
56
+ Alors("la page est titrée {string}") do |text|
57
+ steps %(
58
+ Alors il y a un titre de premier niveau contenant "#{text}"
59
+ Et le titre de la page contient "#{text}"
60
+ )
61
+ end
62
+
63
+ Quand("je remplis {string} avec {string}") do |label, value|
64
+ fill_in label, with: value
65
+ end
66
+
67
+ Quand("je remplis le champ {string} avec {string} dans les champs de {string}") do |label, value, fieldset_legend|
68
+ within_fieldset(fieldset_legend) do
69
+ fill_in label, with: value
70
+ end
71
+ end
72
+
73
+ Quand("je coche {string}") do |label|
74
+ check label
75
+ end
76
+
77
+ Quand("je décoche {string}") do |label|
78
+ uncheck label
79
+ end
80
+
81
+ Quand("je coche toutes les cases") do
82
+ page
83
+ .all('input[type="checkbox"]')
84
+ .map { |node| node.sibling("label").text }
85
+ .each { |label| step(%(je coche "#{label}")) }
86
+ end
87
+
88
+ Quand("je clique sur {string} dans la rangée {string}") do |link, row|
89
+ within("tr", text: row) do
90
+ click_link_or_button(link)
91
+ end
92
+ end
93
+
94
+ Alors("la rangée {string} contient {string}") do |row, content|
95
+ within("tr", text: row) do
96
+ expect(page).to have_content(content)
97
+ end
98
+ end
99
+
100
+ Quand("je clique sur {string} dans la dernière rangée") do |link|
101
+ within(all("tr").last) do
102
+ click_link_or_button(link)
103
+ end
104
+ end
105
+
106
+ Quand("je remplis le champ {string} dans la rangée {string} avec {string}") do |locator, row, value|
107
+ within("tr", text: row) do
108
+ fill_in locator, with: value
109
+ end
110
+ end
111
+
112
+ Quand("je sélectionne {string} pour {string}") do |option, name|
113
+ select option, from: name
114
+ end
115
+
116
+ Quand("je choisis {string}") do |option|
117
+ choose option
118
+ end
119
+
120
+ Quand("je choisis {string} pour {string}") do |option, fieldset|
121
+ within("fieldset", text: fieldset) do
122
+ choose option
123
+ end
124
+ end
125
+
126
+ Quand("j'attache le fichier {string} pour le champ {string}") do |path, field|
127
+ attach_file(field, path)
128
+ end
129
+
130
+ Alors("le tableau {string} contient :") do |caption, table|
131
+ expect(page).to have_table(caption, with_rows: table.rows)
132
+ end
133
+
134
+ Alors("la colonne {string} du tableau {string} contient dans l'ordre :") do |column_name, caption, table_data|
135
+ within_table(caption) do
136
+ headers = all("thead th").map(&:text)
137
+ column_index = headers.index(column_name)
138
+
139
+ column_texts = all("tbody tr").map do |row|
140
+ row.all("th, td")[column_index].text
141
+ end
142
+
143
+ expect(column_texts).to eq(table_data.raw.flatten)
144
+ end
145
+ end
146
+
147
+ Quand("je rafraîchis la page") do
148
+ visit current_path
149
+ end
150
+
151
+ Alors("le fil d'Ariane affiche {string}") do |path|
152
+ components = path.split(" > ")
153
+
154
+ breadcrumbs = page.all("nav.fr-breadcrumb li").map(&:text)
155
+
156
+ expect(breadcrumbs).to eq components
157
+ end
158
+
159
+ Quand("je clique sur {string} dans le menu principal") do |item|
160
+ within("nav#main-nav") do
161
+ click_link_or_button(item)
162
+ end
163
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Cucumber
6
+ # this cop ensures we write constitent steps: active voice for
7
+ # the actions, and passive voice for the expectations.
8
+ #
9
+ # ---
10
+ # Actions :
11
+ #
12
+ # bad :
13
+ # Quand le bouton Enregistrer est cliqué
14
+
15
+ # good :
16
+ # Quand je clique sur le bouton Enregistrer
17
+ #
18
+ # ---
19
+ # Exceptations :
20
+ #
21
+ # bad :
22
+ # Alors je peux voir dans le tableau X que la colonne Y contient dans cet ordre
23
+ #
24
+ # good :
25
+ # Alors la colonne X dans le tableau Y contient dans cet ordre :
26
+ #
27
+ class StepNaming < Base
28
+ MSG_QUAND_SHOULD_START_WITH_JE = '"Quand" steps should start with "je"'
29
+ MSG_ALORS_SHOULD_NOT_START_WITH_JE = '"Alors" steps should not start with "je"'
30
+
31
+ def on_send(node)
32
+ return unless step_definition?(node)
33
+
34
+ step_type = node.method_name.to_s
35
+ step_text = extract_step_text(node)
36
+
37
+ return unless step_text
38
+
39
+ case step_type
40
+ when "Quand"
41
+ check_quand_step(node, step_text)
42
+ when "Alors"
43
+ check_alors_step(node, step_text)
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def step_definition?(node)
50
+ %w[Quand Alors].include?(node.method_name.to_s) && node.arguments.any?
51
+ end
52
+
53
+ def extract_step_text(node)
54
+ first_arg = node.arguments.first
55
+ return unless first_arg&.str_type?
56
+
57
+ first_arg.str_content
58
+ end
59
+
60
+ def check_quand_step(node, step_text)
61
+ return if step_text.start_with?("je ") || step_text.start_with?("j'")
62
+
63
+ add_offense(node, message: MSG_QUAND_SHOULD_START_WITH_JE)
64
+ end
65
+
66
+ def check_alors_step(node, step_text)
67
+ return unless step_text.start_with?("je ") || step_text.start_with?("j'")
68
+
69
+ add_offense(node, message: MSG_ALORS_SHOULD_NOT_START_WITH_JE)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: betagouv-cucumber-steps
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stéphane Maniaci
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-07-10 00:00:00.000000000 Z
10
+ date: 2025-08-20 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  description: These helpers provide a (French) human syntax to drive a browser
13
13
  email:
@@ -18,11 +18,15 @@ extra_rdoc_files: []
18
18
  files:
19
19
  - ".rspec"
20
20
  - ".rubocop.yml"
21
+ - ".rubocop_todo.yml"
21
22
  - LICENSE.txt
22
23
  - README.md
23
24
  - Rakefile
25
+ - lib/betagouv/cucumber/debug_steps.rb
24
26
  - lib/betagouv/cucumber/steps.rb
25
27
  - lib/betagouv/cucumber/steps/version.rb
28
+ - lib/betagouv/cucumber/web_steps.rb
29
+ - lib/rubocop/cop/cucumber/step_naming.rb
26
30
  - sig/betagouv/cucumber/steps.rbs
27
31
  homepage: https://github.com/betagouv/cucumber-steps
28
32
  licenses: