📝 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.
// 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;
});// 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;
});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.
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.