feat(linter): add eslint(jest/valid-expect) rule (#941)

This commit is contained in:
Wenzhe Wang 2023-10-04 22:06:12 -05:00 committed by GitHub
parent 5b1e1e5408
commit 812baeb217
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 1395 additions and 59 deletions

View file

@ -96,23 +96,17 @@ pub fn parse_jest_fn_call<'a>(
// If bailed out, we're not jest function
let resolved = resolve_to_jest_fn(call_expr, ctx)?;
// only the top level Call expression callee's parent is None, it's not necessary to set it to None, but
// I didn't know how to pass Expression to it.
let chain = get_node_chain(callee, None);
let all_member_expr_except_last = chain
.iter()
.rev()
.skip(1)
.all(|member| matches!(member.parent, Some(Expression::MemberExpression(_))));
// Ensure that we're at the "top" of the function call chain otherwise when
// parsing e.g. x().y.z(), we'll incorrectly find & parse "x()" even though
// the full chain is not a valid jest function call chain
if ctx.nodes().parent_node(node.id()).is_some_and(|parent_node| {
matches!(parent_node.kind(), AstKind::CallExpression(_) | AstKind::MemberExpression(_))
}) {
return None;
}
let params = NodeChainParams {
expr: callee,
parent: None, // TODO: not really know how to convert type of call_expr to Expression, set to `None` temporarily.
parent_kind: Some(KnownMemberExpressionParentKind::Call),
grandparent_kind: None,
};
let chain = get_node_chain(&params);
let all_member_expr_except_last =
chain.iter().rev().skip(1).all(|member| {
matches!(member.parent_kind, Some(KnownMemberExpressionParentKind::Member))
});
if let Some(last) = chain.last() {
// If we're an `each()`, ensure we're the outer CallExpression (i.e `.each()()`)
@ -144,7 +138,17 @@ pub fn parse_jest_fn_call<'a>(
}
if matches!(kind, JestFnKind::Expect) {
return parse_jest_expect_fn_call(call_expr, members, name, head);
let options = ExpectFnCallOptions { call_expr, members, name, head, node, ctx };
return parse_jest_expect_fn_call(options);
}
// Ensure that we're at the "top" of the function call chain otherwise when
// parsing e.g. x().y.z(), we'll incorrectly find & parse "x()" even though
// the full chain is not a valid jest function call chain
if ctx.nodes().parent_node(node.id()).is_some_and(|parent_node| {
matches!(parent_node.kind(), AstKind::CallExpression(_) | AstKind::MemberExpression(_))
}) {
return None;
}
// Check every link in the chain except the last is a member expression
@ -168,21 +172,59 @@ pub fn parse_jest_fn_call<'a>(
None
}
fn parse_jest_expect_fn_call<'a>(
fn is_top_most_call_expr<'a, 'b>(node: &'b AstNode<'a>, ctx: &'b LintContext<'a>) -> bool {
let mut node = node;
loop {
let Some(parent) = ctx.nodes().parent_node(node.id()) else { return true };
match parent.kind() {
AstKind::CallExpression(_) => return false,
AstKind::MemberExpression(_) => node = parent,
_ => {
return true;
}
}
}
}
#[derive(Clone, Copy, Debug)]
pub enum ExpectError {
ModifierUnknown,
MatcherNotFound,
MatcherNotCalled,
}
struct ExpectFnCallOptions<'a, 'b> {
call_expr: &'a CallExpression<'a>,
members: Vec<KnownMemberExpressionProperty<'a>>,
name: &'a str,
head: KnownMemberExpressionProperty<'a>,
node: &'b AstNode<'a>,
ctx: &'b LintContext<'a>,
}
fn parse_jest_expect_fn_call<'a>(
options: ExpectFnCallOptions<'a, '_>,
) -> Option<ParsedJestFnCall<'a>> {
// check if the `member` is being called, which means it is the matcher
let has_matcher = match &call_expr.callee {
Expression::MemberExpression(member_expr) => member_expr.static_property_name().is_some(),
_ => false,
let ExpectFnCallOptions { call_expr, members, name, head, node, ctx } = options;
let (modifiers, matcher, mut expect_error) = match find_modifiers_and_matcher(&members) {
Ok((modifier, matcher)) => (modifier, matcher, None),
Err(e) => (vec![], None, Some(e)),
};
let Some((modifiers, matcher)) = find_modifiers_and_matcher(&members, has_matcher) else {
// if the `expect` call chain is not valid, only report on the topmost node
// since all members in the chain are likely to get flagged for some reason
if expect_error.is_some() && !is_top_most_call_expr(node, ctx) {
return None;
};
}
if matches!(expect_error, Some(ExpectError::MatcherNotFound)) {
let parent = ctx.nodes().parent_node(node.id())?;
if matches!(parent.kind(), AstKind::MemberExpression(_)) {
expect_error = Some(ExpectError::MatcherNotCalled);
}
}
return Some(ParsedJestFnCall::ExpectFnCall(ParsedExpectFnCall {
kind: JestFnKind::Expect,
@ -192,25 +234,25 @@ fn parse_jest_expect_fn_call<'a>(
args: &call_expr.arguments,
matcher_index: matcher,
modifier_indices: modifiers,
expect_error,
}));
}
type ModifiersAndMatcherIndex = (Vec<usize>, Option<usize>);
fn find_modifiers_and_matcher(
members: &[KnownMemberExpressionProperty],
has_matcher: bool,
) -> Option<(Vec<usize>, Option<usize>)> {
let mut matcher = None;
) -> Result<ModifiersAndMatcherIndex, ExpectError> {
let mut modifiers: Vec<usize> = vec![];
// matcher is the end of the entire "expect" call chain
if has_matcher {
matcher = Some(members.len() - 1);
}
for (index, member) in members.iter().enumerate() {
// the last member is the matcher, so we can stop here
if index == members.len() - 1 {
break;
// check if the member is being called, which means it is the matcher
// (and also the end of the entire "expect" call chain)
if matches!(member.parent_kind, Some(KnownMemberExpressionParentKind::Member))
&& matches!(member.grandparent_kind, Some(KnownMemberExpressionParentKind::Call))
{
let matcher = Some(index);
return Ok((modifiers, matcher));
}
// the first modifier can be any of the three modifiers
@ -220,30 +262,27 @@ fn find_modifiers_and_matcher(
ModifierName::Resolves,
ModifierName::Rejects,
]) {
return None;
return Err(ExpectError::ModifierUnknown);
}
} else if modifiers.len() == 1 {
// the second modifier can only be "not"
if !member.is_name_in_modifiers(&[ModifierName::Not]) {
return None;
return Err(ExpectError::ModifierUnknown);
}
// and the first modifier has to be either "resolves" or "rejects"
if !members[modifiers[0]]
.is_name_in_modifiers(&[ModifierName::Resolves, ModifierName::Rejects])
{
return None;
return Err(ExpectError::ModifierUnknown);
}
} else {
// the third modifier can only be "resolves" or "rejects"
if !member.is_name_in_modifiers(&[ModifierName::Resolves, ModifierName::Rejects]) {
return None;
}
return Err(ExpectError::ModifierUnknown);
}
modifiers.push(index);
}
Some((modifiers, matcher))
Err(ExpectError::MatcherNotFound)
}
#[derive(PartialEq, Eq)]
@ -419,6 +458,7 @@ pub struct ParsedExpectFnCall<'a> {
// In `expect(1).toBe(2)`, "toBe" will be matcher
// it save the matcher index from members
matcher_index: Option<usize>,
pub expect_error: Option<ExpectError>,
}
impl<'a> ParsedExpectFnCall<'a> {
@ -426,6 +466,9 @@ impl<'a> ParsedExpectFnCall<'a> {
let matcher_index = self.matcher_index?;
self.members.get(matcher_index)
}
pub fn modifiers(&self) -> Vec<&KnownMemberExpressionProperty<'a>> {
self.modifier_indices.iter().filter_map(|i| self.members.get(*i)).collect::<Vec<_>>()
}
}
struct ResolvedJestFn<'a> {
@ -440,9 +483,18 @@ pub enum JestFnFrom {
Import,
}
#[derive(Clone, Copy, Debug)]
pub enum KnownMemberExpressionParentKind {
Member,
Call,
TaggedTemplate,
}
pub struct KnownMemberExpressionProperty<'a> {
pub element: MemberExpressionElement<'a>,
pub parent: Option<&'a Expression<'a>>,
pub parent_kind: Option<KnownMemberExpressionParentKind>,
pub grandparent_kind: Option<KnownMemberExpressionParentKind>,
pub span: Span,
}
@ -547,46 +599,83 @@ pub fn get_node_name_vec<'a>(expr: &'a Expression<'a>) -> Vec<Cow<'a, str>> {
chain
}
/// Port from [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest/blob/a058f22f94774eeea7980ea2d1f24c6808bf3e2c/src/rules/utils/parseJestFnCall.ts#L36-L51)
fn get_node_chain<'a>(
struct NodeChainParams<'a> {
expr: &'a Expression<'a>,
parent: Option<&'a Expression<'a>>,
) -> Vec<KnownMemberExpressionProperty<'a>> {
parent_kind: Option<KnownMemberExpressionParentKind>,
grandparent_kind: Option<KnownMemberExpressionParentKind>,
}
/// Port from [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest/blob/a058f22f94774eeea7980ea2d1f24c6808bf3e2c/src/rules/utils/parseJestFnCall.ts#L36-L51)
fn get_node_chain<'a>(params: &NodeChainParams<'a>) -> Vec<KnownMemberExpressionProperty<'a>> {
let mut chain = Vec::new();
let NodeChainParams { expr, parent, parent_kind, grandparent_kind } = params;
match expr {
Expression::MemberExpression(member_expr) => {
chain.extend(get_node_chain(member_expr.object(), Some(expr)));
let params = NodeChainParams {
expr: member_expr.object(),
parent: Some(expr),
parent_kind: Some(KnownMemberExpressionParentKind::Member),
grandparent_kind: *parent_kind,
};
chain.extend(get_node_chain(&params));
if let Some((span, element)) = MemberExpressionElement::from_member_expr(member_expr) {
chain.push(KnownMemberExpressionProperty { element, parent: Some(expr), span });
chain.push(KnownMemberExpressionProperty {
element,
parent: Some(expr),
parent_kind: Some(KnownMemberExpressionParentKind::Member),
grandparent_kind: *parent_kind,
span,
});
}
}
Expression::Identifier(ident) => {
chain.push(KnownMemberExpressionProperty {
element: MemberExpressionElement::Expression(expr),
parent,
parent: *parent,
parent_kind: *parent_kind,
grandparent_kind: *grandparent_kind,
span: ident.span,
});
}
Expression::CallExpression(call_expr) => {
let sub_chain = get_node_chain(&call_expr.callee, Some(expr));
let params = NodeChainParams {
expr: &call_expr.callee,
parent: Some(expr),
parent_kind: Some(KnownMemberExpressionParentKind::Call),
grandparent_kind: *parent_kind,
};
let sub_chain = get_node_chain(&params);
chain.extend(sub_chain);
}
Expression::TaggedTemplateExpression(tagged_expr) => {
let sub_chain = get_node_chain(&tagged_expr.tag, Some(expr));
let params = NodeChainParams {
expr: &tagged_expr.tag,
parent: Some(expr),
parent_kind: Some(KnownMemberExpressionParentKind::TaggedTemplate),
grandparent_kind: *parent_kind,
};
let sub_chain = get_node_chain(&params);
chain.extend(sub_chain);
}
Expression::StringLiteral(string_literal) => {
chain.push(KnownMemberExpressionProperty {
element: MemberExpressionElement::Expression(expr),
parent,
parent: *parent,
parent_kind: *parent_kind,
grandparent_kind: *grandparent_kind,
span: string_literal.span,
});
}
Expression::TemplateLiteral(template_literal) if is_pure_string(template_literal) => {
chain.push(KnownMemberExpressionProperty {
element: MemberExpressionElement::Expression(expr),
parent,
parent: *parent,
parent_kind: *parent_kind,
grandparent_kind: *grandparent_kind,
span: template_literal.span,
});
}

View file

@ -117,6 +117,7 @@ mod jest {
pub mod no_standalone_expect;
pub mod no_test_prefixes;
pub mod valid_describe_callback;
pub mod valid_expect;
}
mod unicorn {
@ -210,6 +211,7 @@ oxc_macros::declare_all_lint_rules! {
jest::no_test_prefixes,
jest::no_focused_tests,
jest::valid_describe_callback,
jest::valid_expect,
jest::no_commented_out_tests,
jest::expect_expect,
jest::no_alias_methods,

View file

@ -1,4 +1,4 @@
use oxc_ast::{ast::Expression, AstKind};
use oxc_ast::AstKind;
use oxc_diagnostics::{
miette::{self, Diagnostic},
thiserror::Error,
@ -10,7 +10,7 @@ use crate::{
context::LintContext,
jest_ast_util::{
get_node_name, parse_expect_jest_fn_call, parse_general_jest_fn_call, JestFnKind,
JestGeneralFnKind, ParsedExpectFnCall,
JestGeneralFnKind, KnownMemberExpressionParentKind, ParsedExpectFnCall,
},
rule::Rule,
AstNode,
@ -73,10 +73,9 @@ impl Rule for NoStandaloneExpect {
if members.len() == 1
&& members[0].is_name_unequal("assertions")
&& members[0].is_name_unequal("hasAssertions")
&& matches!(head.parent_kind, Some(KnownMemberExpressionParentKind::Member))
{
if let Some(Expression::MemberExpression(_)) = head.parent {
return;
}
return;
}
if is_correct_place_to_call_expect(node, ctx, &self.additional_test_block_functions)

View file

@ -0,0 +1,744 @@
use oxc_ast::{
ast::{Expression, MemberExpression},
AstKind,
};
use oxc_diagnostics::{
miette::{self, Diagnostic},
thiserror::Error,
};
use oxc_macros::declare_oxc_lint;
use oxc_span::{Atom, GetSpan, Span};
use crate::{
context::LintContext,
jest_ast_util::{parse_expect_jest_fn_call, ExpectError},
rule::Rule,
AstNode,
};
#[derive(Debug, Error, Diagnostic)]
#[error("eslint(jest/valid-expect): {0:?}")]
#[diagnostic(severity(warning), help("{1:?}"))]
struct ValidExpectDiagnostic(pub Atom, pub &'static str, #[label] pub Span);
#[derive(Debug, Clone)]
pub struct ValidExpect {
async_matchers: Vec<String>,
min_args: usize,
max_args: usize,
always_await: bool,
}
impl Default for ValidExpect {
fn default() -> Self {
Self {
async_matchers: vec![String::from("toResolve"), String::from("toReject")],
min_args: 1,
max_args: 1,
always_await: false,
}
}
}
declare_oxc_lint!(
/// ### What it does
///
/// This rule triggers a warning if `expect()` is called with more than one argument
/// or without arguments. It would also issue a warning if there is nothing called
/// on `expect()`, e.g.:
///
/// ### Example
/// ```javascript
/// expect();
/// expect('something');
/// expect(true).toBeDefined;
/// expect(Promise.resolve('Hi!')).resolves.toBe('Hi!');
/// ```
ValidExpect,
restriction
);
impl Rule for ValidExpect {
fn from_configuration(value: serde_json::Value) -> Self {
let default_async_matchers = vec![String::from("toResolve"), String::from("toReject")];
let config = value.get(0);
let async_matchers = config
.and_then(|config| config.get("asyncMatchers"))
.and_then(serde_json::Value::as_array)
.map_or(default_async_matchers, |v| {
v.iter().filter_map(serde_json::Value::as_str).map(String::from).collect()
});
let min_args = config
.and_then(|config| config.get("minArgs"))
.and_then(serde_json::Value::as_number)
.and_then(serde_json::Number::as_u64)
.map_or(1, |v| usize::try_from(v).unwrap_or(1));
let max_args = config
.and_then(|config| config.get("maxArgs"))
.and_then(serde_json::Value::as_number)
.and_then(serde_json::Number::as_u64)
.map_or(1, |v| usize::try_from(v).unwrap_or(1));
let always_await = config
.and_then(|config| config.get("alwaysAwait"))
.and_then(serde_json::Value::as_bool)
.unwrap_or(false);
Self { async_matchers, min_args, max_args, always_await }
}
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
let AstKind::CallExpression(call_expr) = node.kind() else { return };
let Some(jest_fn_call) = parse_expect_jest_fn_call(call_expr, node, ctx) else { return };
let reporting_span = jest_fn_call.expect_error.map_or(call_expr.span, |_| {
find_top_most_member_expression(node, ctx).map_or(call_expr.span, GetSpan::span)
});
match jest_fn_call.expect_error {
Some(ExpectError::MatcherNotFound) => {
let (error, help) = Message::MatcherNotFound.details();
ctx.diagnostic(ValidExpectDiagnostic(error, help, reporting_span));
return;
}
Some(ExpectError::MatcherNotCalled) => {
let (error, help) = Message::MatcherNotCalled.details();
ctx.diagnostic(ValidExpectDiagnostic(error, help, reporting_span));
return;
}
Some(ExpectError::ModifierUnknown) => {
let (error, help) = Message::ModifierUnknown.details();
ctx.diagnostic(ValidExpectDiagnostic(error, help, reporting_span));
return;
}
None => {}
}
let Some(Expression::CallExpression(call_expr)) = jest_fn_call.head.parent else { return };
if call_expr.arguments.len() < self.min_args {
let error = Atom::from(format!(
"Expect takes at most {} argument{} ",
self.min_args,
if self.min_args > 1 { "s" } else { "" }
));
let help = "Remove the extra arguments.";
ctx.diagnostic(ValidExpectDiagnostic(error, help, call_expr.span));
return;
}
if call_expr.arguments.len() > self.max_args {
let error = Atom::from(format!(
"Expect requires at least {} argument{} ",
self.max_args,
if self.max_args > 1 { "s" } else { "" }
));
let help = "Add the missing arguments.";
ctx.diagnostic(ValidExpectDiagnostic(error, help, call_expr.span));
return;
}
let Some(matcher) = jest_fn_call.matcher() else {
return;
};
let Some(matcher_name) = matcher.name() else {
return;
};
let Some(parent) = ctx.nodes().parent_node(node.id()) else {
return;
};
let should_be_awaited =
jest_fn_call.modifiers().iter().any(|modifier| modifier.is_name_unequal("not"))
|| self.async_matchers.contains(&matcher_name.to_string());
if ctx.nodes().parent_node(parent.id()).is_none() || !should_be_awaited {
return;
}
// An async assertion can be chained with `then` or `catch` statements.
// In that case our target CallExpression node is the one with
// the last `then` or `catch` statement.
let target_node = get_parent_if_thenable(node, ctx);
let Some(final_node) = find_promise_call_expression_node(node, ctx, target_node) else {
return;
};
let Some(parent) = ctx.nodes().parent_node(final_node.id()) else { return };
if !is_acceptable_return_node(parent, !self.always_await, ctx) {
let span;
let (error, help) = if target_node.id() == final_node.id() {
let AstKind::CallExpression(call_expr) = target_node.kind() else { return };
span = call_expr.span;
Message::AsyncMustBeAwaited.details()
} else {
let AstKind::CallExpression(call_expr) = final_node.kind() else { return };
span = call_expr.span;
Message::PromisesWithAsyncAssertionsMustBeAwaited.details()
};
ctx.diagnostic(ValidExpectDiagnostic(error, help, span));
}
}
}
fn find_top_most_member_expression<'a, 'b>(
node: &'b AstNode<'a>,
ctx: &'b LintContext<'a>,
) -> Option<&'b MemberExpression<'a>> {
let mut top_most_member_expression = None;
let mut node = node;
loop {
let parent = ctx.nodes().parent_node(node.id())?;
match node.kind() {
AstKind::MemberExpression(member_expr) => {
top_most_member_expression = Some(member_expr);
}
_ => {
if !matches!(parent.kind(), AstKind::MemberExpression(_)) {
break;
}
}
}
node = parent;
}
top_most_member_expression
}
fn is_acceptable_return_node<'a, 'b>(
node: &'b AstNode<'a>,
allow_return: bool,
ctx: &'b LintContext<'a>,
) -> bool {
let mut node = node;
loop {
if allow_return && matches!(node.kind(), AstKind::ReturnStatement(_)) {
return true;
}
match node.kind() {
AstKind::ConditionalExpression(_)
| AstKind::Argument(_)
| AstKind::ExpressionStatement(_)
| AstKind::FunctionBody(_) => {
let Some(parent) = ctx.nodes().parent_node(node.id()) else { return false };
node = parent;
}
AstKind::ArrowExpression(arrow_expr) => return arrow_expr.expression,
AstKind::AwaitExpression(_) => return true,
_ => return false,
}
}
}
type ParentAndIsFirstItem<'a, 'b> = (&'b AstNode<'a>, bool);
// Returns the parent node of the given node, ignoring some nodes,
// and return whether the first item if parent is an array.
fn get_parent_with_ignore<'a, 'b>(
node: &'b AstNode<'a>,
ctx: &'b LintContext<'a>,
) -> Option<ParentAndIsFirstItem<'a, 'b>> {
let mut node = node;
loop {
let parent = ctx.nodes().parent_node(node.id())?;
if !matches!(
parent.kind(),
AstKind::Argument(_)
| AstKind::ExpressionArrayElement(_)
| AstKind::ArrayExpressionElement(_)
) {
// we don't want to report `Promise.all([invalidExpectCall_1, invalidExpectCall_2])` twice.
// so we need mark whether the node is the first item of an array.
// if it not the first item, we ignore it in `find_promise_call_expression_node`.
if let AstKind::ArrayExpressionElement(array_expr_element) = node.kind() {
if let AstKind::ArrayExpression(array_expr) = parent.kind() {
return Some((
parent,
array_expr.elements.first()?.span() == array_expr_element.span(),
));
}
}
// if parent is not an array, we assume it's the first item
return Some((parent, true));
}
node = parent;
}
}
fn find_promise_call_expression_node<'a, 'b>(
node: &'b AstNode<'a>,
ctx: &'b LintContext<'a>,
default_node: &'b AstNode<'a>,
) -> Option<&'b AstNode<'a>> {
let Some((mut parent, is_first_array_item)) = get_parent_with_ignore(node, ctx) else {
return Some(default_node);
};
if !matches!(parent.kind(), AstKind::CallExpression(_) | AstKind::ArrayExpression(_)) {
return Some(default_node);
}
let Some((grandparent, _)) = get_parent_with_ignore(parent, ctx) else {
return Some(default_node);
};
if matches!(parent.kind(), AstKind::ArrayExpression(_))
&& matches!(grandparent.kind(), AstKind::CallExpression(_))
{
parent = grandparent;
}
if let AstKind::CallExpression(call_expr) = parent.kind() {
if let Expression::MemberExpression(member_expr) = &call_expr.callee {
if let Expression::Identifier(ident) = member_expr.object() {
if matches!(ident.name.as_str(), "Promise")
&& ctx.nodes().parent_node(parent.id()).is_some()
{
if is_first_array_item {
return Some(parent);
}
return None;
}
}
}
}
Some(default_node)
}
fn get_parent_if_thenable<'a, 'b>(
node: &'b AstNode<'a>,
ctx: &'b LintContext<'a>,
) -> &'b AstNode<'a> {
let grandparent =
ctx.nodes().parent_node(node.id()).and_then(|node| ctx.nodes().parent_node(node.id()));
let Some(grandparent) = grandparent else { return node };
let AstKind::CallExpression(call_expr) = grandparent.kind() else { return node };
let Expression::MemberExpression(member_expr) = &call_expr.callee else { return node };
let Some(name) = member_expr.static_property_name() else { return node };
if ["then", "catch"].contains(&name) {
return get_parent_if_thenable(grandparent, ctx);
}
node
}
#[derive(Clone, Copy)]
enum Message {
MatcherNotFound,
MatcherNotCalled,
ModifierUnknown,
AsyncMustBeAwaited,
PromisesWithAsyncAssertionsMustBeAwaited,
}
impl Message {
fn details(self) -> (Atom, &'static str) {
match self {
Self::MatcherNotFound => (
Atom::from("Expect must have a corresponding matcher call."),
"Did you forget add a matcher(e.g. `toBe`, `toBeDefined`)",
),
Self::MatcherNotCalled => (
Atom::from("Matchers must be called to assert."),
"You need call your matcher, e.g. `expect(true).toBe(true)`.",
),
Self::ModifierUnknown => {
(Atom::from("Expect has an unknown modifier."), "Is it a spelling mistake?")
}
Self::AsyncMustBeAwaited => {
(Atom::from("Async assertions must be awaited."), "Add `await` to your assertion.")
}
Self::PromisesWithAsyncAssertionsMustBeAwaited => (
Atom::from("Promises which return async assertions must be awaited."),
"Add `await` to your assertion.",
),
}
}
}
#[ignore]
#[test]
fn test_1() {
use crate::tester::Tester;
let pass = vec![
("test('valid-expect', async () => { await Promise.race([expect(Promise.reject(2)).rejects.not.toBeDefined(), expect(Promise.reject(2)).rejects.not.toBeDefined()]); })", None)
];
let fail = vec![];
Tester::new(ValidExpect::NAME, pass, fail).test_and_snapshot();
}
#[allow(clippy::too_many_lines)]
#[test]
fn test() {
use crate::tester::Tester;
let pass = vec![
("expect.hasAssertions", None),
("expect.hasAssertions()", None),
("expect('something').toEqual('else');", None),
("expect(true).toBeDefined();", None),
("expect([1, 2, 3]).toEqual([1, 2, 3]);", None),
("expect(undefined).not.toBeDefined();", None),
("test('valid-expect', () => { return expect(Promise.resolve(2)).resolves.toBeDefined(); });", None),
("test('valid-expect', () => { return expect(Promise.reject(2)).rejects.toBeDefined(); });", None),
("test('valid-expect', () => { return expect(Promise.resolve(2)).resolves.not.toBeDefined(); });", None),
("test('valid-expect', () => { return expect(Promise.resolve(2)).rejects.not.toBeDefined(); });", None),
("test('valid-expect', function () { return expect(Promise.resolve(2)).resolves.not.toBeDefined(); });", None),
("test('valid-expect', function () { return expect(Promise.resolve(2)).rejects.not.toBeDefined(); });", None),
("test('valid-expect', function () { return Promise.resolve(expect(Promise.resolve(2)).resolves.not.toBeDefined()); });", None),
("test('valid-expect', function () { return Promise.resolve(expect(Promise.resolve(2)).rejects.not.toBeDefined()); });", None),
("test('valid-expect', () => expect(Promise.resolve(2)).resolves.toBeDefined());", Some(serde_json::json!([{ "alwaysAwait": true }]))),
("test('valid-expect', () => expect(Promise.resolve(2)).resolves.toBeDefined());", None),
("test('valid-expect', () => expect(Promise.reject(2)).rejects.toBeDefined());", None),
("test('valid-expect', () => expect(Promise.reject(2)).resolves.not.toBeDefined());", None),
("test('valid-expect', () => expect(Promise.reject(2)).rejects.not.toBeDefined());", None),
("test('valid-expect', async () => { await expect(Promise.reject(2)).resolves.not.toBeDefined(); });", None),
("test('valid-expect', async () => { await expect(Promise.reject(2)).rejects.not.toBeDefined(); });", None),
("test('valid-expect', async function () { await expect(Promise.reject(2)).resolves.not.toBeDefined(); });", None),
("test('valid-expect', async function () { await expect(Promise.reject(2)).rejects.not.toBeDefined(); });", None),
("test('valid-expect', async () => { await Promise.resolve(expect(Promise.reject(2)).rejects.not.toBeDefined()); });", None),
("test('valid-expect', async () => { await Promise.reject(expect(Promise.reject(2)).rejects.not.toBeDefined()); });", None),
("test('valid-expect', async () => { await Promise.all([expect(Promise.reject(2)).rejects.not.toBeDefined(), expect(Promise.reject(2)).rejects.not.toBeDefined()]); });", None),
("test('valid-expect', async () => { await Promise.race([expect(Promise.reject(2)).rejects.not.toBeDefined(), expect(Promise.reject(2)).rejects.not.toBeDefined()]); });", None),
("test('valid-expect', async () => { await Promise.allSettled([expect(Promise.reject(2)).rejects.not.toBeDefined(), expect(Promise.reject(2)).rejects.not.toBeDefined()]); });", None),
("test('valid-expect', async () => { await Promise.any([expect(Promise.reject(2)).rejects.not.toBeDefined(), expect(Promise.reject(2)).rejects.not.toBeDefined()]); });", None),
("test('valid-expect', async () => { return expect(Promise.reject(2)).resolves.not.toBeDefined().then(() => console.log('valid-case')); });", None),
("test('valid-expect', async () => { return expect(Promise.reject(2)).resolves.not.toBeDefined().then(() => console.log('valid-case')).then(() => console.log('another valid case')); });", None),
("test('valid-expect', async () => { return expect(Promise.reject(2)).resolves.not.toBeDefined().catch(() => console.log('valid-case')); });", None),
("test('valid-expect', async () => { return expect(Promise.reject(2)).resolves.not.toBeDefined().then(() => console.log('valid-case')).catch(() => console.log('another valid case')); });", None),
("test('valid-expect', async () => { return expect(Promise.reject(2)).resolves.not.toBeDefined().then(() => { expect(someMock).toHaveBeenCalledTimes(1); }); });", None),
("test('valid-expect', async () => { await expect(Promise.reject(2)).resolves.not.toBeDefined().then(() => console.log('valid-case')); });", None),
("test('valid-expect', async () => { await expect(Promise.reject(2)).resolves.not.toBeDefined().then(() => console.log('valid-case')).then(() => console.log('another valid case')); });", None),
("test('valid-expect', async () => { await expect(Promise.reject(2)).resolves.not.toBeDefined().catch(() => console.log('valid-case')); });", None),
("test('valid-expect', async () => { await expect(Promise.reject(2)).resolves.not.toBeDefined().then(() => console.log('valid-case')).catch(() => console.log('another valid case')); });", None),
("test('valid-expect', async () => { await expect(Promise.reject(2)).resolves.not.toBeDefined().then(() => { expect(someMock).toHaveBeenCalledTimes(1); }); });", None),
(
"
test('valid-expect', () => {
return expect(functionReturningAPromise()).resolves.toEqual(1).then(() => {
return expect(Promise.resolve(2)).resolves.toBe(1);
});
});
",
None
),
(
"
test('valid-expect', () => {
return expect(functionReturningAPromise()).resolves.toEqual(1).then(async () => {
await expect(Promise.resolve(2)).resolves.toBe(1);
});
});
",
None
),
(
"
test('valid-expect', () => {
return expect(functionReturningAPromise()).resolves.toEqual(1).then(() => expect(Promise.resolve(2)).resolves.toBe(1));
});
",
None
),
(
"
expect.extend({
toResolve(obj) {
return this.isNot
? expect(obj).toBe(true)
: expect(obj).resolves.not.toThrow();
}
});
",
None
),
(
"
expect.extend({
toResolve(obj) {
return this.isNot
? expect(obj).resolves.not.toThrow()
: expect(obj).toBe(true);
}
});
",
None
),
(
"
expect.extend({
toResolve(obj) {
return this.isNot
? expect(obj).toBe(true)
: anotherCondition
? expect(obj).resolves.not.toThrow()
: expect(obj).toBe(false)
}
});
",
None
),
("expect(1).toBe(2);", Some(serde_json::json!([{ "maxArgs": 2 }]))),
("expect(1, '1 !== 2').toBe(2);", Some(serde_json::json!([{ "maxArgs": 2 }]))),
("expect(1, '1 !== 2').toBe(2);", Some(serde_json::json!([{ "maxArgs": 2, "minArgs": 2 }]))),
("test('valid-expect', () => { expect(2).not.toBe(2); });", Some(serde_json::json!([{ "asyncMatchers": ["toRejectWith"] }]))),
("test('valid-expect', () => { expect(Promise.reject(2)).toRejectWith(2); });", Some(serde_json::json!([{ "asyncMatchers": ["toResolveWith"] }]))),
("test('valid-expect', async () => { await expect(Promise.resolve(2)).toResolve(); });", Some(serde_json::json!([{ "asyncMatchers": ["toResolveWith"] }]))),
("test('valid-expect', async () => { expect(Promise.resolve(2)).toResolve(); });", Some(serde_json::json!([{ "asyncMatchers": ["toResolveWith"] }])))
];
let fail = vec![
("expect().toBe(2);", None),
("expect().toBe(true);", None),
("expect().toEqual('something');", None),
("expect('something', 'else').toEqual('something');", None),
("expect('something', 'else', 'entirely').toEqual('something');", Some(serde_json::json!([{ "maxArgs": 2 }]))),
("expect('something', 'else', 'entirely').toEqual('something');", Some(serde_json::json!([{ "maxArgs": 2, "minArgs": 2 }]))),
("expect('something', 'else', 'entirely').toEqual('something');", Some(serde_json::json!([{ "maxArgs": 2, "minArgs": 1 }]))),
("expect('something').toEqual('something');", Some(serde_json::json!([{ "minArgs": 2 }]))),
("expect('something', 'else').toEqual('something');", Some(serde_json::json!([{ "maxArgs": 1, "minArgs": 3 }]))),
("expect('something');", None),
("expect();", None),
("expect(true).toBeDefined;", None),
("expect(true).not.toBeDefined;", None),
("expect(true).nope.toBeDefined;", None),
("expect(true).nope.toBeDefined();", None),
("expect(true).not.resolves.toBeDefined();", None),
("expect(true).not.not.toBeDefined();", None),
("expect(true).resolves.not.exactly.toBeDefined();", None),
("expect(true).resolves;", None),
("expect(true).rejects;", None),
("expect(true).not;", None),
("expect(Promise.resolve(2)).resolves.toBeDefined();", None),
("expect(Promise.resolve(2)).rejects.toBeDefined();", None),
("expect(Promise.resolve(2)).resolves.toBeDefined();", Some(serde_json::json!([{ "alwaysAwait": true }]))),
(
"
expect.extend({
toResolve(obj) {
this.isNot
? expect(obj).toBe(true)
: expect(obj).resolves.not.toThrow();
}
});
",
None
),
(
"
expect.extend({
toResolve(obj) {
this.isNot
? expect(obj).resolves.not.toThrow()
: expect(obj).toBe(true);
}
});
",
None
),
(
"
expect.extend({
toResolve(obj) {
this.isNot
? expect(obj).toBe(true)
: anotherCondition
? expect(obj).resolves.not.toThrow()
: expect(obj).toBe(false)
}
});
",
None
),
("test('valid-expect', () => { expect(Promise.resolve(2)).resolves.toBeDefined(); });", None),
("test('valid-expect', () => { expect(Promise.resolve(2)).toResolve(); });", None),
("test('valid-expect', () => { expect(Promise.resolve(2)).toResolve(); });", None),
("test('valid-expect', () => { expect(Promise.resolve(2)).toReject(); });", None),
("test('valid-expect', () => { expect(Promise.resolve(2)).not.toReject(); });", None),
("test('valid-expect', () => { expect(Promise.resolve(2)).resolves.not.toBeDefined(); });", None),
("test('valid-expect', () => { expect(Promise.resolve(2)).rejects.toBeDefined(); });", None),
("test('valid-expect', () => { expect(Promise.resolve(2)).rejects.not.toBeDefined(); });", None),
("test('valid-expect', async () => { expect(Promise.resolve(2)).resolves.toBeDefined(); });", None),
("test('valid-expect', async () => { expect(Promise.resolve(2)).resolves.not.toBeDefined(); });", None),
("test('valid-expect', () => { expect(Promise.reject(2)).toRejectWith(2); });", Some(serde_json::json!([{ "asyncMatchers": ["toRejectWith"] }]))),
("test('valid-expect', () => { expect(Promise.reject(2)).rejects.toBe(2); });", Some(serde_json::json!([{ "asyncMatchers": ["toRejectWith"] }]))),
(
"
test('valid-expect', async () => {
expect(Promise.resolve(2)).resolves.not.toBeDefined();
expect(Promise.resolve(1)).rejects.toBeDefined();
});
",
None
),
(
"
test('valid-expect', async () => {
await expect(Promise.resolve(2)).resolves.not.toBeDefined();
expect(Promise.resolve(1)).rejects.toBeDefined();
});
",
None
),
(
"
test('valid-expect', async () => {
expect(Promise.resolve(2)).resolves.not.toBeDefined();
return expect(Promise.resolve(1)).rejects.toBeDefined();
});
",
Some(serde_json::json!([{ "alwaysAwait": true }]))
),
(
"
test('valid-expect', async () => {
expect(Promise.resolve(2)).resolves.not.toBeDefined();
return expect(Promise.resolve(1)).rejects.toBeDefined();
});
",
None
),
(
"
test('valid-expect', async () => {
await expect(Promise.resolve(2)).resolves.not.toBeDefined();
return expect(Promise.resolve(1)).rejects.toBeDefined();
});
",
Some(serde_json::json!([{ "alwaysAwait": true }]))
),
(
"
test('valid-expect', async () => {
await expect(Promise.resolve(2)).toResolve();
return expect(Promise.resolve(1)).toReject();
});
",
Some(serde_json::json!([{ "alwaysAwait": true }]))
),
(
"
test('valid-expect', () => {
Promise.resolve(expect(Promise.resolve(2)).resolves.not.toBeDefined());
});
",
None
),
(
"
test('valid-expect', () => {
Promise.reject(expect(Promise.resolve(2)).resolves.not.toBeDefined());
});
",
None
),
(
"
test('valid-expect', () => {
Promise.x(expect(Promise.resolve(2)).resolves.not.toBeDefined());
});
",
None
),
(
"
test('valid-expect', () => {
Promise.resolve(expect(Promise.resolve(2)).resolves.not.toBeDefined());
});
",
Some(serde_json::json!([{ "alwaysAwait": true }]))
),
(
"
test('valid-expect', () => {
Promise.all([
expect(Promise.resolve(2)).resolves.not.toBeDefined(),
expect(Promise.resolve(3)).resolves.not.toBeDefined(),
]);
});
",
None
),
(
"
test('valid-expect', () => {
Promise.x([
expect(Promise.resolve(2)).resolves.not.toBeDefined(),
expect(Promise.resolve(3)).resolves.not.toBeDefined(),
]);
});
",
None
),
(
"
test('valid-expect', () => {
const assertions = [
expect(Promise.resolve(2)).resolves.not.toBeDefined(),
expect(Promise.resolve(3)).resolves.not.toBeDefined(),
]
});
",
None
),
(
"
test('valid-expect', () => {
const assertions = [
expect(Promise.resolve(2)).toResolve(),
expect(Promise.resolve(3)).toReject(),
]
});
",
None
),
(
"
test('valid-expect', () => {
const assertions = [
expect(Promise.resolve(2)).not.toResolve(),
expect(Promise.resolve(3)).resolves.toReject(),
]
});
",
None
),
("expect(Promise.resolve(2)).resolves.toBe;", None),
(
"
test('valid-expect', () => {
return expect(functionReturningAPromise()).resolves.toEqual(1).then(() => {
expect(Promise.resolve(2)).resolves.toBe(1);
});
});
",
None
),
(
"
test('valid-expect', () => {
return expect(functionReturningAPromise()).resolves.toEqual(1).then(async () => {
await expect(Promise.resolve(2)).resolves.toBe(1);
expect(Promise.resolve(4)).resolves.toBe(4);
});
});
",
None
),
(
"
test('valid-expect', async () => {
await expect(Promise.resolve(1));
});
",
None
)
];
Tester::new(ValidExpect::NAME, pass, fail).test_and_snapshot();
}

View file

@ -0,0 +1,502 @@
---
source: crates/oxc_linter/src/tester.rs
expression: valid_expect
---
⚠ eslint(jest/valid-expect): "Expect takes at most 1 argument "
╭─[valid_expect.tsx:1:1]
1 │ expect().toBe(2);
· ────────
╰────
help: "Remove the extra arguments."
⚠ eslint(jest/valid-expect): "Expect takes at most 1 argument "
╭─[valid_expect.tsx:1:1]
1 │ expect().toBe(true);
· ────────
╰────
help: "Remove the extra arguments."
⚠ eslint(jest/valid-expect): "Expect takes at most 1 argument "
╭─[valid_expect.tsx:1:1]
1 │ expect().toEqual('something');
· ────────
╰────
help: "Remove the extra arguments."
⚠ eslint(jest/valid-expect): "Expect requires at least 1 argument "
╭─[valid_expect.tsx:1:1]
1 │ expect('something', 'else').toEqual('something');
· ───────────────────────────
╰────
help: "Add the missing arguments."
⚠ eslint(jest/valid-expect): "Expect requires at least 2 arguments "
╭─[valid_expect.tsx:1:1]
1 │ expect('something', 'else', 'entirely').toEqual('something');
· ───────────────────────────────────────
╰────
help: "Add the missing arguments."
⚠ eslint(jest/valid-expect): "Expect requires at least 2 arguments "
╭─[valid_expect.tsx:1:1]
1 │ expect('something', 'else', 'entirely').toEqual('something');
· ───────────────────────────────────────
╰────
help: "Add the missing arguments."
⚠ eslint(jest/valid-expect): "Expect requires at least 2 arguments "
╭─[valid_expect.tsx:1:1]
1 │ expect('something', 'else', 'entirely').toEqual('something');
· ───────────────────────────────────────
╰────
help: "Add the missing arguments."
⚠ eslint(jest/valid-expect): "Expect takes at most 2 arguments "
╭─[valid_expect.tsx:1:1]
1 │ expect('something').toEqual('something');
· ───────────────────
╰────
help: "Remove the extra arguments."
⚠ eslint(jest/valid-expect): "Expect takes at most 3 arguments "
╭─[valid_expect.tsx:1:1]
1 │ expect('something', 'else').toEqual('something');
· ───────────────────────────
╰────
help: "Remove the extra arguments."
⚠ eslint(jest/valid-expect): "Expect must have a corresponding matcher call."
╭─[valid_expect.tsx:1:1]
1 │ expect('something');
· ───────────────────
╰────
help: "Did you forget add a matcher(e.g. `toBe`, `toBeDefined`)"
⚠ eslint(jest/valid-expect): "Expect must have a corresponding matcher call."
╭─[valid_expect.tsx:1:1]
1 │ expect();
· ────────
╰────
help: "Did you forget add a matcher(e.g. `toBe`, `toBeDefined`)"
⚠ eslint(jest/valid-expect): "Matchers must be called to assert."
╭─[valid_expect.tsx:1:1]
1 │ expect(true).toBeDefined;
· ────────────────────────
╰────
help: "You need call your matcher, e.g. `expect(true).toBe(true)`."
⚠ eslint(jest/valid-expect): "Matchers must be called to assert."
╭─[valid_expect.tsx:1:1]
1 │ expect(true).not.toBeDefined;
· ────────────────────────────
╰────
help: "You need call your matcher, e.g. `expect(true).toBe(true)`."
⚠ eslint(jest/valid-expect): "Matchers must be called to assert."
╭─[valid_expect.tsx:1:1]
1 │ expect(true).nope.toBeDefined;
· ─────────────────────────────
╰────
help: "You need call your matcher, e.g. `expect(true).toBe(true)`."
⚠ eslint(jest/valid-expect): "Expect has an unknown modifier."
╭─[valid_expect.tsx:1:1]
1 │ expect(true).nope.toBeDefined();
· ───────────────────────────────
╰────
help: "Is it a spelling mistake?"
⚠ eslint(jest/valid-expect): "Expect has an unknown modifier."
╭─[valid_expect.tsx:1:1]
1 │ expect(true).not.resolves.toBeDefined();
· ───────────────────────────────────────
╰────
help: "Is it a spelling mistake?"
⚠ eslint(jest/valid-expect): "Expect has an unknown modifier."
╭─[valid_expect.tsx:1:1]
1 │ expect(true).not.not.toBeDefined();
· ──────────────────────────────────
╰────
help: "Is it a spelling mistake?"
⚠ eslint(jest/valid-expect): "Expect has an unknown modifier."
╭─[valid_expect.tsx:1:1]
1 │ expect(true).resolves.not.exactly.toBeDefined();
· ───────────────────────────────────────────────
╰────
help: "Is it a spelling mistake?"
⚠ eslint(jest/valid-expect): "Matchers must be called to assert."
╭─[valid_expect.tsx:1:1]
1 │ expect(true).resolves;
· ─────────────────────
╰────
help: "You need call your matcher, e.g. `expect(true).toBe(true)`."
⚠ eslint(jest/valid-expect): "Matchers must be called to assert."
╭─[valid_expect.tsx:1:1]
1 │ expect(true).rejects;
· ────────────────────
╰────
help: "You need call your matcher, e.g. `expect(true).toBe(true)`."
⚠ eslint(jest/valid-expect): "Matchers must be called to assert."
╭─[valid_expect.tsx:1:1]
1 │ expect(true).not;
· ────────────────
╰────
help: "You need call your matcher, e.g. `expect(true).toBe(true)`."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:1:1]
1 │ expect(Promise.resolve(2)).resolves.toBeDefined();
· ─────────────────────────────────────────────────
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:1:1]
1 │ expect(Promise.resolve(2)).rejects.toBeDefined();
· ────────────────────────────────────────────────
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:1:1]
1 │ expect(Promise.resolve(2)).resolves.toBeDefined();
· ─────────────────────────────────────────────────
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:5:1]
5 │ ? expect(obj).toBe(true)
6 │ : expect(obj).resolves.not.toThrow();
· ──────────────────────────────────
7 │ }
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:4:1]
4 │ this.isNot
5 │ ? expect(obj).resolves.not.toThrow()
· ──────────────────────────────────
6 │ : expect(obj).toBe(true);
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:6:1]
6 │ : anotherCondition
7 │ ? expect(obj).resolves.not.toThrow()
· ──────────────────────────────────
8 │ : expect(obj).toBe(false)
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:1:1]
1 │ test('valid-expect', () => { expect(Promise.resolve(2)).resolves.toBeDefined(); });
· ─────────────────────────────────────────────────
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:1:1]
1 │ test('valid-expect', () => { expect(Promise.resolve(2)).toResolve(); });
· ──────────────────────────────────────
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:1:1]
1 │ test('valid-expect', () => { expect(Promise.resolve(2)).toResolve(); });
· ──────────────────────────────────────
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:1:1]
1 │ test('valid-expect', () => { expect(Promise.resolve(2)).toReject(); });
· ─────────────────────────────────────
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:1:1]
1 │ test('valid-expect', () => { expect(Promise.resolve(2)).not.toReject(); });
· ─────────────────────────────────────────
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:1:1]
1 │ test('valid-expect', () => { expect(Promise.resolve(2)).resolves.not.toBeDefined(); });
· ─────────────────────────────────────────────────────
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:1:1]
1 │ test('valid-expect', () => { expect(Promise.resolve(2)).rejects.toBeDefined(); });
· ────────────────────────────────────────────────
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:1:1]
1 │ test('valid-expect', () => { expect(Promise.resolve(2)).rejects.not.toBeDefined(); });
· ────────────────────────────────────────────────────
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:1:1]
1 │ test('valid-expect', async () => { expect(Promise.resolve(2)).resolves.toBeDefined(); });
· ─────────────────────────────────────────────────
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:1:1]
1 │ test('valid-expect', async () => { expect(Promise.resolve(2)).resolves.not.toBeDefined(); });
· ─────────────────────────────────────────────────────
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:1:1]
1 │ test('valid-expect', () => { expect(Promise.reject(2)).toRejectWith(2); });
· ─────────────────────────────────────────
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:1:1]
1 │ test('valid-expect', () => { expect(Promise.reject(2)).rejects.toBe(2); });
· ─────────────────────────────────────────
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:2:1]
2 │ test('valid-expect', async () => {
3 │ expect(Promise.resolve(2)).resolves.not.toBeDefined();
· ─────────────────────────────────────────────────────
4 │ expect(Promise.resolve(1)).rejects.toBeDefined();
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:3:1]
3 │ expect(Promise.resolve(2)).resolves.not.toBeDefined();
4 │ expect(Promise.resolve(1)).rejects.toBeDefined();
· ────────────────────────────────────────────────
5 │ });
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:3:1]
3 │ await expect(Promise.resolve(2)).resolves.not.toBeDefined();
4 │ expect(Promise.resolve(1)).rejects.toBeDefined();
· ────────────────────────────────────────────────
5 │ });
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:2:1]
2 │ test('valid-expect', async () => {
3 │ expect(Promise.resolve(2)).resolves.not.toBeDefined();
· ─────────────────────────────────────────────────────
4 │ return expect(Promise.resolve(1)).rejects.toBeDefined();
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:3:1]
3 │ expect(Promise.resolve(2)).resolves.not.toBeDefined();
4 │ return expect(Promise.resolve(1)).rejects.toBeDefined();
· ────────────────────────────────────────────────
5 │ });
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:2:1]
2 │ test('valid-expect', async () => {
3 │ expect(Promise.resolve(2)).resolves.not.toBeDefined();
· ─────────────────────────────────────────────────────
4 │ return expect(Promise.resolve(1)).rejects.toBeDefined();
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:3:1]
3 │ await expect(Promise.resolve(2)).resolves.not.toBeDefined();
4 │ return expect(Promise.resolve(1)).rejects.toBeDefined();
· ────────────────────────────────────────────────
5 │ });
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:3:1]
3 │ await expect(Promise.resolve(2)).toResolve();
4 │ return expect(Promise.resolve(1)).toReject();
· ─────────────────────────────────────
5 │ });
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Promises which return async assertions must be awaited."
╭─[valid_expect.tsx:2:1]
2 │ test('valid-expect', () => {
3 │ Promise.resolve(expect(Promise.resolve(2)).resolves.not.toBeDefined());
· ──────────────────────────────────────────────────────────────────────
4 │ });
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Promises which return async assertions must be awaited."
╭─[valid_expect.tsx:2:1]
2 │ test('valid-expect', () => {
3 │ Promise.reject(expect(Promise.resolve(2)).resolves.not.toBeDefined());
· ─────────────────────────────────────────────────────────────────────
4 │ });
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Promises which return async assertions must be awaited."
╭─[valid_expect.tsx:2:1]
2 │ test('valid-expect', () => {
3 │ Promise.x(expect(Promise.resolve(2)).resolves.not.toBeDefined());
· ────────────────────────────────────────────────────────────────
4 │ });
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Promises which return async assertions must be awaited."
╭─[valid_expect.tsx:2:1]
2 │ test('valid-expect', () => {
3 │ Promise.resolve(expect(Promise.resolve(2)).resolves.not.toBeDefined());
· ──────────────────────────────────────────────────────────────────────
4 │ });
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Promises which return async assertions must be awaited."
╭─[valid_expect.tsx:2:1]
2 │ test('valid-expect', () => {
3 │ ╭─▶ Promise.all([
4 │ │ expect(Promise.resolve(2)).resolves.not.toBeDefined(),
5 │ │ expect(Promise.resolve(3)).resolves.not.toBeDefined(),
6 │ ╰─▶ ]);
7 │ });
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Promises which return async assertions must be awaited."
╭─[valid_expect.tsx:2:1]
2 │ test('valid-expect', () => {
3 │ ╭─▶ Promise.x([
4 │ │ expect(Promise.resolve(2)).resolves.not.toBeDefined(),
5 │ │ expect(Promise.resolve(3)).resolves.not.toBeDefined(),
6 │ ╰─▶ ]);
7 │ });
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:3:1]
3 │ const assertions = [
4 │ expect(Promise.resolve(2)).resolves.not.toBeDefined(),
· ─────────────────────────────────────────────────────
5 │ expect(Promise.resolve(3)).resolves.not.toBeDefined(),
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:4:1]
4 │ expect(Promise.resolve(2)).resolves.not.toBeDefined(),
5 │ expect(Promise.resolve(3)).resolves.not.toBeDefined(),
· ─────────────────────────────────────────────────────
6 │ ]
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:3:1]
3 │ const assertions = [
4 │ expect(Promise.resolve(2)).toResolve(),
· ──────────────────────────────────────
5 │ expect(Promise.resolve(3)).toReject(),
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:4:1]
4 │ expect(Promise.resolve(2)).toResolve(),
5 │ expect(Promise.resolve(3)).toReject(),
· ─────────────────────────────────────
6 │ ]
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:3:1]
3 │ const assertions = [
4 │ expect(Promise.resolve(2)).not.toResolve(),
· ──────────────────────────────────────────
5 │ expect(Promise.resolve(3)).resolves.toReject(),
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:4:1]
4 │ expect(Promise.resolve(2)).not.toResolve(),
5 │ expect(Promise.resolve(3)).resolves.toReject(),
· ──────────────────────────────────────────────
6 │ ]
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Matchers must be called to assert."
╭─[valid_expect.tsx:1:1]
1 │ expect(Promise.resolve(2)).resolves.toBe;
· ────────────────────────────────────────
╰────
help: "You need call your matcher, e.g. `expect(true).toBe(true)`."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:3:1]
3 │ return expect(functionReturningAPromise()).resolves.toEqual(1).then(() => {
4 │ expect(Promise.resolve(2)).resolves.toBe(1);
· ───────────────────────────────────────────
5 │ });
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Async assertions must be awaited."
╭─[valid_expect.tsx:4:1]
4 │ await expect(Promise.resolve(2)).resolves.toBe(1);
5 │ expect(Promise.resolve(4)).resolves.toBe(4);
· ───────────────────────────────────────────
6 │ });
╰────
help: "Add `await` to your assertion."
⚠ eslint(jest/valid-expect): "Expect must have a corresponding matcher call."
╭─[valid_expect.tsx:2:1]
2 │ test('valid-expect', async () => {
3 │ await expect(Promise.resolve(1));
· ──────────────────────────
4 │ });
╰────
help: "Did you forget add a matcher(e.g. `toBe`, `toBeDefined`)"