|
| 1 | +# Feature proposal: `expectFailure` enhancements |
| 2 | + |
| 3 | +## Summary |
| 4 | +Update the `expectFailure` option in `test()` to accept different types of values, enabling both **custom failure messages** and **error validation**. This aligns with `skip` and `todo` options while adding capabilities similar to `assert.throws`. |
| 5 | + |
| 6 | +## API & Behavior |
| 7 | + |
| 8 | +The behavior of `expectFailure` is determined by the type of value provided: |
| 9 | + |
| 10 | +### 1. String: Failure Reason |
| 11 | +When a **non-empty string** is provided, it acts as a documentation message (reason), identical to `skip` and `todo` options. |
| 12 | + |
| 13 | +```js |
| 14 | +test('fails with a specific reason', { |
| 15 | + expectFailure: 'Bug #123: Feature not implemented yet' |
| 16 | +}, () => { |
| 17 | + throw new Error('boom'); |
| 18 | +}); |
| 19 | +``` |
| 20 | +- **Behavior**: The test is expected to fail. The string is treated as a label/reason. |
| 21 | +- **Validation**: None. It accepts *any* error. |
| 22 | +- **Output**: The reporter displays the string (e.g., `# EXPECTED FAILURE Bug #123...`). |
| 23 | +- **Rationale**: Maintains consistency with existing `test` options where a string implies a reason/description. |
| 24 | + |
| 25 | +### 2. RegExp: Error Matcher |
| 26 | +When a **RegExp** is provided, it acts as a validator for the thrown error. |
| 27 | + |
| 28 | +```js |
| 29 | +test('fails with matching error', { |
| 30 | + expectFailure: /expected error message/ |
| 31 | +}, () => { |
| 32 | + throw new Error('this is the expected error message'); |
| 33 | +}); |
| 34 | +``` |
| 35 | +- **Behavior**: The test passes **only if** the thrown error matches the regular expression. |
| 36 | +- **Validation**: Strict matching against the error message. |
| 37 | +- **Output**: Standard expected failure output. |
| 38 | + |
| 39 | +## Ambiguity Resolution |
| 40 | +Potential ambiguity between "String as Reason" and "String as Matcher" is resolved by strict type separation: |
| 41 | +* `typeof value === 'string'` → **Reason** (Documentation only) |
| 42 | +* `value instanceof RegExp` → **Matcher** (Validation) |
| 43 | + |
| 44 | +Users needing to match a specific string error message should use a RegExp (e.g., `/Error message/`) to avoid confusion. |
| 45 | + |
| 46 | +## Edge Cases & Implementation Details |
| 47 | + |
| 48 | +### Empty String (`expectFailure: ''`) |
| 49 | +Following standard JavaScript truthiness rules, an empty string should be treated as **falsy**. |
| 50 | + |
| 51 | +* `expectFailure: ''` behaves exactly like `expectFailure: false`. |
| 52 | +* The feature is **disabled**, and the test is expected to pass normally. |
| 53 | + |
| 54 | +### Type Safety for `this.passed` |
| 55 | +The implementation must ensure that `this.passed` remains a strict `boolean`. |
| 56 | +Assigning a string directly (e.g., `this.passed = this.expectFailure`) is unsafe as it introduces type pollution (assigning `""` or `"reason"` instead of `false`/`true`). |
| 57 | + |
| 58 | +**Recommended Implementation Logic:** |
| 59 | +```javascript |
| 60 | +// When an error is caught: |
| 61 | +this.passed = !!this.expectFailure; // Forces conversion to boolean |
| 62 | +``` |
| 63 | +* If `expectFailure` is `"reason"` → `true` (Test Passes) |
| 64 | +* If `expectFailure` is `""` → `false` (Test Fails, as expected failure was not active) |
0 commit comments