eslint-plugin-task-needs-wait-for

0.1.0 • Public • Published

eslint plugin task-needs-wait-for

Why

As per ember-test-waiters documentation:

... the waitFor function can be use to wait for async behavior. It can be used with async functions, and in decorator form to wrap an async function so calls to it are registered with the test waiter system. It can also be used with generator functions such as those used in ember-concurrency.

This hints that it's possible to write an ember-concurrency task in a way so that ember testing won't be aware of it and it will create a race condition.

Examples

Consider following component:

import Component from "@glimmer/component";
import { task, timeout } from "ember-concurrency";
import { tracked } from "@glimmer/tracking";

export default class FooComponent extends Component {
  @tracked output = "nothing";

  @task
  *goodButton() {
    this.output = "bad";

    yield timeout(1000);

    this.output = "good";
  }
}

Thanks to using timeout from ember-concurrency we have the guarantee that tests will wait for the task to finish and the this.output will have value good.

Compare it to following:

import Component from "@glimmer/component";
import { task, timeout } from "ember-concurrency";
import { tracked } from "@glimmer/tracking";

export default class FooComponent extends Component {
  @tracked output = "nothing";

  @task
  *badButton() {
    this.output = "bad";

    yield new Promise((resolve) => {
      setTimeout(resolve, 1000);
    });

    this.output = "good";
  }
}

This task will also wait 1000ms till it proceeds, but ember testing will not wait for it to complete, because yielding a simple Promise won't register it with the ember test waiter system.

  • This type of error is very easy to make.
  • It leads to flaky tests. Imagine that the setTimeout (or whatever async behaviour) is very fast to resolve. Will the value of output be good or bad? Sometimes this, sometimes that.
  • It is an issue that is extremely hard to fix, because:
    • It flakes only sometimes.
    • It will fail on CI, but work perfectly fine on developer's machine.
    • Can point at completely unrelated pieces of code.

This issue can be fixed simply by adding @waitFor:

import Component from "@glimmer/component";
import { task, timeout } from "ember-concurrency";
import { tracked } from "@glimmer/tracking";
import { waitFor } from "@ember/test-waiters";

export default class FooComponent extends Component {
  @tracked output = "nothing";

  @task
  @waitFor
  *badButtonWithWaitFor() {
    this.output = "bad";

    yield new Promise((resolve) => {
      setTimeout(resolve, 1000);
    });

    this.output = "good";
  }
}

Conclusion

And since preventing people from stepping on a rake is a good idea, this plugin is trying to make sure that folks will have a good time.

Functionality

decorator-presence

Linter with auto-fix that makes sure that every @task has @waitFor after it:

// bad
class Foo {
  @task *example() {}
}

// good
import { waitFor } from "@ember/test-waiters";
class Foo {
  @task @waitFor *example() {}
}

decorator-order

Linter with auto-fix that makes sure as per ember-test-waiters documentation that we have certain order of the decorators:

waitFor acts as a wrapper for the generator function, producing another generator function that is registered with the test waiter system, suitable for wrapping in an ember-concurrency task. So @waitFor/waitFor() needs to be applied directly to the generator function, and @task/task() applied to the result.

//bad
class Foo {
  @waitFor @task *example() {}
}

// good
class Foo {
  @task @waitFor *example() {}
}

Contributing

Prerequisities

  1. pnpm

Setup

pnpm install

Testing

Automated tests

pnpm test

Running locally in a project

  1. Clone this repo next to your project
  2. Add following line to your project package.json:
  "eslint-plugin-task-needs-wait-for": "file:../eslint-plugin-task-needs-wait-for",
  1. Add following line to your project .eslintrc.js:
  plugins: ['task-needs-wait-for'],
  extends: {
    'plugin:task-needs-wait-for/recommended',
  }
  1. Run in a console:
pnpm build --watch
  1. Then inside your project you can run following to test:
npx eslint .

Package Sidebar

Install

npm i eslint-plugin-task-needs-wait-for

Weekly Downloads

1

Version

0.1.0

License

ISC

Unpacked Size

28.1 kB

Total Files

12

Last publish

Collaborators

  • michal.bryxi