--- title: Style Guide --- This document outlines the style guide that applies to all of our projects. ## 1. Global Conventions These sections apply to all code in any of our projects. ### 1.1. Copyright Notice All code files should begin with a comment section containing the copyright information: ```js /** * @copyright nhcarrigan * @license Naomi's Public License * @author Naomi Carrigan */ ``` Note that `copyright` _must_ be assigned to `nhcarrigan`, and our `license` must be applied, but when checking in new code you may attribute yourself as the author, or add yourself to the list for existing code. ## 2. JavaScript Projects The following sections apply to our JavaScript/TypeScript projects. Style conventions are enforced through our [custom ESLint package](/projects/eslint). :::caution We do _not_ use Prettier in any of our projects. Instead, our linter package also handles code styling. If you are using VSCode, you can add this to your `.vscode/settings.json` file in the project directory to enable auto-formatting on save. ```json { "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, "eslint.validate": ["typescript"] } ``` Because of this, our style guide will not cover the formatting section of our linter package. ::: ## 2.1. Main Rules These rules apply to all TypeScript code, and will run on files in `src/**/*.ts`. ### 2.1.1. `eslint` Enforced Rules - Setters must have a corresponding getter. - Array methods which return a value must return a value in the callback. - Arrow functions should always use curly braces. - Variables must be used within the scope they are defined. - Comments should always start with a capital letter. - Avoid overly complex functions. - `return` statements must either never return a value, or always return a value. - `if` statements and similar control blocks should always use curly braces. - Switch statements must always have a default case, and the default case must always come last. - Never use loose equality. - A `for` loops iteration direction must match the conditional bound. - Functions assigned to a property must match the name of the property. - With the exception of callback functions, all functions should be assigned a name. - When using the `function` keyword, prefer `function name()` declarations over `const name = function()` expressions. - Getters should always return a value. - Getters and setters should be grouped together. - You should never use logical assignment operators. - You should never have more than one class per file. - Logical blocks should not be nested more than five levels deep. Prefer extracting into separate functions. - Files should not have more than 300 lines. Prefer extracting into modular functions. - Functions should not have more than 50 lines. Prefer breaking down into smaller logic. - Callbacks should not be nested more than two levels. - Functions should not have more than 20 logical statements/branches. - Constructor names should begin with a capital letter. - Do not use the `alert` API. - Promise executor functions should not be async. - Do not use `await` within loops. - Do not use bitwise operators. - Do not use `arguments.caller` or `arguments.callee`. - Do not declare variables within switch case statements. - Do not reassign class definitions. - Do not compare with `-0`. - Do not perform assignments in conditions e.g. `if (name = "Naomi")`. - Do not use the `console` API, prefer our custom logger. - Avoid confusing operator combinations e.g. `a + b ?? c`. - Avoid constant conditions e.g. `if(false)` or `while(true)`. - Constructors should not return a value. They implicitly return the instance. - Do not use control characters `\x00` in regular expressions. - Do not use `debugger` statements. - Do not use `delete` on variables. - Do not start a regular expression with `=` (confusing with `/=` operator). - Functions should not have multiple parameters with the same name. - Classes should not have multiple members with the same name. - Objects should not have multiple keys with the same name. - If/else statements should not use the same condition. - Switch statements should not have duplicate case conditions. - Import statements should not have duplicate imports. - Prefer using early return over if/else. - Block statements should not be empty. - Character classes in regular expressions should not be empty. - Destructured values should not be empty. - Static class blocks should not be empty. - You should not use loose equality with `null`. - You should not use `eval`. - Do not reassign the error parameter in a catch block. - Do not extend native prototypes. - Do not call `bind` unnecessarily. - Do not cast to a boolean in conditions. - Do not use `continue` or `break` unnecessarily. - All case statements should have a break statement. - Do not reassign function declarations. - Do not reassign global values (e.g. `window`). - Do not use implicit coercion (e.g. `!!bool`, `+num`). - Do not declare global variables. All values should be scoped to a module. - Do not reassign to imported variables. - Do not declare functions in logical blocks. - Do not pass invalid regular expression strings to `RegExp()`. - Do not use `this` outside of classes. - Do not use unusual space characters. - Do not access `__iterator__`. - Do not use loop labels. - Do not create unnecessary blocks. - Do not nest `if` within `else`, use `else if`. - Do not use multi-code characters (such as emotes) within character classes. - Do not chain assignment operators. - Do not use `\` to create multi-line strings. Use concatenation or template strings. - `if` conditions should not be in the negative (e.g. `!bool`). - Do not nest ternary statements. - Do not use `new` outside of an assignment. - Do not use `new` with `BigInt` or `Symbol`. - Do not use `new` with `String`, `Number`, or `Boolean`. - Do not use octal escapes (e.g. `\8`, `\2`). - Do not call the `Math`, `JSON`, `Reflect`, `Atomics`, or `Intl` objects as functions or constructors. - Do not call `new Object()` without an argument. - Do not use octal literals e.g. `071`. - Do not reassign function parameters. - Do not use the `++` operator. - Do not use `return` in promise executors. - Do not use the `__proto__` property. - Prefer calling the Object prototype over builtins (e.g. `Object.prototype.hasOwnProperty.call(foo, "bar")` over `foo.hasOwnProperty("bar")`). - Do not use literal spaces in regular expressions, use the escape sequence `\s`. - Do not return assignment operations. - Do not use `javascript:void` URLs. - Do not assign a variable to itself. - Do not compare a variable with itself. - Do not use the comma operator to evaluate multiple expressions. - Setters should not return a value. - Do not allow sparse arrays. - Do not use `${foo}` template interpolation in regular strings. - Do not reference `this` before calling `super` in constructors that call `super`. - Do not reference variables that are not declared. - Do not initialise variables to `undefined`. - With the exception of trailing underscores for unused variables, do not use dangling underscores. - Do not insert line breaks in confusing locations, such as the middle of a variable assignment. - Do not use a variable in a loop condition that is not modified within the loop. - Do not use a ternary if a simpler approach exists. - There should be no unreachable code. - Do not use control statements in `finally` blocks. - You must use parentheses when negating `in` or `instanceOf`, e.g. `if (!(key in obj))`. - Do not use optional chaining where an undefined value is not allowed. - Do not declare private class members that are not consumed by the class. - Do not declare variables that are unused. - Avoid backreferences in regular expressions when they would match nothing. - Do not use `.call()` or `.apply()` unnecessarily. - `catch` statements should _handle_ an error, not turn around and throw the error. - Do not use computed-key syntax for literal keys e.g. `{["a"]: "b"}`. - Do not concatenate string literals without variables. - Do not escape characters unnecessarily. - Do not rename a reference to the same name e.g. `let { a: a } = b;`. - Do not use redundant `return` statements. - No use of the `var` keyword. - Do not merge `TODO` or `FIX` comments into the main branch. - Do not use the `with` statement. - An object must use either all long-form definitions `{a : a}` or all short-form definitions `{ a }`, but never a mix of the two `{ a, b: c}`. - Avoid shorthand operators `+=`, prefer the explicit `x = x + y`. - Callback functions should be anonymous arrow functions. - Use `const` for all variables that are not reassigned. - Capture groups should be named. - Prefer `Object.hasOwn()`. - Prefer the spread operator over `Object.assign`. - Prefer regular expression literals over the RegExp constructor. - Prefer using the `...` operator instead of `arguments`. - Prefer the spread operator over `.apply()`. - Prefer template literals over string concatenation. - `parseInt()` should always be given a radix. - When an asynchronous function relies on an argument that might change (such as an object reference), care must be taken to avoid race conditions within that function. - Generator functions must use the `yield` keyword. - Object keys must be sorted alphabetically. - Variables within the same declaration block must be sorted alphabetically. - If using the `Symbol` function, you must include a description. - Do not compare to `NaN`, use `Number.isNaN()`. - `typeof` comparators must be a valid return type of `typeof`. - Conditions should always start with the variable, not the constant. ### 2.1.2. `typescript-eslint` Enforced Rules - Do not use `await` on functions that do not return a Promise. - Exported type definitions should use the `export type` keyword. - Use dot notation for property access, unless such syntax would be invalid. - Do not use the `delete` keyword with arrays. - Do not use `.toString()` on objects. Prefer `JSON.stringify()`. - Do not assign void values to variables, such as `const log = console.log("Bad");`. - Do not duplicate types (e.g. `type myType = "A" | "A"`). - Promises should always be awaited. - Do not implicitly evaluate code, such as with `new Function()`. - Do not use `void` with functions that do not return a value. - Do not use Promises in places that do not consume them (such as callbacks). - Do not combine string and number values in an enum. - Do not use unions/intersections with overriding values (such as `any` or `unknown`). - Do not use comparison operators with boolean values (e.g. `variable === false`). - Do not pass values to a conditional statement that are always truthy or falsy. - Do not use unnecessary namespace references. - Do not use template strings without interpolated values. - Do not pass type arguments to a function that match the default value of that parameter. - Do not define unused type parameters in a function. - Values with an `any` type should not be passed to function calls. - Values with an `any` type should not be assigned to variables. - Values which have an `any` type should not be called as functions. - Do not merge declarations (instead of `interface Foo` and `class Foo`, use `interface Foo` and `class Bar implements Foo`). - Do not compare enum values against arbitrary values. - Do not use the `Function` type. Functions should have a specific type signature. - Values with an `any` type cannot have properties, and such properties should not be accessed. - Functions should not return an `any` value. - The unary minus operator should only be used on numbers. - `throw` statements should only throw an `Error` instance. - Object properties should be destructured, rather than assigned to variables via direct access. - Use `.find()` instead of `.filter()[0]` to find an element in an array. - Use `.includes()` instead of `.indexOf() === -1`. - Use `??` instead of `||`. - Use optional chaining when possible. - Promises that reject should always return an Error. - Class members that are not modified outside of the constructor should be `readonly`. - The `.reduce()` callback should have typed parameters. - When using a non-global regular expression, use `.exec` over `.match()`. - Class methods that return `this` should have `this` as the return type, not the class. - When testing the beginning or end of a string, use `.startsWith()` or `.endsWith()`. - Functions which return a Promise must use the `async` keyword. - `.sort()` should always be given a compare function. - `async` functions must use `await`, or be rewritten without the promise. - The `+` operator can only be used with operands of the same type. - Values interpolated in a template string must be of type `string`. - Functions which return a promise should await that return value. - Conditions should not use nullable values - you must always explicitly compare against `null` or `undefined`. - Switch statements are not preferred, but when you must use them it should have exhaustive cases. - Unbound class methods must be called with the expected scope (e.g. `myClass.method.bind(myClass)`). - The `error` parameter in a catch statement is of type `unknown`. - Function overload signatures must be grouped together. - Array type definitions should use the generic `Array` over `T[]`. - `@ts` directives are disallowed, except for `@ts-expect-error` which requires a description. - Classes with static public properties should have a getter for each property. - Class methods which are not static must make use of `this` or be converted to static. - Prefer passing type arguments to a constructor instead of a type annotation. - Prefer `Record` over `{ [key: string]: T}`. - Do not use any type assertions (`x as T`). - Object types should be defined with `interface` over `type`. - Imported values which are only used as types should be imported with `type`. - Function parameters which are optional or have a default value should come after all mandatory parameters. - Functions should always have an explicit return type. - All class members should have an accessibility modifier (`public`, `protected`, `private`). - All exported functions and public class methods should have explicit parameter and return types. - Variables should always be initialised with a value. - Functions which take more than three arguments should take a single object instead. - Class members should be ordered as `constructor`, `fields`, `statics`, `getters`, `setters`, then `methods`. - Method types should use the property signature `func: (arg: string) => string`. - Variables should use `camelCase`, types and classes should use `PascalCase`. Only variables may use a leading underscore to indicate they are unused. - Never use the `Array` constructor. - Do not use non-null assertions. - Enums should not have duplicate values. - Do not use the `delete` operator on dynamic property accesses. - Functions, interfaces, and object types should never be empty. - Do not use `any`. - Classes should not consist solely of static members. - The `for...in` loop should not be used. - When all imported members are type imports, use `import type { a, b }` over `import { type a, type b }`. - Do not use type annotations for variables with a string, number, or boolean value. These can be inferred. - The `void` type should only be used as the _sole_ return type of a function. `(): void | string` is invalid. - Do not reference unsafe values in a loop. - Do not use number literals which would lose precision due to memory constraints. - Classes should use `constructor()`, not `new()`. Interfaces for the class should use `new()`, not `constructor()`. - Do not use namespaces. - Do not use `require()`. - Do not declare variables in an inner scope that share a name with a variable in the outer scope. - Do not alias `this`. - Do not needlessly assign constructor parameters to class members of the same name. - Do not `extends` `any` or `unknown`. - Unused expressions should be removed. - Unused variables should be removed. - Do not access a variable before its definition. - Do not define empty constructors. - Do not define empty exports. - Do not use wrapper types (e.g. use `boolean`, not `Boolean`). - Use `as const` over literal types. - Enum values should always be explicitly initialised. - The `for...of` loop should be used over the `for` loop, when the index _value_ is not needed. - Enum values should always be literal values. - Do not use TypeScript's `///` reference. - Do not use overloads when the same result can be achieved via union types or optional parameters. ### 2.1.3. `eslint-comments` Enforced Rules - Do not use blanket `eslint-disable` directives. All `disable` directives must target specific rules. - All `disable` directives must include a comment explaining why the linter is being bypassed. ### 2.1.4. `deprecation` Enforced Rules - Do not use deprecated methods/features, even if the package still supports them. ### 2.1.5. `import` Enforced Rules - Do not use default imports if the module does not have a default export. - Do not import modules simply to re-export them. - Exports should always be at the end of the file. - With the exception of packages, imports should always have a file extension. - Imports should always be at the beginning of the file. - If a module has multiple named exports, group them in a single `export { a, b }` declaration. - Import statements should be followed by a newline. - Import paths should never be absolute - You should not use AMD syntax - You should not use CJS syntax - You should not define cyclical imports (if file a imports file b, file b CANNOT import file a) - Modules should not use a default export. - A module should not import the same file multiple times. - You should not use dynamic `require` calls. - Named import blocks cannot be empty. - You should not import packages that are not included in the `package.json` file. - You cannot `import` a CommonJS module. - Exports must always be declared with `const`. - You cannot use a named export as the default export. - You cannot use a named export as the property of a default export. - You should not have orphaned modules (modules without any exports, or a module which is never imported). - You cannot use webpack loader syntax. - Imports must be sorted alphabetically, first grouped by: builtin node modules, external packages, internal packages, modules in parent directory, modules in current directory, type-only imports. - There should be no new lines between these groups. ### 2.1.6. `jsdoc` Enforced Rules - `@access` tags should be one of `package`, `private`, `protected`, or `public`. - Asterisks should be aligned. - Lines should not be improperly indented. - `@param` names should reflect the actual parameters. - `@property` names should reflect the actual properties. - Should not use `=` (GCC syntax). - All tags must be valid JSDoc tags. - `@template` names should be used in the `@typedef`. - `@license` tag MUST be set to `Naomi's Public License`. - Tags that do not expect content should not have content. - `@implements` should only be used on constructors or classes. - Tag descriptions should not be just a reflection of the name. - Description tags must be complete sentences. - JSDocs must be multiple lines. The first and last line cannot have text content. - JSDoc syntax must be valid. - JSDocs must have a description. - JSDocs cannot be empty. - A hyphen should separate the parameter name from the description. - JSDocs are required on all public functions, classes, and methods. They are encouraged on all public exports. - Parameters must have a name and description. - Properties must have a name and description. - If a function returns a value, it must be documented in `@returns`. - If a function throws an unhandled error intentionally, it must be documented in `@throws`. - If a generator yields a value, it must be documented in `@yields`. - Tags must be sorted alphabetically. - There should be no blank lines between tags. - Any types referenced must be valid types. ### 2.1.7. `unicorn` Enforced Rules - Regular expressions should use special shorthand where possible. - `catch` blocks should always have `error` as the parameter. - When destructuring an object, all consumed properties should be destructured. Do not perform direct property access on an object that is previously destructured. - When spreading a ternary in an array, both sides of the expression must be of the same type. - Functions should be declared in the highest scope possible. - `Error()` constructor calls must always be given a message. - Escape sequences must use uppercase text. - Files should be named in camelCase. - Do not pass function references as callbacks to array methods. - Do not use `.forEach()`. - Do not use `this` in array methods. - Do not chain `.push()` calls. - Do not use `.reduce()`. - Do not access properties directly from an `await` expression. - Do not use `await` in Promise methods such as `.all()`. - Files should not be empty. - Do not use a `for` loop when you can use a `for...of` loop. - Use Unicode escape sequences over hexadecimal escape sequences. - Use `Array.isArray()` over `instanceof Array`. - `GET` or `HEAD` requests should never have a body. - Do not declare variables that start with `new` or `class`. - Do not use `.length` as the argument to `.slice()`. - Use `Buffer.from()`, not `new Buffer()`. - Do not use objects as default parameters. - Do not pass single-element arrays to Promise methods. - Do not define a class that only has static members. - Do not use `.then()`. - Do not assign to `this`. - Do not use `typeof x === "undefined"`. - `await` cannot be used on non-promise values. - You cannot ignore consecutive values when destructuring an array, e.g. `[,,,, val]`. - You cannot use IIFEs. - Promises should not return `.resolve()` or `.reject()`. - You should not spread an array into a new array without adding values. - You should not associate any case statements with the default statement. - Number literals cannot have trailing zeroes after the decimal place. - Number literals must use proper case. - Numbers over 9999 should use `_` to separate each group of three digits, e.g. `10_000`. - If you need to flatten an array, use `Array.flat()`. - Prefer `.flatMap()` over `.map().flat()`. - Prefer `indexOf` over `findIndex`. - Prefer `.some()` over `.filter().length`. - Prefer `.at()` for accessing array and string indices. - Prefer `.codePointAt()` over `.charCodeAt()`. - Prefer `Date.now()` over `new Date().getTime()`. - Use default parameters instead of reassigning undefined values. - Use `Math.trunc` to round numbers, not bitwise operators. - Use ESM modules, not CJS. - Use coercion functions such as `String()` directly. - Use a negative index instead of `.legnth - 1`. - Use the `node:` protocol when importing built-in packages. - Use `Number` properties instead of global helpers e.g. `Number.isNaN()` over `isNaN()`. - Use `Object.fromEntries` to create an object from key-value pairs. - If a `catch` block does not use the `error` parameter, the parameter should be omitted. - Prefer calling prototype methods over instance methods. - Prefer using `Set.has()` over `Array.includes` to check for existence. - Prefer using `Set.size` over `Array.length`. - Prefer using the spread operator over `Array.concat()` or `String.split("")`. - Prefer using `String.replaceAll()` over regex searches. - Prefer `String.slice()` over `String.substr[ing]()`. - Prefer `String.startsWith()` or `.endsWith()` over `RegExp.test()`. - Prefer `String.trim[Start|End]()` over `String.trim[Left|Right]()`. - Use `structuredClone` to create a deep clone of an object. - Use top-level await instead of IIFEs. - Variable names should not be abbreviated. - `Array.join()` must always be given a separator. - `Number.toFixed()` must always be given a digits argument. ## 2.2. React Rules These rules apply to TSX, and will run on files in `src/**/*.tsx`. - Boolean properties should be named accordingly, such as `isEnabled` instead of `enabled`. - Buttons must always have a `type` attribute. - If a radio button or checkbox is checked, it must be `readonly` or have an `onChange` handler. - Default properties should match the property types. - Properties, state, and context should always be destructured. - Components must always have a name. - A component cannot consume another component's propTypes. - PropTypes cannot use `any`, `array`, or `object`. - `forwardRef` components must use `ref`. - Components should always be defined as named arrow functions. - State should have matching names, e.g. `[color, setColor]`. - `iframes` should always use the `sandbox` attribute. - Boolean properties should always be explicitly written: ``. - Spaces between JSX elements must be explicitly written: `{' '}`. - Files that contain JSX must have the extension `.jsx` or `.tsx`. - Fragments should always use the shorthand syntax. - Event handlers must be appropriately named: `onChange={this.handleChange}` - Iterators must always have a `key` prop, and it should never be the index. - You should not use `.bind()` in component props - You should not insert comments as text nodes. - Contexts should not use mutable values (e.g. objects), to prevent re-renders. - Components should not have duplicate properties. - Avoid leaking conditional values. Prefer `condition ? : null` over `condition && `. - Element text should always be wrapped in JSX containers. - You should never use `javascript:` urls. - `target=_blank` must always be accompanied with `rel=noreferrer`. - You should not use variables that are not declared. - You should not use fragments unless there is no singular wrapping element. - Components should use PascalCase. - You should not spread props multiple times. - Props should be sorted alphabetically. - You should not access `this.state` inside any `setState` functions. - You should not use arrow functions for lifecycle methods. - Children cannot be passed as props. - You cannot use dangerous properties (`dangerouslySetInnerHTML`). - You cannot use deprecated methods. - You cannot use `setState` in `componentDidMount` or `componentDidUpdate` or `componentWillUpdate`. - You cannot directly mutate `this.state`. - You cannot use `findDOMNode`. - You cannot use `isMounted`. - Each file should have only one component. - `ReactDOM.render`'s return value should not be used. - You should not use string references. - `this` cannot be used in stateless functional components. - HTML entities must be escaped. - You cannot use unknown HTML properties. - You cannot use unsafe lifecycle methods. - You cannot define a component within another component. - Class components should not have unused methods. - Components should not have unused propTypes or state - Class components should use the ES6 `class Component extends React.Component` syntax. - Props should be readonly. - Stateless components should be pure functions. - Property accesses must be reflected in the propTypes. - Any optional property must have a default value. - Classes using the `render` method MUST return a value. - Components without children must still have a separate closing tag. - State initialisation must always happen in the constructor. - Static properties should be defined as `static name = `. - The `style` property must always be an object. - Void elements `img`, `br` etc. must never have children. ## 2.3. Playwright Rules These files apply to Playwright End-to-end tests, and will run on `e2e/**/*.spec.ts`. - All tests must have at least one `expect` assertion. - `describe` calls cannot be nested more than two levels deep. - Playwright APIs must be `await`ed. - Tests cannot be commented out. - `expect` cannot be called conditionally. - Tests cannot contain conditional logic. - Setup and teardown hooks cannot be duplicated. - You cannot use element handles (`page.$`). - You cannot use `page.$eval()`. - Tests cannot be focused with `.only()`. - Tests cannot be forced with `{ force: true }`. - You cannot use `getByTitle()`. - You cannot use the `networkidle` option. - You cannot use `first()`, `lat()`, or `nth()`. - You cannot use `page.pause()`. - You cannot use `page.locator()`. Prefer specific methods like `page.getByRole()`. - Tests cannot be skipped with `.skip()`. - `expect` can only be called within test blocks. - You cannot reference variables in `page.evaluate()`. - Some Playwright methods are synchronous - these cannot be `await`ed. - You cannot use `.not()` when a specific matcher exists. - You cannot use `page.waitForSelector()` or `page.waitForTimeout()`. - Prefer `.toBeGreaterThan()` over `(x > 5).toBe(true)`. - Prefer `.toBe(5)` over `(x === 5).toBe(true)` - Hooks should be ordered as Playwright calls them: `beforeAll`, `beforeEach`, `afterEach`, `afterAll`. - Hooks should be at the top of the test. - Test names should be lowercase. - Use `.toStrictEqual()` over `.toEqual()`. - Use `.toBe()` over `.toStrictEqual()` for primitive values. - Use `.toContain()` over `.includes()`. - Use `.toHaveCount()` over `locator.count()` and `.toHaveLength()` over `.length`. - Prefer assertions like `.toBeVisible()` over `(locator.isVisible()).toBe(true)`. - Setup and teardown code must be in a hook. - `.toThrow()` assertions require a message. - Tests must be within a `describe` block. - All `expect()` calls must have a custom message. - Promises that contain an `expect` must be awaited. - Tests must have a title that is not empty. ## 2.4. Vitest Rules These files apply to Vitest unit tests, and will run on `test/**/*.spec.ts`. :::warn We also mandate the use of `describe`, `it`, and `expect` over `suite`, `test`, and `assert`. ::: - Files must have `.spec.ts` extension. - consistent-test-it - Tests must have at least one `expect`. - `describe` calls cannot be nested more than two levels deep. - Prefer `.toHaveBeenCalled()` over `.toBeCalled()`. - Tests cannot be commented out. - `expect` cannot be called conditionally. - Tests cannot contain conditional logic. - Tests cannot be run conditionally. - Tests cannot be disabled. - You cannot use the `done()` callback. - Setup and teardown hooks cannot be duplicated. - Tests cannot be focused with `.only()`. - Test titles must be unique. - `node:test` cannot be imported. - Snapshots cannot use string interpolation. - `expect` must be within `it` or `test`. - Tests cannot use `return`. - Prefer `toHaveBeenCalledWith()` over `toHaveBeenCalled()`. - Prefer `.toBeGreaterThan()` over `(x > 5).toBe(true)`. - Use `describe.each` instead of manual loops. - Prefer `.toBe(5)` over `(x === 5).toBe(true)`. - All tests must start with `expect.assertions(number)`. - Use `expect.resolves(fn)` over `expect(await fn)`. - Hooks should be ordered as Vitest calls them: `beforeAll`, `beforeEach`, `afterEach`, `afterAll`. - Hooks should be at the top of the test. - Test titles should be lowercase. - Prefer `vi.fn().mockResolvedValue(val)` over `vi.fn().mockImplementation(() => Promise.resolve(val))`. - Prefer `vi.spyOn(Date, "now")` instead of overwriting the global `Date.now = vi.fn()`. - Use `.toStrictEqual()` over `.toEqual()`. - Use `.toBe()` over `.toStrictEqual()` for primitive values. - Use `.toBeFalsy()` over `.toBe(false)`. - Use `.toBeObject()` over `.toBeInstanceOf(Object)`. - Use `.toBeTruthy()` over `.toBe(true)`. - Use `.toContain()` over `.includes()`. - Use `.toHaveLength()` over `.length`. - Use `test.todo()` instead of `test.skip()`. - Setup and teardown code must be in a hook. - `.toThrow()` assertions require a message. - Tests must be within a `describe` block. - The `describe` callback should not have any parameters and cannot use `return`. - All `expect()` calls must have a custom message. - Tests must have a title that is not empty.