Skip to content

Latest commit

 

History

History
292 lines (220 loc) · 7.4 KB

File metadata and controls

292 lines (220 loc) · 7.4 KB

e2ed/require-definite-variable-in-test

📝 Require definite assignment assertions and undefined-free types for uninitialized variables in the test body.

💼 This rule is enabled in the 🌐 all config.

🔧 This rule is automatically fixable by the --fix CLI option.

The body of a test consists of step calls (Given, When, Then, And) and of statements between them — mostly declarations of variables that are shared between steps and assigned inside the first of them. Such a variable must be declared with a definite assignment assertion (let name!: Type), and its type must not include undefined in a union: both an implicit undefined (a declaration without !) and an explicit | undefined hide the exact type of the variable from the following steps.

The rule applies only to test files — all JS/TS files in the autotests/tests directory (at any depth). It checks let and var declarations without initializers at the top level of the body of the function passed to a top-level test(...) call (const cannot be declared without an initializer). Declarations with a type equal to undefined (let nothing!: undefined) are allowed.

Examples

❌ Incorrect

// autotests/tests/search/searchPage.ts

import {test} from 'autotests';

test('Search page is working', {meta: {testId: 1209}}, async () => {
  let searchPage: SearchPage;
  let filters!: Filters | undefined;

  await Given('Open the search page', async () => {
    searchPage = await navigateToPage(SearchPage);
    filters = searchPage.filters;
  });
});
Fixed
import {test} from 'autotests';

test('Search page is working', {meta: {testId: 1209}}, async () => {
  let searchPage!: SearchPage;
  let filters!: Filters;

  await Given('Open the search page', async () => {
    searchPage = await navigateToPage(SearchPage);
    filters = searchPage.filters;
  });
});
// autotests/tests/search/searchPage.ts

test('Search page is working', {meta: {testId: 1209}}, async () => {
  let searchPage: SearchPage;
});
// autotests/tests/search/searchPage.ts

test('Search page is working', {meta: {testId: 1209}}, async () => {
  var searchPage: SearchPage;
});
// autotests/tests/search/searchPage.ts

// Both the missing `!` and `undefined` in the type are fixed
test('Search page is working', {meta: {testId: 1209}}, async () => {
  let searchPage: SearchPage | undefined;
});
Fixed
test('Search page is working', {meta: {testId: 1209}}, async () => {
  let searchPage!: SearchPage;
});
// autotests/tests/search/searchPage.ts

// `undefined` is removed from the union
test('Search page is working', {meta: {testId: 1209}}, async () => {
  let searchPage!: SearchPage | undefined;
});
// autotests/tests/search/searchPage.ts

// Every declarator is checked
test('Search page is working', {meta: {testId: 1209}}, async () => {
  let searchPage: SearchPage, filters: Filters;
});
// autotests/tests/search/searchPage.ts

// Reported without autofix: the exact type must be written manually
test('Search page is working', {meta: {testId: 1209}}, async () => {
  let searchPage;
});
// autotests/tests/search/searchPage.ts

// `undefined` is reported in any position of the union
test('Search page is working', {meta: {testId: 1209}}, async () => {
  let searchPage!: undefined | SearchPage;
});
// autotests/tests/search/searchPage.ts

// Nested unions are handled too
test('Search page is working', {meta: {testId: 1209}}, async () => {
  let searchPage!: SearchPage | (MainPage | undefined);
});
Fixed
test('Search page is working', {meta: {testId: 1209}}, async () => {
  let searchPage!: SearchPage | MainPage;
});

✅ Correct

// autotests/tests/search/searchPage.ts

import {test} from 'autotests';

test('Search page is working', {meta: {testId: 1209}}, async () => {
  const language = Language.PT_BR;

  let searchPage!: SearchPage;
  let filters!: Filters;

  await Given('Open the search page', async () => {
    searchPage = await navigateToPage(SearchPage, {language});
    filters = searchPage.filters;
  });
});
// autotests/tests/search/searchPage.ts

// `var` with the definite assignment assertion
test('Search page is working', {meta: {testId: 1209}}, async function () {
  var searchPage!: SearchPage;
});
// autotests/tests/search/searchPage.ts

// A declaration with an initializer is not checked
test('Search page is working', {meta: {testId: 1209}}, async () => {
  let searchPage: SearchPage | undefined = undefined;
});
// autotests/tests/search/searchPage.ts

// Declarations inside step callbacks are not checked
test('Search page is working', {meta: {testId: 1209}}, async () => {
  await Given('Open the search page', async () => {
    let searchPage;
  });
});
// autotests/tests/search/searchPage.ts

// Declarations inside nested blocks are not checked
test('Search page is working', {meta: {testId: 1209}}, async () => {
  {
    let searchPage: SearchPage;
  }
});
// autotests/tests/search/searchPage.ts

// A type equal to `undefined` is allowed
test('Search page is working', {meta: {testId: 1209}}, async () => {
  let emptyValue!: undefined;
});
// autotests/tests/search/searchPage.ts

// Only top-level `test(...)` calls are checked
withRetries(3, () => {
  test('Search page is working', {meta: {testId: 1209}}, async () => {
    let searchPage: SearchPage;
  });
});
// autotests/tests/search/searchPage.ts

// Not the e2ed `test` signature: the callback is not the last argument
test(
  'Search page is working',
  async () => {
    let searchPage: SearchPage;
  },
  {meta: {testId: 1209}},
);
// autotests/tests/search/searchPage.ts

// Only calls of `test` are checked
it('Search page is working', {meta: {testId: 1209}}, async () => {
  let searchPage: SearchPage;
});
// autotests/pageObjects/SearchPage.ts

// Only test files in the `autotests/tests` directory are checked
test('Search page is working', {meta: {testId: 1209}}, async () => {
  let searchPage: SearchPage;
});

Rule details

The autofix adds the missing ! and removes undefined members from the union (including nested unions like First | (Second | undefined)). A declaration without a type annotation (let searchPage;) is reported without the autofix, because the assertion requires a type annotation — write the exact type manually.

The rule is purely syntactic: a type alias that hides undefined (type Maybe = SearchPage | undefined) is not detected. Declarations inside step callbacks and nested blocks are not checked — only the first level of the test body.

When not to use it

The rule enforces the convention of strict splitting of tests into steps, where variables shared between steps are assigned inside the steps. If your project does not follow this convention, do not enable this rule.

Further reading