rspec-path_matchers 0.1.1 → 0.2.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.release-please-manifest.json +1 -1
  3. data/.rubocop.yml +5 -0
  4. data/CHANGELOG.md +21 -0
  5. data/README.md +161 -260
  6. data/design.rb +10 -10
  7. data/lib/rspec/path_matchers/matchers/base.rb +210 -56
  8. data/lib/rspec/path_matchers/matchers/directory_matcher.rb +172 -0
  9. data/lib/rspec/path_matchers/matchers/{have_file.rb → file_matcher.rb} +8 -5
  10. data/lib/rspec/path_matchers/matchers/no_entry_matcher.rb +64 -0
  11. data/lib/rspec/path_matchers/matchers/{have_symlink.rb → symlink_matcher.rb} +9 -6
  12. data/lib/rspec/path_matchers/options/atime.rb +6 -40
  13. data/lib/rspec/path_matchers/options/base.rb +296 -0
  14. data/lib/rspec/path_matchers/options/birthtime.rb +6 -49
  15. data/lib/rspec/path_matchers/options/content.rb +13 -42
  16. data/lib/rspec/path_matchers/options/ctime.rb +6 -40
  17. data/lib/rspec/path_matchers/options/etc_base.rb +42 -0
  18. data/lib/rspec/path_matchers/options/file_stat_base.rb +47 -0
  19. data/lib/rspec/path_matchers/options/group.rb +5 -52
  20. data/lib/rspec/path_matchers/options/json_content.rb +6 -30
  21. data/lib/rspec/path_matchers/options/mode.rb +6 -40
  22. data/lib/rspec/path_matchers/options/mtime.rb +7 -41
  23. data/lib/rspec/path_matchers/options/owner.rb +6 -53
  24. data/lib/rspec/path_matchers/options/parsed_content_base.rb +67 -0
  25. data/lib/rspec/path_matchers/options/size.rb +5 -40
  26. data/lib/rspec/path_matchers/options/symlink_atime.rb +7 -40
  27. data/lib/rspec/path_matchers/options/symlink_birthtime.rb +7 -49
  28. data/lib/rspec/path_matchers/options/symlink_ctime.rb +7 -40
  29. data/lib/rspec/path_matchers/options/symlink_group.rb +6 -52
  30. data/lib/rspec/path_matchers/options/symlink_mtime.rb +7 -40
  31. data/lib/rspec/path_matchers/options/symlink_owner.rb +7 -53
  32. data/lib/rspec/path_matchers/options/symlink_target.rb +6 -43
  33. data/lib/rspec/path_matchers/options/symlink_target_exist.rb +6 -41
  34. data/lib/rspec/path_matchers/options/symlink_target_type.rb +20 -43
  35. data/lib/rspec/path_matchers/options/yaml_content.rb +6 -31
  36. data/lib/rspec/path_matchers/options.rb +1 -0
  37. data/lib/rspec/path_matchers/refinements.rb +79 -0
  38. data/lib/rspec/path_matchers/version.rb +1 -1
  39. data/lib/rspec/path_matchers.rb +185 -16
  40. metadata +12 -8
  41. data/lib/rspec/path_matchers/matchers/directory_contents_inspector.rb +0 -57
  42. data/lib/rspec/path_matchers/matchers/have_directory.rb +0 -126
  43. data/lib/rspec/path_matchers/matchers/have_no_entry.rb +0 -49
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2a59b91fb7ad467e3e63ab8283922768d3689b09cd7d963d4faae49df16bf53b
4
- data.tar.gz: 655e3cf262349fac84617e86248172ad5f969d678acd2f8a50c72ed48fac82ad
3
+ metadata.gz: 485c656106b438d3aa53d5c4795e4d9a6e784e8e8e6aafa4b42aa704e5df9a36
4
+ data.tar.gz: '093a33b369d0a8d9c904080c84099f7f9cfe0f4095d2f3e5cba5b06af23ecd94'
5
5
  SHA512:
6
- metadata.gz: 76d0f829a0c9448834d700065249fc7dbae48dd55d192bb62097e4db8412eaf14f0222955b6ff3c039cf505c36f28dd5dd95063cd393e71953976d203daf8a00
7
- data.tar.gz: 29e65db5f7c0d4b2b96e6b1f2815502bfa884aca33e734b1866ab8f7e988a1671fbb7008c7e9121b9e0644d1928e605eb65498b77bf8645bc9c9a9c8fbdae971
6
+ metadata.gz: ef5013d34444c276cae1a37e18071780d1ab79045491222e022a6bd9391480dbf22d68abe8142dfb00036b06a2eb3fdb88823bc8b05029ce6f1bb78093a708b9
7
+ data.tar.gz: a28d711d8edc2494e5e228c23f2269f8936d01eb62566d3fe8a35f5a56ba66b3d64bc4e5c8e915dd22570f8f8ffcaa275de95a4a91dd3eb0953eecd09694bf54
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.1.1"
2
+ ".": "0.2.0"
3
3
  }
data/.rubocop.yml CHANGED
@@ -11,5 +11,10 @@ Metrics/AbcSize:
11
11
  - "spec/spec_helper.rb"
12
12
  - "spec/**/*_spec.rb"
13
13
 
14
+ Metrics/ParameterLists:
15
+ Exclude:
16
+ - "spec/spec_helper.rb"
17
+ - "spec/**/*_spec.rb"
18
+
14
19
  AllCops:
15
20
  TargetRubyVersion: 3.2
data/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.0](https://github.com/main-branch/rspec-path_matchers/compare/v0.1.1...v0.2.0) (2025-06-30)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * **dsl:** The format of failure messages has been completely redesigned. `be_dir`, `be_file`, and `be_symlink` are introduced as the preferred top-level matchers for clarity.
9
+ * You must use `#containing` to to set expectations on dir content instead of a block
10
+
11
+ ### Features
12
+
13
+ * Add the be_dir matcher ([b16bf00](https://github.com/main-branch/rspec-path_matchers/commit/b16bf005fabba9dfa55b284543c1347efcaae62e))
14
+ * Change how expectations are nested for have_dir and be_dir ([e61d8fd](https://github.com/main-branch/rspec-path_matchers/commit/e61d8fddf0b6f3290fbdd01c15940b5a8a2e7361))
15
+ * **dsl:** Introduce be_* matchers for a clearer and more robust API ([0c76e85](https://github.com/main-branch/rspec-path_matchers/commit/0c76e8564215ae3e86b72135c7793c5ca386b53e))
16
+
17
+
18
+ ### Other Changes
19
+
20
+ * Refactor the Options classes to reduce duplicaate code ([7e23190](https://github.com/main-branch/rspec-path_matchers/commit/7e231902a1a56162511a745d529c19acd06d50e6))
21
+ * Update README to give an example toward the top of the doc ([ece4afc](https://github.com/main-branch/rspec-path_matchers/commit/ece4afc3edb1e27e96fa3fe70052c99a73f6a221))
22
+ * Update README to give an example toward the top of the doc ([5b0f196](https://github.com/main-branch/rspec-path_matchers/commit/5b0f19646fe6cd7b7cba6c057e690b7b54d5c3c3))
23
+
3
24
  ## [0.1.1](https://github.com/main-branch/rspec-path_matchers/compare/v0.1.0...v0.1.1) (2025-06-25)
4
25
 
5
26
 
data/README.md CHANGED
@@ -10,13 +10,13 @@ License](https://img.shields.io/badge/license-MIT-green)](https://opensource.org
10
10
  - [Summary](#summary)
11
11
  - [Installation](#installation)
12
12
  - [Setup](#setup)
13
- - [Usage \& Examples](#usage--examples)
13
+ - [Usage](#usage)
14
14
  - [Basic Assertions](#basic-assertions)
15
- - [Negative Assertions (Checking for Absence)](#negative-assertions-checking-for-absence)
16
- - [File Content Assertions](#file-content-assertions)
17
15
  - [Attribute Assertions](#attribute-assertions)
18
- - [Directory Structure Assertions](#directory-structure-assertions)
19
- - [Exact Directory Contents](#exact-directory-contents)
16
+ - [Directory Content \& Nested Assertions](#directory-content--nested-assertions)
17
+ - [Clear Failure Messages](#clear-failure-messages)
18
+ - [Available Options](#available-options)
19
+ - [Added Value Over Standard RSpec Matchers](#added-value-over-standard-rspec-matchers)
20
20
  - [Development](#development)
21
21
  - [Contributing](#contributing)
22
22
  - [License](#license)
@@ -24,13 +24,143 @@ License](https://img.shields.io/badge/license-MIT-green)](https://opensource.org
24
24
  ## Summary
25
25
 
26
26
  **RSpec::PathMatchers** provides a comprehensive suite of RSpec matchers for
27
- testing file system entries and structures.
27
+ testing directory structures.
28
28
 
29
29
  Verifying that a generator, build script, or any file-manipulating process has
30
30
  produced the correct output can be tedious and verbose. This gem makes those
31
- assertions simple, declarative, and easy to read, allowing you to describe an entire
31
+ assertions simple, declarative, and easier to read, allowing you to describe an entire
32
32
  file tree and its properties within your specs.
33
33
 
34
+ ## Installation
35
+
36
+ Add this line to your application's `Gemfile` in the `:test` or `:development` group:
37
+
38
+ ```ruby
39
+ group :test, :development do
40
+ gem 'rspec-path_matchers'
41
+ end
42
+ ```
43
+
44
+ OR add it to your project's `gemspec`:
45
+
46
+ ```ruby
47
+ spec.add_development_dependency 'rspec-path_matchers'
48
+ ```
49
+
50
+ And then execute:
51
+
52
+ ```bash
53
+ bundle install
54
+ ```
55
+
56
+ ## Setup
57
+
58
+ Require the gem in your `spec/spec_helper.rb` file:
59
+
60
+ ```ruby
61
+ # spec/spec_helper.rb
62
+ require 'rspec/path_matchers'
63
+
64
+ RSpec.configure do |config|
65
+ config.include RSpec::PathMatchers
66
+ end
67
+ ```
68
+
69
+ ## Usage
70
+
71
+ ### Basic Assertions
72
+
73
+ In their simplest forms, the `be_dir`, `be_file`, and `be_symlink` matchers **verify
74
+ the existence and type** of the given path. Each also supports negative expectations.
75
+
76
+ ```ruby
77
+ # Check for existence and type
78
+ expect('/tmp/new_project').to be_dir
79
+
80
+ # Check for absence
81
+ expect('/tmp/new_project').not_to be_file
82
+ ```
83
+
84
+ ### Attribute Assertions
85
+
86
+ All top-level and nested matchers can take options as a hash to assert on specific
87
+ file attributes. The value of each option can be an RSpec matcher or a literal value.
88
+
89
+ ```ruby
90
+ expect('config.yml').to be_file(content: include('production'), size: be < 1000)
91
+
92
+ expect('app.sock').to be_symlink(target: '/var/run/app.sock')
93
+ ```
94
+
95
+ ### Directory Content & Nested Assertions
96
+
97
+ The real power comes from describing the contents of a directory. The `#containing`
98
+ method asserts that a directory contains at least the specified entries, while
99
+ `#containing_exactly` asserts that it has exactly those entries and no others.
100
+
101
+ These expectations can be nested to any depth to describe a complete directory tree.
102
+
103
+ ```ruby
104
+ expect('/var/www').to(
105
+ be_dir.containing(
106
+ file('index.html', mode: '0644'),
107
+ dir('assets').containing_exactly(
108
+ file('app.js'),
109
+ file('style.css')
110
+ ),
111
+ no_file_named('config.php') # Assert that an entry does not exist
112
+ )
113
+ )
114
+ ```
115
+
116
+ Methods available as arguments to the containing methods include `dir`, `file`,
117
+ `symlink`, `no_file_named`, `no_dir_named`, and `no_symlink_named`.
118
+
119
+ ### Clear Failure Messages
120
+
121
+ When an expectation is not met, this library gives well-formatted, easy-to-diagnose
122
+ error messages. If the index.html file from the previous example had the wrong
123
+ permissions AND style.css did not exist, the error would be:
124
+
125
+ ```text
126
+ '/var/www' was not as expected:
127
+ - index.html
128
+ expected mode to be '0644', but it was '0600'
129
+ - assets/style.css
130
+ expected it to exist
131
+ ```
132
+
133
+ ### Available Options
134
+
135
+ Here is a list of all options that can be given to the matchers in this gem:
136
+
137
+ ```ruby
138
+ mode: <matcher|String>,
139
+ size: <matcher|Integer>, # only for file matchers
140
+
141
+ # Owner and group require a Unix-like platform that supports the Etc module.
142
+ owner: <matcher|String>,
143
+ group: <matcher|String>,
144
+
145
+ # See `File.birthtime`, `File.atime`, etc. for platform support
146
+ birthtime: <matcher|Time|DateTime>,
147
+ atime: <matcher|Time|DateTime>,
148
+ ctime: <matcher|Time|DateTime>,
149
+ mtime: <matcher|Time|DateTime>,
150
+
151
+ # Content matchers are only for file matchers
152
+ content: <matcher|String|Regexp>,
153
+ json_content: <matcher|true>,
154
+ yaml_content: <matcher|true>
155
+
156
+ # Target matchers are only for symlink matchers
157
+ target: <matcher|String>
158
+ target_type: <matcher|String|Symbol> # e.g., 'file', 'directory'
159
+ target_exist: <matcher|true|false>
160
+ ```
161
+
162
+ ## Added Value Over Standard RSpec Matchers
163
+
34
164
  Here’s a breakdown of the value this API provides over what is available in standard
35
165
  RSpec.
36
166
 
@@ -55,7 +185,7 @@ With this API (Declarative Style):
55
185
  ```ruby
56
186
  # This code describes the WHAT. The implementation details are hidden.
57
187
  # It reads like a specification document.
58
- expect('/var/www/html').to have_file('index.html', mode: '0644', owner: 'httpd')
188
+ expect('/var/www/html/index.html').to be_file(mode: '0644', owner: 'httpd')
59
189
  ```
60
190
 
61
191
  This hides the complex, imperative logic inside the matcher and exposes a clean,
@@ -80,9 +210,9 @@ expect(JSON.parse(File.read(path))['status']).to eq('complete')
80
210
  With this API:
81
211
 
82
212
  ```ruby
83
- expect('/var/data').to have_file('status.json',
213
+ expect('/var/data/status.json').to be_file(
84
214
  size: be > 0,
85
- content: not(/error/),
215
+ content: not_to(match(/error/)),
86
216
  json_content: include('status' => 'complete')
87
217
  )
88
218
  ```
@@ -111,15 +241,17 @@ With this API:
111
241
 
112
242
  ```ruby
113
243
  # This is a clear, hierarchical specification of the directory's contents.
114
- expect('/etc/service').to(have_dir('nginx') do
115
- file('run')
116
- dir('log')
117
- no_file('down')
118
- end)
244
+ expect('/etc/service/nginx').to(
245
+ be_dir.containing(
246
+ file('run'),
247
+ dir('log'),
248
+ no_file_named('down')
249
+ )
250
+ )
119
251
  ```
120
252
 
121
- The nested block is a leap in expressiveness and power, allowing you to write
122
- complex integration and infrastructure tests with ease.
253
+ The nested `containing` matchers allows you to write tests that humans
254
+ can make sense of.
123
255
 
124
256
  <h3>4. Descriptive and Intelligible Failure Messages</h3>
125
257
 
@@ -128,9 +260,9 @@ saving you valuable debugging time.
128
260
 
129
261
  **Standard RSpec Failure:**
130
262
 
131
- ```
263
+ ```text
132
264
  expected: true
133
- got: false
265
+ got: false
134
266
  ```
135
267
 
136
268
  This kind of message forces you to manually inspect the directory structure to understand
@@ -141,252 +273,21 @@ what went wrong.
141
273
  You get a detailed, hierarchical report that shows the full expectation and
142
274
  clearly marks what failed.
143
275
 
144
- ```
145
- the entry 'my-app' at '/tmp/d20250622-12345-abcdef' was expected to satisfy the following but did not:
146
- - have directory "config" containing:
147
- - have file "database.yml" with owner "db_user" and mode "0600"
148
- - expected owner to be "db_user", but was "root"
149
- - expected mode to be "0600", but was "0644"
150
- ```
151
-
152
- ## Installation
153
-
154
- Add this line to your application's `Gemfile` in the `:test` or `:development` group:
155
-
156
- ```ruby
157
- group :test, :development do
158
- gem 'rspec-path_matchers'
159
- end
160
- ```
161
-
162
- And then execute:
163
-
164
- ```bash
165
- $ bundle install
166
- ```
167
-
168
- Or install it yourself as:
169
-
170
- ```bash
171
- $ gem install rspec-path_matchers
172
- ```
173
-
174
- ## Setup
175
-
176
- Require the gem in your `spec/spec_helper.rb` file:
276
+ For this expectation:
177
277
 
178
278
  ```ruby
179
- # spec/spec_helper.rb
180
- require 'rspec/path_matchers'
181
- ```
182
-
183
- ## Usage & Examples
184
-
185
- All matchers operate on a base path, making your tests clean and portable.
186
-
187
- ### Basic Assertions
188
-
189
- At its simplest, you can check for the existence of files and directories.
190
-
191
- ```ruby
192
- it "creates the basic structure" do
193
- # Setup: Create some files and directories in our temp dir
194
- FileUtils.mkdir_p(File.join(@tmpdir, "app/models"))
195
- FileUtils.touch(File.join(@tmpdir, "config.yml"))
196
-
197
- # Assertions
198
- expect(@tmpdir).to have_file("config.yml")
199
- expect(@tmpdir).to have_dir("app")
200
- end
201
- ```
202
-
203
- ### Negative Assertions (Checking for Absence)
204
-
205
- You can use `not_to` to ensure that a file, directory, or symlink of a specific
206
- type does not exist.
207
-
208
- - `not_to have_file` passes if the entry is a directory, a symlink, or non-existent.
209
- - `not_to have_dir` passes if the entry is a file, a symlink, or non-existent.
210
- - `not_to have_symlink` passes if the entry is a file, a directory, or non-existent.
211
-
212
- **Important:** Negative matchers cannot be given options (`:mode`, `:content`, etc.)
213
- or blocks.
214
-
215
- ```ruby
216
- it "can check for the absence of entries" do
217
- # Setup
218
- Dir.mkdir(File.join(@tmpdir, "existing_dir"))
219
- File.write(File.join(@tmpdir, "existing_file.txt"), "content")
220
-
221
- # Assert that a path that doesn't exist fails all checks
222
- expect(@tmpdir).not_to have_file("non_existent.txt")
223
- expect(@tmpdir).not_to have_dir("non_existent_dir")
224
- expect(@tmpdir).not_to have_symlink("non_existent_link")
225
-
226
- # Assert that an existing directory is NOT a file or symlink
227
- expect(@tmpdir).not_to have_file("existing_dir")
228
- expect(@tmpdir).not_to have_symlink("existing_dir")
229
- # expect(@tmpdir).not_to have_dir("existing_dir") # This would fail
230
-
231
- # Assert that an existing file is NOT a directory or symlink
232
- expect(@tmpdir).not_to have_dir("existing_file.txt")
233
- expect(@tmpdir).not_to have_symlink("existing_file.txt")
234
- # expect(@tmpdir).not_to have_file("existing_file.txt") # This would fail
235
- end
236
- ```
237
-
238
- ### File Content Assertions
239
-
240
- Go beyond existence and inspect what's inside a file.
241
-
242
- ```ruby
243
- before do
244
- File.write(File.join(@tmpdir, "app.log"), "INFO: User logged in\nWARN: Low disk space")
245
- File.write(File.join(@tmpdir, "config.json"), '{"theme":"dark","version":2}')
246
- FileUtils.touch(File.join(@tmpdir, "empty.file"))
247
- end
248
-
249
- it "validates file content" do
250
- # Check for content with a string or regex
251
- expect(@tmpdir).to have_file("app.log", content: "INFO: User logged in")
252
- expect(@tmpdir).to have_file("app.log", content: /WARN:.*space/)
253
-
254
- # Check for the absence of content
255
- expect(@tmpdir).to have_file("app.log", content: not(/ERROR/))
256
-
257
- # Check if a file is empty
258
- expect(@tmpdir).to have_file("empty.file", size: 0)
259
-
260
- # Check for valid JSON and match its structure
261
- expect(@tmpdir).to have_file("config.json", json_content: {
262
- "theme" => "dark",
263
- "version" => an_instance_of(Integer)
264
- })
265
- end
266
- ```
267
-
268
- ### Attribute Assertions
269
-
270
- Matcher options allow detailed expectations on files, directories, and symlinks. Here
271
- are the options available on the three top level matchers:
272
-
273
- ```text
274
- expect(path).to have_file(
275
- name, mode:, owner:, group:, ctime:, mtime:, size:, content:, json_content:, yaml_content:,
276
- )
277
-
278
- expect(path).to have_dir(
279
- name, mode:, owner:, group:, ctime:, mtime:, exact:
280
- )
281
-
282
- expect(path).to have_symlink(
283
- name, mode:, owner:, group:, ctime:, mtime:, target:, target_type:, target_exist:
279
+ expect('config').to(
280
+ be_dir.containing(
281
+ file('database.xml', owner: 'db_user', mode: '0600')
282
+ )
284
283
  )
285
284
  ```
286
285
 
287
- Here is a detailed example of using options:
288
-
289
- ```ruby
290
- before do
291
- # Create a script, a secret key, and a symlink
292
- script_path = File.join(@tmpdir, "deploy.sh")
293
- File.write(script_path, "#!/bin/bash\n...")
294
- FileUtils.chmod(0755, script_path)
295
-
296
- key_path = File.join(@tmpdir, "secret.key")
297
- File.write(key_path, "KEY_DATA")
298
- FileUtils.chmod(0600, key_path)
299
-
300
- FileUtils.ln_s("deploy.sh", File.join(@tmpdir, "latest_script"))
301
- end
302
-
303
- it "validates file attributes" do
304
- # A single file can have many attributes checked at once
305
- expect(@tmpdir).to have_file("deploy.sh", mode: "0755", size: be > 10)
306
-
307
- # On Unix systems, you can check ownership
308
- current_user = Etc.getlogin
309
- expect(@tmpdir).to have_file("secret.key", owner: current_user, mode: "0600")
310
-
311
- # Check symlinks and their targets
312
- expect(@tmpdir).to have_symlink("latest_script", target: "deploy.sh")
313
- end
314
- ```
315
-
316
- ### Directory Structure Assertions
317
-
318
- The block syntax is the most powerful feature. It allows you to describe and verify
319
- an entire file tree, including both the presence and *absence* of entries using
320
- methods like `no_file`, `no_dir`, and `no_symlink`.
321
-
322
- ```ruby
323
- before do
324
- # Generate a complex directory structure
325
- app_dir = File.join(@tmpdir, "my-app")
326
- FileUtils.mkdir_p(File.join(app_dir, "bin"))
327
- FileUtils.mkdir_p(File.join(app_dir, "config"))
328
- FileUtils.mkdir_p(File.join(app_dir, "log"))
329
-
330
- File.write(File.join(app_dir, "bin/run"), "#!/bin/bash")
331
- FileUtils.chmod(0755, File.join(app_dir, "bin/run"))
332
-
333
- File.write(File.join(app_dir, "config/database.yml"), "adapter: postgresql")
334
- FileUtils.ln_s("database.yml", File.join(app_dir, "config/db.yml"))
335
- end
336
-
337
- it "validates a nested directory structure" do
338
- # Note the parentheses around the matcher and its block
339
- expect(@tmpdir).to(have_dir("my-app") do
340
- # Assert on the 'bin' directory and its contents
341
- dir "bin" do
342
- file "run", mode: "0755", content: /bash/
343
- end
344
-
345
- # Assert on the 'config' directory and its contents
346
- dir "config" do
347
- file "database.yml"
348
- symlink "db.yml", target: "database.yml"
349
- no_file "secrets.yml" # Assert that a file is NOT present
350
- end
351
-
352
- # Assert that the 'log' directory is present and empty
353
- dir "log"
354
-
355
- # Assert the absence of other entries at the root of 'my-app'
356
- no_dir "tmp"
357
- no_file "README.md"
358
- end)
359
- end
360
- ```
361
-
362
- ### Exact Directory Contents
363
-
364
- You can enforce that a directory contains *only* the entries defined in your
365
- specification block by using the `exact: true` option. This is perfect for testing
366
- generators or build scripts that should produce a clean, specific output without any
367
- extra files.
368
-
369
- If any undeclared entries are found on the PathMatchers, the matcher will fail.
370
-
371
- ```ruby
372
- it "creates a directory with only the expected files" do
373
- # Setup: Create a directory with an extra, unexpected file.
374
- FileUtils.mkdir(File.join(@tmpdir, 'dist'))
375
- File.write(File.join(@tmpdir, 'dist/app.js'), '// ...')
376
- File.write(File.join(@tmpdir, 'dist/unexpected.log'), 'debug info')
377
-
378
- # This test will fail because 'unexpected.log' was not declared.
379
- expect(@tmpdir).to(
380
- have_dir('dist', exact: true) do
381
- file 'app.js'
382
- end
383
- )
384
- end
385
-
386
- # Failure Message:
387
- #
388
- # the entry 'dist' at '...' was expected to satisfy the following but did not:
389
- # - did not expect entries ["unexpected.log"] to be present
286
+ ```text
287
+ 'config' was not as expected:
288
+ - database.xml
289
+ expected owner to be "db_user", but was "root"
290
+ expected mode to be "0600", but was "0644"
390
291
  ```
391
292
 
392
293
  ## Development
data/design.rb CHANGED
@@ -47,17 +47,17 @@ expect(path).to have_dir(name, mtime: matcher) # ...the directory modification t
47
47
 
48
48
  # Nested directory checks ()
49
49
  expect(path).to(
50
- have_dir(name) do
51
- file('nested_file.txt', content: 'expected content')
52
- dir('nested_dir') do
50
+ have_dir(name).containing(
51
+ file('nested_file.txt', content: 'expected content'),
52
+ symlink('nested_symlink', target: 'expected_target'),
53
+ no_file_named('non_existent_file.txt'),
54
+ no_dir_named('non_existent_dir'),
55
+ no_symlink_named('non_existent_symlink'),
56
+ no_entry_named('non_existent_entry'),
57
+ dir('nested_dir').containing_exactly(
53
58
  file('deeply_nested_file.txt', content: 'deeply expected content')
54
- end
55
- symlink('nested_symlink', target: 'expected_target')
56
- no_file('non_existent_file.txt')
57
- no_dir('non_existent_dir')
58
- no_symlink('non_existent_symlink')
59
- no_entry('non_existent_entry') # This checks for the absence of any type of entry with the given name
60
- end
59
+ )
60
+ )
61
61
  )
62
62
 
63
63
  ########################################