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:
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.
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, preferfunction name()
declarations overconst 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
orarguments.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)
orwhile(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
orbreak
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
withinelse
, useelse 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
withBigInt
orSymbol
. - Do not use
new
withString
,Number
, orBoolean
. - Do not use octal escapes (e.g.
\8
,\2
). - Do not call the
Math
,JSON
,Reflect
,Atomics
, orIntl
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")
overfoo.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 callingsuper
in constructors that callsuper
. - 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
orinstanceOf
, 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
orFIX
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 explicitx = 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 ofarguments
. - 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
, useNumber.isNaN()
. typeof
comparators must be a valid return type oftypeof
.- 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
<Object>.toString()
on objects. PreferJSON.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
orunknown
). - 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
andclass Foo
, useinterface Foo
andclass 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 anError
instance.- Object properties should be destructured, rather than assigned to variables via direct access.
- Use
<Array>.find()
instead of<Array>.filter()[0]
to find an element in an array. - Use
<Array>.includes()
instead of<Array>.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
<RegExp>.exec
over<String>.match()
. - Class methods that return
this
should havethis
as the return type, not the class. - When testing the beginning or end of a string, use
<String>.startsWith()
or<String>.endsWith()
. - Functions which return a Promise must use the
async
keyword. <Array>.sort()
should always be given a compare function.async
functions must useawait
, 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
orundefined
. - 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 typeunknown
. - Function overload signatures must be grouped together.
- Array type definitions should use the generic
Array<T>
overT[]
. @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<string, T>
over{ [key: string]: T}
. - Do not use any type assertions (
x as T
). - Object types should be defined with
interface
overtype
. - 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
, thenmethods
. - Method types should use the property signature
func: (arg: string) => string
. - Variables should use
camelCase
, types and classes should usePascalCase
. 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 }
overimport { 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()
, notnew()
. Interfaces for the class should usenew()
, notconstructor()
. - 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
orunknown
. - 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
, notBoolean
). - Use
as const
over literal types. - Enum values should always be explicitly initialised.
- The
for...of
loop should be used over thefor
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. Alldisable
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 ofpackage
,private
,protected
, orpublic
.- 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 toNaomi'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 haveerror
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 afor...of
loop. - Use Unicode escape sequences over hexadecimal escape sequences.
- Use
Array.isArray()
overinstanceof Array
. GET
orHEAD
requests should never have a body.- Do not declare variables that start with
new
orclass
. - Do not use
.length
as the argument to.slice()
. - Use
Buffer.from()
, notnew 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
overfindIndex
. - Prefer
.some()
over.filter().length
. - Prefer
.at()
for accessing array and string indices. - Prefer
.codePointAt()
over.charCodeAt()
. - Prefer
Date.now()
overnew 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()
overisNaN()
. - Use
Object.fromEntries
to create an object from key-value pairs. - If a
catch
block does not use theerror
parameter, the parameter should be omitted. - Prefer calling prototype methods over instance methods.
- Prefer using
Set.has()
overArray.includes
to check for existence. - Prefer using
Set.size
overArray.length
. - Prefer using the spread operator over
Array.concat()
orString.split("")
. - Prefer using
String.replaceAll()
over regex searches. - Prefer
String.slice()
overString.substr[ing]()
. - Prefer
String.startsWith()
or.endsWith()
overRegExp.test()
. - Prefer
String.trim[Start|End]()
overString.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 ofenabled
. - Buttons must always have a
type
attribute. - If a radio button or checkbox is checked, it must be
readonly
or have anonChange
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
, orobject
. forwardRef
components must useref
.- Components should always be defined as named arrow functions.
- State should have matching names, e.g.
[color, setColor]
. iframes
should always use thesandbox
attribute.- Boolean properties should always be explicitly written:
<Component prop={true}>
. - 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 ? <Component> : null
overcondition && <Component>
. - Element text should always be wrapped in JSX containers.
- You should never use
javascript:
urls. target=_blank
must always be accompanied withrel=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 anysetState
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
incomponentDidMount
orcomponentDidUpdate
orcomponentWillUpdate
. - 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()
, ornth()
. - You cannot use
page.pause()
. - You cannot use
page.locator()
. Prefer specific methods likepage.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()
orpage.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()
overlocator.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
.
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 withinit
ortest
.- Tests cannot use
return
. - Prefer
toHaveBeenCalledWith()
overtoHaveBeenCalled()
. - 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)
overexpect(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)
overvi.fn().mockImplementation(() => Promise.resolve(val))
. - Prefer
vi.spyOn(Date, "now")
instead of overwriting the globalDate.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 oftest.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 usereturn
. - All
expect()
calls must have a custom message. - Tests must have a title that is not empty.