mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(linter): add eslint(jest/valid-expect) rule (#941)
This commit is contained in:
parent
5b1e1e5408
commit
812baeb217
5 changed files with 1395 additions and 59 deletions
|
|
@ -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(¶ms);
|
||||
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(¶ms));
|
||||
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(¶ms);
|
||||
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(¶ms);
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
744
crates/oxc_linter/src/rules/jest/valid_expect.rs
Normal file
744
crates/oxc_linter/src/rules/jest/valid_expect.rs
Normal 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();
|
||||
}
|
||||
502
crates/oxc_linter/src/snapshots/valid_expect.snap
Normal file
502
crates/oxc_linter/src/snapshots/valid_expect.snap
Normal 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`)"
|
||||
|
||||
|
||||
Loading…
Reference in a new issue