You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: proposals/expect-failure-enhancements.md
+35-34Lines changed: 35 additions & 34 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -21,19 +21,25 @@ test('fails with a specific reason', {
21
21
-**Validation**: None. It accepts *any* error.
22
22
-**Output**: The reporter displays the string (e.g., `# EXPECTED FAILURE Bug #123...`).
23
23
24
-
### 2. RegExp: Error Matcher (via Object)
25
-
Use the object form with the `with` property.
24
+
### 2. Matcher: RegExp, Class, or Error Object
25
+
When a **RegExp**, **Class** (Function), or **Error Object** is provided directly, it acts as the validation logic. This leverages `assert.throws` behavior directly.
26
26
27
27
```js
28
-
test('fails with matching error', {
29
-
expectFailure:{ with:/expected error message/ }
28
+
test('fails with matching error (RegExp)', {
29
+
expectFailure:/expected error message/
30
30
}, () => {
31
31
thrownewError('this is the expected error message');
32
32
});
33
+
34
+
test('fails with matching error (Class)', {
35
+
expectFailure:RangeError
36
+
}, () => {
37
+
thrownewRangeError('Index out of bounds');
38
+
});
33
39
```
34
40
35
-
### 3. Object: Reason & Validation
36
-
When an**Object** is provided, it allows specifying both a failure reason and validation logic simultaneously.
41
+
### 3. Configuration Object: Reason & Validation
42
+
When a**Plain Object** with specific properties (`with`, `message`) is provided, it allows specifying both a failure reason and validation logic simultaneously.
37
43
38
44
```js
39
45
test('fails with reason and specific error', {
@@ -51,46 +57,41 @@ test('fails with reason and specific error', {
51
57
-**Behavior**: The test passes **only if** the error matches the `with` criteria.
52
58
-**Output**: The reporter displays the `message`.
53
59
60
+
### Equivalence
61
+
The following configurations are equivalent in behavior (both set a failure reason without validation):
62
+
```js
63
+
expectFailure:'reason'
64
+
expectFailure: { message:'reason' }
65
+
```
66
+
54
67
## Ambiguity Resolution
55
-
Potential ambiguity is resolved by strict type separation:
56
-
*`typeof value === 'string'` → **Reason**
57
-
*`typeof value === 'object'` → **Configuration Object** (`message` and/or `with`)
68
+
Potential ambiguity between a **Matcher Object** and a **Configuration Object** is resolved as follows:
69
+
70
+
1.**String** → Reason.
71
+
2.**RegExp** or **Function** → Matcher (Validation).
72
+
3.**Object**:
73
+
* If the object contains `with` or `message` properties → **Configuration Object**.
74
+
* Otherwise → **Matcher Object** (passed to `assert.throws` for property matching).
58
75
59
76
## Alternatives Considered
60
77
61
78
### Flat Options (`expectFailureError`)
62
79
It was proposed to split the options into `expectFailure` (reason) and `expectFailureError` (validation).
63
-
```js
64
-
{
65
-
expectFailure:'reason',
66
-
expectFailureError:/error/
67
-
}
68
-
```
69
-
This was rejected in favor of the nested object structure to:
70
-
1. Keep related configuration grouped.
71
-
2. Avoid polluting the top-level options namespace.
72
-
3. Allow future extensibility within the `expectFailure` object.
80
+
This was rejected in favor of the nested/polymorphic structure using `with` and `message` properties. This syntax was selected as the preferred choice for its readability and clarity:
81
+
*`with`: Clearly indicates "fails **with** this error" (Validation).
82
+
*`message`: Clearly indicates the **reason** or label for the expected failure.
83
+
This approach keeps related configuration grouped without polluting the top-level options namespace.
73
84
74
85
## Implementation Details
75
86
76
87
### Validation Logic
77
-
The implementation leverages `assert.throws` internally to perform error validation. This ensures consistency with the existing assertion ecosystem and supports advanced validation (Classes, Custom Functions) out of the box without code duplication.
88
+
The implementation leverages `assert.throws` internally to perform error validation.
89
+
- If `expectFailure` is a Matcher (RegExp, Class, Object), it is passed as the second argument to `assert.throws(fn, expectFailure)`.
90
+
- If `expectFailure` is a Configuration Object, `expectFailure.with` is passed to `assert.throws`.
78
91
79
-
## Edge Cases & Implementation Details
92
+
## Edge Cases
80
93
81
94
### Empty String (`expectFailure: ''`)
82
95
Following standard JavaScript truthiness rules, an empty string should be treated as **falsy**.
83
96
*`expectFailure: ''` behaves exactly like `expectFailure: false`.
84
-
* The feature is **disabled**, and the test is expected to pass normally.
85
-
86
-
### Type Safety for `this.passed`
87
-
The implementation must ensure that `this.passed` remains a strict `boolean`.
88
-
Assigning a string directly (e.g., `this.passed = this.expectFailure`) is unsafe as it introduces type pollution.
89
-
90
-
**Recommended Implementation Logic:**
91
-
```javascript
92
-
// When an error is caught:
93
-
this.passed=!!this.expectFailure; // Forces conversion to boolean
94
-
```
95
-
* If `expectFailure` is `"reason"` → `true` (Test Passes)
96
-
* If `expectFailure` is `""` → `false` (Test Fails, as expected failure was not active)
97
+
* The feature is **disabled**, and the test is expected to pass normally.
0 commit comments