Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement <Set> to have an item satisfying <any|assertion> #52

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 19 additions & 0 deletions .vscode/launch.json
@@ -0,0 +1,19 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"protocol": "inspector",
"type": "node",
"request": "launch",
"name": "Mocha Tests",
"program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
"internalConsoleOptions": "openOnSessionStart",
"skipFiles": [
"<node_internals>/**" // Prevent stepping through async_hooks.js et al.
]
}
]
}
2 changes: 1 addition & 1 deletion documentation/assertions/Set/to-be-empty.md
Expand Up @@ -11,7 +11,7 @@ expect(new Set([1, 2, 3]), 'to be empty');
```

```output
expected Set([1, 2, 3]) to be empty
expected Set([ 1, 2, 3 ]) to be empty
```

The assertion can be negated using the `not` flag:
Expand Down
37 changes: 37 additions & 0 deletions documentation/assertions/Set/to-have-an-item-satisfying.md
@@ -0,0 +1,37 @@
Asserts that at least one item contained in a Set satisfies a given value, function or other assertion.

```js
expect(new Set([1, 2, 3]), 'to have an item satisfying', 4);
```

```output
expected Set([ 1, 2, 3 ]) to have an item satisfying 4

Set([
1, // should equal 4
2, // should equal 4
3 // should equal 4
])
```

In order to check a property holds for at least one item, an assertion can be
passed as the argument – in this example we assert that one of the items in
the set is a number:

```js
expect(
new Set(['a', false, []]),
'to have an item satisfying',
'to be a number'
);
```

```output
expected Set([ 'a', false, [] ]) to have an item satisfying to be a number

Set([
'a', // should be a number
false, // should be a number
[] // should be a number
])
```
118 changes: 118 additions & 0 deletions lib/unexpected-set.js
Expand Up @@ -272,6 +272,124 @@ module.exports = {
}
);

expect.addAssertion(
[
'<Set> to have an item [exhaustively] satisfying <any>',
'<Set> to have an item [exhaustively] satisfying <assertion>',
],
(expect, subject, nextArg) => {
expect.errorMode = 'nested';
expect(subject, 'not to be empty');
expect.errorMode = 'bubble';

const subjectElements = [];
subject.forEach((element) => {
subjectElements.push(element);
});

const expected =
typeof nextArg === 'string'
? expect.it((s) => expect.shift(s, 0))
: nextArg;
const expectedType = expect.findTypeOf(expected);
const results = new Array(subjectElements.length);
subjectElements.forEach((subjectElement, subjectIndex) => {
results[subjectIndex] = expect.promise(() => {
if (expectedType.is('expect.it')) {
expect.context.thisObject = subject;
return expected(subjectElement, expect.context);
} else {
return expect(
subjectElement,
'to [exhaustively] satisfy',
nextArg
);
}
});
});

return expect.promise.settle(results).then(function () {
if (results.every((result) => !result.isFulfilled())) {
expect.fail({
message(output) {
output.append(
expect.standardErrorMessage(output.clone(), { compact: true })
);
},
diff(output, diff, inspect, equal) {
output.inline = true;
const prefixOutput = expect.subjectType.prefix(
output.clone(),
subject
);
const suffixOutput = expect.subjectType.suffix(
output.clone(),
subject
);

output.append(prefixOutput);
if (!prefixOutput.isEmpty()) {
output.nl();
}
if (expect.subjectType.indent) {
output.indentLines();
}

let index = 0;
subjectElements.forEach((subjectElement, subjectIndex) => {
output.omitSubject = subject;
output
.nl(index > 0 ? 1 : 0)
.i()
.block(function () {
const delimiterOutput = expect.subjectType.delimiter(
output.clone(),
subjectIndex,
subjectElements.length
);

const err = results[subjectIndex].reason();
const diff = err.getDiff({ output: output.clone() });

if (diff && diff.inline) {
this.append(diff).amend(delimiterOutput);
} else {
this.appendInspected(subjectElement)
.amend(delimiterOutput)
.sp()
.annotationBlock(function () {
this.omitSubject = subjectElement;
const label = err.getLabel();
if (label) {
this.error(label).sp().block(inspect(nextArg));
if (diff) {
this.nl(2).append(diff);
}
} else {
this.appendErrorMessage(err);
}
});
}
});
index += 1;
});

if (expect.subjectType.indent) {
output.outdentLines();
}
if (!suffixOutput.isEmpty()) {
output.nl();
}
output.append(suffixOutput);

return output;
},
});
}
});
}
);

expect.addAssertion(
'<Set> to [exhaustively] satisfy <Set>',
(expect, subject, value) => {
Expand Down
86 changes: 86 additions & 0 deletions test/unexpected-set.js
Expand Up @@ -204,6 +204,92 @@ describe('unexpected-set', () => {
});
});

describe('to have an item satisfying assertion', () => {
it('should succeed with a primitive as the expected value', () => {
expect(new Set([1, 2, 3]), 'to have an item satisfying', 3);
});

it('should succeed with an expect.it as the expected value', () => {
expect(
new Set([1, 2, 3]),
'to have an item satisfying',
expect.it('to be a number').and('to be greater than', 2)
);
});

it('should succeed with an assertion as the expected value', () => {
expect(
new Set([1, 2, 3]),
'to have an item satisfying',
'to be a number'
);
});

it('should succeed with an array as the expected value', () => {
expect(new Set([[1], [2], [3]]), 'to have an item satisfying', [
expect.it('to be a number').and('to be greater than', 2),
]);
});

it('should fail with a diff', () => {
expect(
() => {
expect(new Set([[1], [2], ['foo']]), 'to have an item satisfying', [
expect.it('to be a number').and('to be greater than', 2),
]);
},
'to throw',
"expected Set([ [ 1 ], [ 2 ], [ 'foo' ] ]) to have an item satisfying\n" +
'[\n' +
" expect.it('to be a number')\n" +
" .and('to be greater than', 2)\n" +
']\n' +
'\n' +
'Set([\n' +
' [\n' +
' 1 // ✓ should be a number and\n' +
' // ⨯ should be greater than 2\n' +
' ],\n' +
' [\n' +
' 2 // ✓ should be a number and\n' +
' // ⨯ should be greater than 2\n' +
' ],\n' +
' [\n' +
" 'foo' // ⨯ should be a number and\n" +
' // ⨯ should be greater than 2\n' +
' ]\n' +
'])'
);
});

it('should fail with a non-inline diff', () => {
expect(
() => {
expect(
new Set(['foo']),
'to have an item satisfying',
expect.it('to equal', 'bar')
);
},
'to throw',
"expected Set([ 'foo' ]) to have an item satisfying expect.it('to equal', 'bar')\n" +
'\n' +
'Set([\n' +
" 'foo' // should equal 'bar'\n" +
' //\n' +
' // -foo\n' +
' // +bar\n' +
'])'
);
});

it('should fail for the empty set', () => {
expect(() => {
expect(new Set([]), 'to have an item satisfying to be a number');
}, 'to throw');
});
});

describe('to satisfy assertion', () => {
describe('with a Set instance', () => {
describe('against another Set instance', () => {
Expand Down