Code Monkey home page Code Monkey logo

eslint-plugin-qunit's People

Contributors

aliaksandr-yermalayeu avatar bmish avatar calvinballing avatar dbattou avatar ddzz avatar dependabot[bot] avatar dwickern avatar edg2s avatar krinkle avatar mitchlloyd avatar platinumazure avatar raycohen avatar scalvert avatar techn1x avatar turbo87 avatar ventuno avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

eslint-plugin-qunit's Issues

New rule: no-throws-string

assert.throws has removed (block, string, string) signature in QUnit 2.0. Need a new rule to catch this and add to qunit/two configuration.

Probably worth adding to qunit/recommended configuration, since (block, regex, string) is more flexible anyway.

No need to flag (block, string) form as that has always been interpreted as an assertion message rather than an expected string, so its behavior is unchanged.

Marked as "breaking" since changes to shared configurations are very likely to be breaking changes.

New rule: Forbid expect argument in QUnit.test

This rule should be added to qunit/two config.

Examples that should warn:

test("test name", 0, function () { });

QUnit.test("test name", 0, function () { });

asyncTest("test name", 0, function () { });

QUnit.asyncTest("test name", 0, function () { });

Examples that should not warn:

// No expect is allowed by this rule (see "require-expect")
test("test name", function () { });
QUnit.test("test name", function () { });
asyncTest("test name", function () { });
QUnit.asyncTest("test name", function () { });

// Global expect() okay by this rule (see "no-global-expect")
test("test name", function () { expect(0); });
QUnit.test("test name", function () { expect(0); });
asyncTest("test name", function () { expect(0); });
QUnit.asyncTest("test name", function () { expect(0); });

// assert.expect preferred
test("test name", function (assert) { assert.expect(0); });
QUnit.test("test name", function (assert) { assert.expect(0); });
asyncTest("test name", function (assert) { assert.expect(0); });
QUnit.asyncTest("test name", function (assert) { assert.expect(0); });

Summarize rules in the README

Some people may not know to look in docs/lib/rules to discover which rules this plugin supports. It may also be worth calling out that the plugin is stable despite its 0.x version number.

no-negated-ok should not warn on global ok

The no-negated-ok rule should not warn on ok(!something). This is because there is no global notOk assertion.

There is also no point in warning for global notOk either, since that isn't a real assertion.

In summary, this rule should only really be enforced for MemberExpression assert.ok and assert.notOk.

New rule: No arrow function expression as test callback

As arrow functions become more commonly used, developers may use them to create QUnit test callbacks. This changes the expected test context to something else (probably to window). Some testing setups have specialized modules that use this inside beforeEach to manipulate the context, so changing it can be problematic.

Example that should warn:

test('description', (assert) => {
});

Example that is ok:

test('description', function(assert) {
});

I haven't thought too much about implementation yet, but it seems fairly simple to look for the test callback and then ensure that an arrowFunctionExpression is not being used.

It might also make sense to apply this rule to the beforeEach and afterEach callbacks in module setup.

New rule: Enforce use of expect

Ensure that expect is called when using assertions outside of top-level test callback

QUnit's assert.expect(...) helps developers create tests that correctly fail
when their expected number of assertions are not called. QUnit will throw an
error if no assertions are called by the time the test ends. This rule goes
further and ensures that expect is used anytime a developer uses are assertion
that is nested in a block or anytime a developer passes assert to another
function.

Rule Details

The following patterns are considered warnings:

test('name', function(assert) {
    assert.ok(true);
    while (false) {
        assert.ok(true);
    }
});

test('name', function(assert) {
    assert.ok(true);
    maybeCallback(function() {
        assert.ok(true);
    });
});

test('name', function(assert) {
    assert.ok(true);
    var callback = function() {
        assert.ok(true);
    }
    callback();
});

test('name', function(assert) {
    assert.ok(true);
    maybeCallback(function() {
        assert.expect(2); // Must be called in the top test function.
        assert.ok(true);
    });
});

test('name', function(assert) {
    assert.ok(true);
    myCustomAssertionWrapper(assert);
});

The following patterns are not considered warnings:

test('name', function(assert) {
    assert.ok(true);
});

test('name', function(assert) {
    assert.expect(2);

    if (true) {
        assert.ok(true);
        callMeBack(function() {
            assert.ok(true);
        });
    }
});

test('name', function(assert) {
    assert.expect(1);
    myCustomAssertionWrapper(assert);
});

When Not to Use It

  1. If your tests have some logic that relies on an unpredictable number of
    assertions being called.
  2. If you want to pass the assert context to functions without using expect.

no-async-in-loops does not respect other assert context identifiers besides "assert"

Rule "no-async-in-loops" does not respect other assert context identifiers besides "assert".

The following cases should be invalid but currently pass:

  • test('Name', function (foo) { while (false) foo.async(); });
  • test('Name', function (foo) { do foo.async(); while (false); });
  • test('Name', function (foo) { for (;;) foo.async(); });
  • test('Name', function (foo) { for (i in {}) foo.async(); });
  • test('Name', function (foo) { for (i of {}) foo.async(); });

New rule: no-only

Now that QUnit 1.20.0 is out and the QUnit.only is available, it would probably be a good idea to add an ESLint rule to forbid its use (at least on check-in).

New rule: no-ok-equality

We should create a rule which ensures that equality checks are not performed as the assertion argument of assert.ok and assert.notOk.

Ideally, the rule would suggest a better assertion to use instead:

  • assert.ok(a === b) would become assert.strictEqual(a, b)
  • assert.ok(a == b) would become assert.equal(a, b)
  • assert.ok(a !== b) would become assert.notStrictEqual(a, b)
  • assert.ok(a != b) would become assert.notEqual(a, b)
  • assert.notOk(a === b) would become assert.notStrictEqual(a, b)
  • assert.notOk(a == b) would become assert.notEqual(a, b)
  • assert.notOk(a !== b) would become assert.strictEqual(a, b)
  • assert.notOk(a != b) would become assert.equal(a, b)

Bump ESLint peer dependency

Need to determine which version of ESLint to aim for (around 2.6 or 2.8?). Want to support both plugin shareable configuration and new-style rules (rules as objects rather than factory functions).

Prevent early return (before assertion) in QUnit test

The purpose of this rule is to prevent unit tests from returning early (even conditionally) if there are still assertions further in the test code. If a test may need to return early, this is a sign that the test either is testing nondeterministically (in which case the test or code should be refactored to support deterministic testing), or that the entire test might need to be skipped in certain conditions.

The following example is a problem under this rule:

QUnit.test("test", function (assert) {
    if (true) {
        return;
    }
    assert.ok(false);
});

The following examples are not problems under this rule:

if (condition) {
    QUnit.test("test", function (assert) {
        assert.ok(false);
    });
}

QUnit[condition ? "test" : "skip"]("a test", function (assert) {
    assert.ok(false);
});

New rule: assert-args

It would be good to have a rule that checks if the number of arguments to assertions is correct.

The rule could have an option { requireMessage: true } which would require an assertion message in all cases. If this option is not specified, then assertions without messages would be acceptable.

The following patterns would trigger a warning:

  • ok(result, true, "This really should be equal() or similar");

The following patterns would trigger a warning with requireMessage: true on:

  • ok(result);
  • equal(a, b);
  • throws(function () {}, TypeError);

Still need to flesh this out a bit.

Prevent mutable fixtures from being defined outside of tests

The goal of this rule is to try to catch cases where there is potential for tests being dependent on each other (or on the order they are run) due to a shared dependence on mutable fixtures.

Example problem:

var myFixture = [];

QUnit.test("Mutates", function (assert) {
    myFixture.push("a value");
    assert.strictEqual(myFixture.length, 1);
});

QUnit.test("Reads", function (assert) {
    assert.strictEqual(myFixture.length, 0);   // this will fail
});

Ideally, this rule would use escope to look at variable references and would flag the definition node if the initial value is mutable (object or array) and the variable is accessed in at least one test. The lint message should suggest using a function to return the fixture to avoid shared state.

New rule: no-assert-equal

We should create a new rule which forbids assert.equal (preferring instead assert.strictEqual, assert.deepEqual, or assert.propEqual as needed).

Combine utility functions into a separate file

Rules "resolve-async" and "no-async-in-loops" currently use some similar functions to detect calls to stop(), start(), assert.async(), and test contexts. These functions should be grouped into their own file to maximize code reuse.

Forbid extra actions and assertions in tests

The goal of this issue is to avoid scenarios like this:

QUnit.test("a test", function (assert) {
    var result1 = doSomething();
    assert.ok(result1);

    var result2 = doSomething();
    assert.ok(result2);
});

This test should be split into multiple tests.

In order to allow re-synchronizing async code and clean up any objects that need to be cleaned up, the best approach is probably to warn only

  1. when an assertion occurs,
  2. after a non-assertion statement (i.e., possible action),
  3. which is itself after an assertion statement.

This means one of two things:

  1. The extra action can be moved above the assertions (i.e., as part of the unit test action); or
  2. The extra action is another test and should be split into a test accordingly.

Add rule to catch stop()/start()/assert.async() in loops

The use of async controls in loops makes test code much harder to reason about and maintain. We should create a rule which will detect and report those uses.

Some valid test cases:

  • test('name', function () { stop(); for (var i = 0; i < 2; ++i) {} start(); });

Some invalid test cases:

  • test('name', function () { for (var i = 0; i < 2; ++i) { stop(); } start(2); });
  • test('name', function () { for (var i in obj) { stop(); } });
  • test('name', function () { for (var i of obj) { stop(); } });
  • test('name', function () { var i = 2; while (i--) { stop(); } start(2); });
  • test('name', function () { var i = 2; do { stop(); } while (i--); start(2); });

New rule: literal-compare-order

This rule should warn if any of the built-in two-argument assertions are invoked with arguments in the wrong order.

Will flag the following:

assert.equal("a literal", someVar);
assert.equal("a literal", someVar, "a message");
// etc. for strictEqual, deepEqual, propEqual, notEqual,
// notStrictEqual, notDeepEqual, notPropEqual

Will not flag the following:

assert.equal(someVar, "a literal");
assert.equal(someVar, "a literal", "a message");
assert.equal(expected, result);    // Args are in wrong order but we can't flag them :-(
assert.equal(expected, result, "a message");
// etc. for strictEqual, deepEqual, propEqual, notEqual,
// notStrictEqual, notDeepEqual, notPropEqual

Chore: Use some ES6 constructs

Enable these ESLint rules:

  • no-var
  • prefer-const
  • prefer-arrow-callback Can add this later if desired
  • arrow-body-style Can add this later if desired
  • prefer-template
  • no-const-assign
  • template-curly-spacing

Support semaphore counts in resolve-async

Currently we are just tracking the number of calls to QUnit.stop() and QUnit.start(), regardless of any semaphore count that might be passed in as an argument. We need to take the semaphore argument into account.

Some test cases which should be valid but currently aren't:

  • test('name', function () { stop(2); start(); start(); });
  • test('name', function () { stop(); stop(); start(2); });
  • asyncTest('name', function () { stop(); start(2); });
  • asyncTest('name', function () { stop(2); start(3); });

New rule: Forbid assertions inside conditional statements

Assertions should not be run inside a conditional statement. Assertions run inside conditional statements imply either non-determinism, or else that the test needs to be broken out into multiple, simpler tests.

The following code is problematic under this rule:

if (foo) {
    assert.ok(true);
}

if (foo) {
    doSomething();
} else {
    assert.ok(true);
}

Also, a lot of the time, the condition in question could be asserted on instead: if (foo) becomes assert.ok(foo);.

New rule: Forbid QUnit 1.x syntax

Per the migration guide, this implies a few warnings (which may or may not be separate rules):

  • Forbid use of module, test, and asyncTest globals
  • Forbid use of QUnit.asyncTest
  • Forbid global stop()/start()
  • Require assert.async() instead of QUnit.stop()
  • Forbid use of assertion globals
  • Forbid use of global expect()
  • Require beforeEach and afterEach module hooks instead of setup and teardown
  • Forbid QUnit.push (use this.push in custom assertion)
  • Forbid QUnit.init
  • Forbid QUnit.reset
  • Forbid assignments to QUnit.log and other logging callbacks
  • Forbid QUnit.jsDump, favoring QUnit.dump instead

Rule idea: no-negated-ok

It is preferable to use .notOk(something) rather than .ok(!something) since it will improve the readability of the error message.

This rule would be especially valuable since .notOk() was only introduced in version 1.18.0. Projects that started using the framework prior to that would have had to use the negated .ok() value approach.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.