| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
There was a problem hiding this comment.
Nit: s/conveniency/convenience/?
Sorry, something went wrong.
There was a problem hiding this comment.
Thank you 😅
Sorry, something went wrong.
There was a problem hiding this comment.
Maybe name assert.doesNotThrow as a top level function and reference it directly here to avoid being messed up by monkey-patched assert.doesNotThrow
Sorry, something went wrong.
There was a problem hiding this comment.
Maybe name assert.throws as a top level function and reference it directly here to avoid being messed up by monkey-patched assert.throws
Sorry, something went wrong.
There was a problem hiding this comment.
👌
Sorry, something went wrong.
There was a problem hiding this comment.
Since other assigned APIs do not return promises here:
require('assert').promises.fail().then(() => {}); // does not workThis behavior looks somewhat strange to me..
Sorry, something went wrong.
There was a problem hiding this comment.
To me as well. I would rather have a dedicated assert.rejects function. Reject is used for promises, so this is a good fit out of my perspective.
Sorry, something went wrong.
There was a problem hiding this comment.
That's an interesting point.
My intention was to keep the existing behaviour for all assert functions except throws() and doesNotThrow(), which are the only two able to run (potentially async) code.
I wouldn't like to force people writing:
const assert = require('assert').promises
await assert(something)But we can definitely add new functions like reject() that would be async equivalent of existing one.
Are there any other function you would like to cover?
Sorry, something went wrong.
There was a problem hiding this comment.
If we are not talking about redesigning a new assert API here, providing a promisified version of the existing assert APIs is good enough to me. Something like
for (const key of Object.keys(assert)) {
const syncFunc = assert[key];
promises[key] = async function (...args) { return syncFunc(...args) };
}
// override throws later, or in the block
Sorry, something went wrong.
There was a problem hiding this comment.
hum, but it's actually what I'd like to avoid.
There's no point making assert.ok() or assert.fail() asynchronous. It will make the API more cumbersome to use with no benefit.
It make sense for assert.throws() and assert.doesNotThrow() to turn this code (that we used quite a lot in our projects):
it('should report database option errors', async () => {
try {
await transferData({}, '');
throw new Error('it should have failed');
} catch (err) {
assert(err.message.includes('"host" is required'));
}
});into
it('should report database option errors', async () =>
assert.throws(async () => transferData({}, ''), /^Error: "host" is required/)
);I don't want to redesign the whole API, but rather wants to support the above.
Sorry, something went wrong.
There was a problem hiding this comment.
Why not?
Sorry, something went wrong.
There was a problem hiding this comment.
In this PR, you have currently (all synchronous):
I wasn't sure it worth providing require('assert').strict.promises. Do you think it worth it?
Sorry, something went wrong.
There was a problem hiding this comment.
In general strict and regular assert should have exactly the same API.
Sorry, something went wrong.
There was a problem hiding this comment.
Copy that
Sorry, something went wrong.
There was a problem hiding this comment.
To me as well. I would rather have a dedicated assert.rejects function. Reject is used for promises, so this is a good fit out of my perspective.
Sorry, something went wrong.
|
Because there seems to be a bit of confusion in the discussion: I personally am strongly against adding a new promise API. The only thing that should be added out of my perspective are two new functions rejects and doesNotReject. These two would be accessible in both the legacy and strict mode. They behave similar to the current throws and doesNotThrow functions but they are async instead of sync. No other assert function should be async as there is no reason for it as far as I can tell. |
Sorry, something went wrong.
|
@BridgeAR, by bringing assert.promises, I was following the advise @jasnell gave me on my previous PR. But what you suggested above makes more sense, so I'm 100% with you, and implement it if @joyeecheung is happy with it. |
Sorry, something went wrong.
|
@feugy If adding asynchronous versions of the synchronous APIs doesn't make sense, then maybe we don't even need to add them to assert.promises. It's fine to only have throws() and doesNotThrow() in assert.promises, and we can work out other APIs later. IMO assert.promises don't need to be feature-complete in this PR. |
Sorry, something went wrong.
There was a problem hiding this comment.
This is a good start but I'm also in favour of using rejects and doesNotReject instead of a separate promises namespace.
(Worth noting that this would also be in line with the fact that promises emit unhandledRejection rather than uncaughtException.)
Sorry, something went wrong.
There was a problem hiding this comment.
Can this be moved down closer to trySyncBlock which is its first usage? Or otherwise, move up those two functions here.
I would actually even suggest just inlining this function since it's only used twice. The savings are minimal and now the error thrown will have an extra function in the trace. The latter can be worked around (captureStackTrace) but I don't think it's worth it.
Sorry, something went wrong.
There was a problem hiding this comment.
We don't need the common.platformTimeout here. Just matters that it's async.
Sorry, something went wrong.
There was a problem hiding this comment.
nits:
s/doNotThrows()/doesNotThrow()
s/truely/truly
s/other/others
Sorry, something went wrong.
There was a problem hiding this comment.
s/or let it bubble/or rethrow.
Sorry, something went wrong.
There was a problem hiding this comment.
catch (err) or catch (e) is more in line with other usage in the code base.
Sorry, something went wrong.
|
Hello gents! What do you think about the latest update? |
Sorry, something went wrong.
There was a problem hiding this comment.
LGTM if CI is green
Sorry, something went wrong.
quick fyi... we're not all "gents" :-) |
Sorry, something went wrong.
|
Sorry, it's bad habit 😓 |
Sorry, something went wrong.
There was a problem hiding this comment.
Overall it is already looking very promising to me! Just a few small comments :-)
Sorry, something went wrong.
There was a problem hiding this comment.
This is actually a slightly different behavior as before.
Before assert.throws(() => {}, 'foo', undefined) would also result in an error. That is not the case anymore with this change. As this is completely independent from the actual change here, I would rather not have this included, especially as I think this should throw.
Sorry, something went wrong.
There was a problem hiding this comment.
You're absolutely right!
To keep this code in an internal function, and not break the existing behaviour, I've used rest params and splat so arguments.length would still make sense.
Sorry, something went wrong.
There was a problem hiding this comment.
The comment is actually not correct anymore as it is only a no-op in case actual is the sentinel.
I personally feel like it is not necessary to have these comments here because the code is easy to understand but others might disagree.
Sorry, something went wrong.
There was a problem hiding this comment.
👌
Sorry, something went wrong.
There was a problem hiding this comment.
Super tiny nit:
Destructuring would be nice as in const { promisify } = require('util')
Sorry, something went wrong.
There was a problem hiding this comment.
I am not totally happy with this test as it does not test that the code does not fail sync.
So I would write something like:
assert.doesNotThrow(() => {
assert.rejects(
() => assert.fail(),
common.expectsError({
code: 'ERR_ASSERTION',
type: assert.AssertionError,
message: 'Failed',
operator: undefined,
actual: undefined,
expected: undefined
})
)
});The same applies to the test below.
Sorry, something went wrong.
There was a problem hiding this comment.
👌
Sorry, something went wrong.
There was a problem hiding this comment.
If I am not mistaken, we do not know if these really work because the function itself might theoretically execute sync.
So a better test out of my perspective would be:
let promise;
let threw = false;
try {
promise = assert.doesNotReject(async () => {
await wait(10);
assert.fail();
});
} catch (err) {
threw = true;
}
assert.strictEqual(threw, false);
assert.rejects(() => promise,
common.expectsError({
code: 'ERR_ASSERTION',
type: assert.AssertionError,
message: 'Got unwanted exception ... '
...
})
);
Sorry, something went wrong.
There was a problem hiding this comment.
👌
Sorry, something went wrong.
There was a problem hiding this comment.
Hm, I would say it tests the functionality of assert.rejects() and assert.doesNotReject() but not that it "ensures" async support for those as they should always be async.
I would describe it as e.g. "Tests assert.rejects() and assert.doesNotReject() by checking their expected output and by verifying that they do not work sync."
Sorry, something went wrong.
There was a problem hiding this comment.
I am a bit surprised that this for the stackStartFn shall work. this should normally refer to either assert.ok or assert.strict.
Can you please write a test for this as well if none exist? :-)
Sorry, something went wrong.
There was a problem hiding this comment.
I added a test, and you were absolutely right.
Providing explicit values keeps the existing stack-traces:
Sorry, something went wrong.
There was a problem hiding this comment.
The code change itself looks very good now! Just the tests might need a bit more polishing :-)
Sorry, something went wrong.
There was a problem hiding this comment.
Super super tiny nit: you can actually access the own function without assert. It would just save some characters.
Sorry, something went wrong.
There was a problem hiding this comment.
It also keep you away from monkey-patching. I would prefer referencing it directly here.
Sorry, something went wrong.
There was a problem hiding this comment.
Nit: It would be really nice if the error message is changed to Missing expected rejection.
Sorry, something went wrong.
There was a problem hiding this comment.
The two tests above are already tested for in test-assert.js. This adds the check for the error stack, so it indeed tests something new, but in that case I would rather replace the ones in test-assert.js with these tests instead of "duplicating" them.
Sorry, something went wrong.
There was a problem hiding this comment.
This tests the same as https://github.com/nodejs/node/pull/18023/files#diff-cdf49dc677a8ea6f00de9f95639355e8R44.
What was not yet tested for is if the functions throws sync.
Sorry, something went wrong.
There was a problem hiding this comment.
Using a async function as wrapper will actually hide if the function is executed sync or async. So I would just use the pattern as above without the async wrapper.
Sorry, something went wrong.
|
@feugy thanks a lot for being so patient! |
Sorry, something went wrong.
There was a problem hiding this comment.
LGTM
Sorry, something went wrong.
There was a problem hiding this comment.
It also keep you away from monkey-patching. I would prefer referencing it directly here.
Sorry, something went wrong.
|
LGTM with @BridgeAR 's comments addressed |
Sorry, something went wrong.
There was a problem hiding this comment.
Maybe the next lines (copied from assert.doesNotThrow() doc) may be skipped in favour of a reference?
Sorry, something went wrong.
There was a problem hiding this comment.
I agree. I would actually even go so far and remove everything from here on. A text like:
Besides the async nature to await the completion it behaves identical to `assert.doesNotThrow()`.Keep an example with async await and also maybe add a example using promises.
The same can be applied to the reject function.
Sorry, something went wrong.
There was a problem hiding this comment.
operator & message will now contain appropriate values
Sorry, something went wrong.
There was a problem hiding this comment.
There is the unwritten rule to not use multi line template strings in Node.js. This does look somewhat elegant but it is probably best to just have something like: Missing expected ${fnType}${details}.
Sorry, something went wrong.
There was a problem hiding this comment.
I wasn't confident to replace common.expectsError() with such custom assertions, but couldn't find a better way to reuse the existing test, as suggested.
Sorry, something went wrong.
There was a problem hiding this comment.
I would actually just keep this as is and just add a single line to one of the existing tests that checks for assert.ok(!err.stack.includes('at throws').
Sorry, something went wrong.
There was a problem hiding this comment.
This exceeds 80 characters.
Sorry, something went wrong.
There was a problem hiding this comment.
Nit (non-blocking): I personally prefer the following to save lines:
assert.throws = function throws(block, ...args) {
expectsError(throws, getActual(block), ...args);
};
Sorry, something went wrong.
Sorry, something went wrong.
There was a problem hiding this comment.
Good point. We could check for the function name instead but that could theoretically be manipulated.
So just keep it as is. Thanks for the heads up.
Sorry, something went wrong.
There was a problem hiding this comment.
Hm, while looking at the code again: we actually already use the function name and do not strictly test if it it a "valid" function. So we could indeed just switch to name checking only.
Sorry, something went wrong.
There was a problem hiding this comment.
I agree. I would actually even go so far and remove everything from here on. A text like:
Besides the async nature to await the completion it behaves identical to `assert.doesNotThrow()`.Keep an example with async await and also maybe add a example using promises.
The same can be applied to the reject function.
Sorry, something went wrong.
There was a problem hiding this comment.
There is the unwritten rule to not use multi line template strings in Node.js. This does look somewhat elegant but it is probably best to just have something like: Missing expected ${fnType}${details}.
Sorry, something went wrong.
There was a problem hiding this comment.
I am relatively certain that this is going to trigger a lint error. The err should probably be in parentheses. The same applies to the test below.
Sorry, something went wrong.
There was a problem hiding this comment.
After thinking about this again, the try catch and the threw is not necessary here at all. If the promise throws the test would fail right away. threw is only necessary for tests that check for threw === true. So the tests can be much smaller (I know I originally suggested this pattern).
Sorry, something went wrong.
There was a problem hiding this comment.
The wrapper is not needed as the test would fail right away if assert.rejects would fail.
Sorry, something went wrong.
There was a problem hiding this comment.
The wrapper is not needed due to the reason mentioned above.
Sorry, something went wrong.
There was a problem hiding this comment.
I would actually just keep this as is and just add a single line to one of the existing tests that checks for assert.ok(!err.stack.includes('at throws').
Sorry, something went wrong.
There was a problem hiding this comment.
Hm, I am not certain if that is actually true? I thought that frame would actually be excluded? Do the tests pass?
Sorry, something went wrong.
|
@BridgeAR It seems to me like rejections have slightly different semantics than throw. Since it's currently possible to just entirely ignore unhandledRejection in our code, I can see a use for doesNotReject. Just IMO of course... |
Sorry, something went wrong.
|
@apapirovski I agree that it is not exactly the same but the reason for that is that we have another issue that has to be solved as well. See #15126. |
Sorry, something went wrong.
|
@BridgeAR Yeah, I'm aware and have dug around in that PR, actually, but that code will take a while to land given the performance implications. I'm not really certain we have a great path forward that doesn't impact performance. Maybe I'm wrong... (I have some thoughts on how to do it without all of the C++ boundary crossing but only theoretical atm.) |
Sorry, something went wrong.
|
@BridgeAR given that this is only semver-minor whereas any changes to uncaught Promise handling would be semver-major, do you think it would be ok to land as is? Especially in the light of the recent rejection of the doesNotThrow deprecation? |
Sorry, something went wrong.
Sorry, something went wrong.
|
@apapirovski yes, I am now ok with landing it as is. I will open another PR to add a comment to it similar to doesNotThrow afterwards. @feugy thanks a lot for your work and for being patient :) |
Sorry, something went wrong.
|
It's all fine! |
Sorry, something went wrong.
Implement asynchronous equivalent for assert.throws() and assert.doesNotThrow().
|
And done! What's next? |
Sorry, something went wrong.
Implement asynchronous equivalent for assert.throws() and assert.doesNotThrow(). PR-URL: nodejs#18023 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Shingo Inoue <leko.noor@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
|
Landed in 599337f 🎉 I removed the experimental as I do not really see that and fixed a typo in the docs while landing. |
Sorry, something went wrong.
|
Should this be backported to v9.x-staging? If yes please follow the guide and raise a backport PR, if not let me know or add the dont-land-on label. |
Sorry, something went wrong.
|
If this gets backported, it should be backported at the same time as #19650 (possibly in the same commit). |
Sorry, something went wrong.
Implement asynchronous equivalent for assert.throws() and assert.doesNotThrow(). PR-URL: nodejs#18023 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Shingo Inoue <leko.noor@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Implement asynchronous equivalent for assert.throws() and assert.doesNotThrow(). PR-URL: nodejs#18023 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Shingo Inoue <leko.noor@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Implement asynchronous equivalent for assert.throws() and assert.doesNotThrow(). PR-URL: nodejs#18023 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Shingo Inoue <leko.noor@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Implement asynchronous equivalent for assert.throws() and assert.doesNotThrow(). Backport-PR-URL: #24019 PR-URL: #18023 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Shingo Inoue <leko.noor@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
| Back | FazBrowse Home | New Git URL |
Implement async equivalent of assert.throws() and assert.doesNotThrow().
This PR is a follow-up of #17843, but takes into account James' suggestion to bring this as experimental feature, without change the existing API.
Checklist
Affected core subsystem(s)
assert