createTestUtils
This is the main function that provides access to other testing functions (createTestRule
and createTestRuleConfig
) bound to your testing platform.
Here you can configure some useful defaults described below.
Options
You can always see the actual options in the source code here.
testingFunctions
Testing functions provided by your test platform, if you want to be explicit.
This is also an example of recommended use case.
import { assert, describe, expect, it } from 'vitest';
const { createTestRule, testRuleConfig } = createTestUtils({
testFunctions: { assert, describe, expect, it },
});
import { describe, expect, it } from '@jest/globals';
const { createTestRule, testRuleConfig } = createTestUtils({
testFunctions: { assert, describe, expect, it },
});
import * as assert from 'node:assert/strict';
import { describe, it } from 'node:test';
const { createTestRule, testRuleConfig } = createTestUtils({
testFunctions: { assert, describe, it },
});
Info
Passing functions for testing is optional.
If your test platform injects them into the global scope, the module will pick them on its own.
plugins
Maps to Stylelint's plugins
configuration property, has the same signature.
Expected either the path to the JS file that provides your rule, or its contents, or an array of such elements in the case you are testing a plugin pack.
Minimal example:
import stylelint from 'stylelint';
import { fooRule } from './rules/foo-rule.js';
import { barRule } from './rules/bar-rule.js';
export default [
stylelint.createPlugin('scope/foo-rule', fooRule),
stylelint.createPlugin('scope/bar-rule', barRule),
];
import { assert, describe, expect, it } from 'vitest';
import plugins from './src/index.js';
const { createTestRule, testRuleConfig } = createTestUtils({
testFunctions: { assert, describe, expect, it },
plugins,
});
extraRules
Object in form compatible with Stylelint's rules
property, that allows to run stylelint
with extra rules in addition to the one being tested.
/**
* @default {}
*/
type ExtraRules = Record<string, any>;
It is a good practice to test a rule in isolation from all others, but in edge cases you may want to test how rules work together - this setting allows you to do that.
customSyntax
Maps to Stylelint's customSyntax
configuration property, has the same signature.
Can be configured for separate groups of tests, but if your plugin works exclusively with a single syntax (e.g. SCSS), it may make sense to specify a parser here to reduce the amount of boilerplate code in the test files.
Example:
import { assert, describe, expect, it } from 'vitest';
import plugins from './src/index.js';
const { createTestRule, testRuleConfig } = createTestUtils({
testFunctions: { assert, describe, expect, it },
customSyntax: 'postcss-scss',
plugins,
});
testGroupWithoutDescriptionAppearance
The option controls how test groups are displayed in the console output if they do not have a description. It is recommended to always use a description for a group of tests - it allows to find the right set faster.
/**
* @default 'group-index'
*/
type testGroupWithoutDescriptionAppearance = 'group-index' | 'config' | 'line-in-file';
We have three options:
- Use the group index (default);
- Use the stringified
config
property passed to thetestRule
utility; - Line number in the file where the
testRule
call is located (experimental).
The output of all of them is shown below:
group-index
(default)
A clean, minimalistic look that helps to quickly locate the right group of tests (especially when using folding feature in the editor).
Note: this is a sequence number, not an index in the programming sense, so it starts with 1
.
✓ {rule-name}: group №1 (9)
✓ accept (5)
✓ ...
✓ reject (4)
✓ ...
✓ {rule-name}: group №2 (9)
✓ accept (5)
✓ ...
✓ reject (4)
✓ ...
config
Using configuration as a hint seems logical, but practice shows that it is not very useful due to the fact that output is almost always not the same as specified in the file, meaning that you can not just select it and search the file - and you have to count the index, or extract individual properties from the config and search for them - this is inconvenient.
The option exists to ensure that the project can be a drop-in replacement for an existing solutions for testing Stylelint plugins that did not provide the ability to set a description for a test group.
✓ {rule-name}: [ true ] (9)
✓ accept (5)
✓ ...
✓ reject (4)
✓ ...
✓ {rule-name}: [ true, { ignore: 'foo' } ] (9)
✓ accept (5)
✓ ...
✓ reject (4)
✓ ...
error-line
(experimental)
Using the group-index
property is usually more convenient than config
, but for existing codebases with a large number of tests it can still be quite painful and require routine counting work.
This option allows you to use the line number in the file where the testRule
call is located as a description of the test script group.
testRule({
config: [true],
accept: [
{ /* ... */ },
{ /* ... */ },
],
});
testRule({
config: [true],
accept: [
{ /* ... */ },
{ /* ... */ },
],
});
✓ {rule-name}: line 1 in the source file (9)
✓ accept (5)
✓ ...
✓ reject (4)
✓ ...
✓ {rule-name}: line 9 in the source file (9)
✓ accept (5)
✓ ...
✓ reject (4)
✓ ...
testCaseWithoutDescriptionAppearance
The option controls how test cases are displayed in the console output if they do not have a description. It is recommended to always use a description for a test cases - it allows to find the right one faster.
/**
* @default 'case-index'
*/
type TestCaseWithoutDescriptionAppearance = 'case-index' | 'code';
We have two options: use the test index or its code, the output of both is shown below:
case-index
(default)
A clean, minimalistic look that helps to quickly locate the right test case.
✓ {rule-name}: group №1 (9)
✓ accept (2)
✓ Accept test case №1
✓ Accept test case №2
✓ reject (2)
✓ Reject test case №1
✓ Reject test case №2
code
Using the code as a hint seems logical, but practice shows that it is not very useful due to the fact that output is usually pretty verbose and not the same as written in the file (in case of multi-line code), meaning that you can not just select the code and search the file - and you have to count the index, or extract individual entities from the code and search for them - this is inconvenient.
The option exists to ensure that the project can be a drop-in replacement for an existing solutions for testing Stylelint plugins that did not provide the ability to set a description for a test cases.
✓ {rule-name}: group №1 (9)
✓ accept (2)
✓ '.the-component {}'
✓ '.the-component {\n\t&__element {}\n}'
✓ reject (1)
✓ '.the-component {\n\t$b: #{&};\n\n\t#{$b} {}\n}'
autoStripIndent
Controls whether indentation should be automatically stripped out of code blocks.
/**
* @default false
*/
type AutoStripIndent = boolean;
Um, why?
It can be quite tedious to calculate error positions when testing complex multi-line rules.
Let's pretend we have the following sample code and we expect to see an error highlighting on the .another-component
selector:
{
description: 'Side-effect within `@media`-query on the root level',
code: `
.the-component {}
@media (max-width: 320px) {
.another-component {}
}
`,
}
You would say that the error should start on line 4
and column 3
, wouldn't you?
But in fact, for this particular code block, the error will start on line 5
and column 9
!
This is because the input is a string that contains all linebreaks and indentation:
`
.the-component {}
@media (max-width: 320px) {
.another-component {}
}
`
Not very similar to how our CSS usually looks like.
Imagine you are designing a rule that interacts with indentation...
With the autoStripIndent
option enabled, all code blocks automatically remove the start/end spaces as well as the extra indentation, so the string becomes exactly what we would see in the CSS file:
.the-component {}
@media (max-width: 320px) {
.another-component {}
}
Note
You can always redefine this global setting for a group of tests or a single test.