Search code examples
javascripteslintabstract-syntax-tree

Prevent screen.findByX without an await with eslint no-restricted-syntax?


This medium article shows me how to prevent an await preceding a property:

"no-restricted-syntax": [
  "error",
  {
    "message": "promise.then is unnecessary when using async/await",
    "selector": "AwaitExpression[argument.callee.property.name=\" then\"]"
  }
]

But I want the opposite, I want to restrict these:

expect(screen.findByRole(...))....;
screen.findByRole(...);

but allow these:

expect(await screen.findByRole(...))....;
await screen.findByRole(...);

I tried this under my overrides for test files:

"no-restricted-syntax": [
  "error",
  {
    "message": "Find By queries must be followed by an await",
    "selector": ":not(AwaitExpression[argument.callee.property.name=/findBy.*/])"
  }
]

But now every line is showing that error. I also tried putting * in front, hoping it would allow anything (the *) unless is was followed by my banned syntax expression, but no dice.

How can I make this work?


Solution

  • This AST selector seems to work:

    :not(AwaitExpression) > CallExpression[callee.property.name=/^findBy.*$/]

    The selector looks for direct children of non-await expressions that call a method beginning with findBy....

    The whole rule may look like:

    "no-restricted-syntax": [
      "error",
      {
        "message": "findBy... queries must be preceded by an await",
        "selector": ":not(AwaitExpression) > CallExpression[callee.property.name=/^findBy.*$/]"
      }
    ]
    

    Standalone Functions

    For function calls that aren't called by objects (e.g. just findByRole instead of screen.findByRole), you can omit the property nested attribute in the selector, like so:

    :not(AwaitExpression) > CallExpression[callee.name=/^findBy.*$/]

    If you want to match both Standalone functions and Method functions, you can have two separate rules, or combine them with the :matches(...) selector.

    Try it out

    You can try it out here on the ESLint Playground.