test : add coverage for reference-based visitor creator

This commit is contained in:
Ante Budimir 2025-10-17 09:43:28 +03:00
parent d4bac62046
commit 24681ebad9

View file

@ -0,0 +1,616 @@
import type { Rule } from 'eslint';
import tsParser from '@typescript-eslint/parser';
import { run } from 'eslint-vitest-rule-tester';
import alphabeticalOrderRule from '../../alphabetical-order/rule-definition.js';
import concentricOrderRule from '../../concentric-order/rule-definition.js';
import { createReferenceBasedNodeVisitors } from '../reference-based-visitor-creator.js';
import type { OrderingStrategy } from '../../types.js';
// Test alphabetical order with reference-based visitor
run({
name: 'reference-based-visitor/alphabetical',
rule: alphabeticalOrderRule,
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaVersion: 2022,
sourceType: 'module',
},
},
valid: [
// fontFace with src first (special fontFace ordering)
`
import { fontFace } from '@vanilla-extract/css';
const myFont = fontFace({
src: 'url("/fonts/my-font.woff2")',
fontFamily: 'MyFont',
fontWeight: 'bold'
});
`,
// globalFontFace with src first (special fontFace ordering)
`
import { globalFontFace } from '@vanilla-extract/css';
globalFontFace('MyFont', {
src: 'url("/fonts/my-font.woff2")',
fontFamily: 'MyFont',
fontWeight: 'bold'
});
`,
// style with alphabetical order
`
import { style } from '@vanilla-extract/css';
const myStyle = style({
backgroundColor: 'blue',
color: 'white',
margin: '10px'
});
`,
// styleVariants with alphabetical order
`
import { styleVariants } from '@vanilla-extract/css';
const variants = styleVariants({
primary: {
backgroundColor: 'blue',
color: 'white'
},
secondary: {
backgroundColor: 'red',
color: 'white'
}
});
`,
// keyframes with alphabetical order
`
import { keyframes } from '@vanilla-extract/css';
const fadeIn = keyframes({
'0%': {
opacity: 0,
transform: 'scale(0.9)'
},
'100%': {
opacity: 1,
transform: 'scale(1)'
}
});
`,
// globalStyle with alphabetical order
`
import { globalStyle } from '@vanilla-extract/css';
globalStyle('.button', {
backgroundColor: 'blue',
color: 'white',
padding: '10px'
});
`,
// globalKeyframes with alphabetical order
`
import { globalKeyframes } from '@vanilla-extract/css';
globalKeyframes('fadeIn', {
'0%': {
opacity: 0,
transform: 'scale(0.9)'
},
'100%': {
opacity: 1,
transform: 'scale(1)'
}
});
`,
// recipe with alphabetical order
`
import { recipe } from '@vanilla-extract/recipes';
const button = recipe({
base: {
backgroundColor: 'blue',
color: 'white'
},
variants: {
size: {
small: {
fontSize: '12px',
padding: '4px'
},
large: {
fontSize: '16px',
padding: '8px'
}
}
}
});
`,
],
invalid: [
// style with wrong order
{
code: `
import { style } from '@vanilla-extract/css';
const myStyle = style({
margin: '10px',
backgroundColor: 'blue'
});
`,
errors: [{ messageId: 'alphabeticalOrder' }],
},
// globalStyle with wrong order
{
code: `
import { globalStyle } from '@vanilla-extract/css';
globalStyle('.button', {
padding: '10px',
backgroundColor: 'blue'
});
`,
errors: [{ messageId: 'alphabeticalOrder' }],
},
],
});
// Test concentric order with reference-based visitor
run({
name: 'reference-based-visitor/concentric',
rule: concentricOrderRule,
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaVersion: 2022,
sourceType: 'module',
},
},
valid: [
// style with concentric order
`
import { style } from '@vanilla-extract/css';
const myStyle = style({
display: 'flex',
backgroundColor: 'blue'
});
`,
],
invalid: [
// style with wrong concentric order
{
code: `
import { style } from '@vanilla-extract/css';
const myStyle = style({
backgroundColor: 'blue',
display: 'flex'
});
`,
errors: [{ messageId: 'incorrectOrder' }],
},
],
});
// Test edge cases
run({
name: 'reference-based-visitor/edge-cases',
rule: alphabeticalOrderRule,
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaVersion: 2022,
sourceType: 'module',
},
},
valid: [
// fontFace with no arguments
`
import { fontFace } from '@vanilla-extract/css';
const myFont = fontFace();
`,
// globalFontFace with only one argument
`
import { globalFontFace } from '@vanilla-extract/css';
globalFontFace('MyFont');
`,
// style with no arguments
`
import { style } from '@vanilla-extract/css';
const myStyle = style();
`,
// globalStyle with only one argument
`
import { globalStyle } from '@vanilla-extract/css';
globalStyle('.button');
`,
// Non-identifier callee (should be ignored)
`
import { style } from '@vanilla-extract/css';
const obj = {
style: (props) => props
};
obj.style({ margin: '10px', backgroundColor: 'blue' });
`,
// Untracked function (should be ignored)
`
import { style } from '@vanilla-extract/css';
function customFunction(props) {
return props;
}
customFunction({ margin: '10px', backgroundColor: 'blue' });
`,
// fontFace with correct alphabetical order after src
`
import { fontFace } from '@vanilla-extract/css';
const myFont = fontFace({
src: 'url("/fonts/my-font.woff2")',
fontFamily: 'MyFont',
fontWeight: 'bold'
});
`,
// globalFontFace with correct alphabetical order after src
`
import { globalFontFace } from '@vanilla-extract/css';
globalFontFace('MyFont', {
src: 'url("/fonts/my-font.woff2")',
fontFamily: 'MyFont',
fontWeight: 'bold'
});
`,
],
invalid: [
// fontFace with wrong order (should report error)
{
code: `
import { fontFace } from '@vanilla-extract/css';
const myFont = fontFace({
fontWeight: 'bold',
fontFamily: 'MyFont',
src: 'url("/fonts/my-font.woff2")'
});
`,
errors: [{ messageId: 'fontFaceOrder' }],
},
// globalFontFace with wrong order (should report error)
{
code: `
import { globalFontFace } from '@vanilla-extract/css';
globalFontFace('MyFont', {
fontWeight: 'bold',
src: 'url("/fonts/my-font.woff2")'
});
`,
errors: [{ messageId: 'fontFaceOrder' }],
},
],
});
// Test userDefinedGroupOrder strategy with reference-based visitor
run({
name: 'reference-based-visitor/user-defined-order',
rule: {
meta: {
type: 'suggestion',
docs: {
description: 'Test user-defined group order',
},
messages: {
incorrectOrder: 'Properties should be ordered according to user-defined groups.',
},
fixable: 'code',
},
create(context: Rule.RuleContext) {
return createReferenceBasedNodeVisitors(
context,
'userDefinedGroupOrder',
['display', 'position', 'color', 'backgroundColor'],
'alphabetical'
);
},
},
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaVersion: 2022,
sourceType: 'module',
},
},
valid: [
// style with correct user-defined order
`
import { style } from '@vanilla-extract/css';
const myStyle = style({
display: 'flex',
color: 'blue'
});
`,
// recipe with user-defined order
`
import { recipe } from '@vanilla-extract/recipes';
const button = recipe({
base: {
display: 'flex',
color: 'blue'
}
});
`,
],
invalid: [
// style with wrong user-defined order
{
code: `
import { style } from '@vanilla-extract/css';
const myStyle = style({
color: 'blue',
display: 'flex'
});
`,
errors: [{ messageId: 'incorrectOrder' }],
},
],
});
// Test userDefinedGroupOrder with empty array (should fallback to alphabetical)
run({
name: 'reference-based-visitor/user-defined-order-empty',
rule: {
meta: {
type: 'suggestion',
docs: {
description: 'Test user-defined group order with empty array',
},
messages: {
alphabeticalOrder: 'Properties should be in alphabetical order.',
},
fixable: 'code',
},
create(context: Rule.RuleContext) {
return createReferenceBasedNodeVisitors(
context,
'userDefinedGroupOrder',
[],
'alphabetical'
);
},
},
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaVersion: 2022,
sourceType: 'module',
},
},
valid: [
// style with alphabetical order (fallback)
`
import { style } from '@vanilla-extract/css';
const myStyle = style({
backgroundColor: 'blue',
color: 'white'
});
`,
],
invalid: [
// style with wrong alphabetical order
{
code: `
import { style } from '@vanilla-extract/css';
const myStyle = style({
color: 'white',
backgroundColor: 'blue'
});
`,
errors: [{ messageId: 'alphabeticalOrder' }],
},
],
});
// Test default case in ordering strategy
run({
name: 'reference-based-visitor/default-strategy',
rule: {
meta: {
type: 'suggestion',
docs: {
description: 'Test default ordering strategy',
},
messages: {
alphabeticalOrder: 'Properties should be in alphabetical order.',
},
fixable: 'code',
},
create(context: Rule.RuleContext) {
return createReferenceBasedNodeVisitors(
context,
'unknown' as OrderingStrategy,
undefined,
undefined
);
},
},
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaVersion: 2022,
sourceType: 'module',
},
},
valid: [
// Should fall back to alphabetical
`
import { style } from '@vanilla-extract/css';
const myStyle = style({
backgroundColor: 'blue',
color: 'white'
});
`,
],
invalid: [
{
code: `
import { style } from '@vanilla-extract/css';
const myStyle = style({
color: 'white',
backgroundColor: 'blue'
});
`,
errors: [{ messageId: 'alphabeticalOrder' }],
},
],
});
// Test non-Identifier callee (should be ignored)
run({
name: 'reference-based-visitor/non-identifier-callee',
rule: alphabeticalOrderRule,
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaVersion: 2022,
sourceType: 'module',
},
},
valid: [
// Member expression callee should be ignored
`
import { style } from '@vanilla-extract/css';
const obj = {
style: (props) => props
};
obj.style({ margin: '10px', backgroundColor: 'blue' });
`,
],
invalid: [],
});
// Test functions with no arguments
run({
name: 'reference-based-visitor/no-arguments',
rule: alphabeticalOrderRule,
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaVersion: 2022,
sourceType: 'module',
},
},
valid: [
// fontFace with no arguments
`
import { fontFace } from '@vanilla-extract/css';
const myFont = fontFace();
`,
// globalFontFace with only one argument
`
import { globalFontFace } from '@vanilla-extract/css';
globalFontFace('MyFont');
`,
// style with no arguments
`
import { style } from '@vanilla-extract/css';
const myStyle = style();
`,
// globalStyle with only one argument
`
import { globalStyle } from '@vanilla-extract/css';
globalStyle('.button');
`,
// globalKeyframes with only one argument
`
import { globalKeyframes } from '@vanilla-extract/css';
globalKeyframes('fadeIn');
`,
],
invalid: [],
});
// Test concentric order with recipe
run({
name: 'reference-based-visitor/concentric-recipe',
rule: concentricOrderRule,
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaVersion: 2022,
sourceType: 'module',
},
},
valid: [
// recipe with concentric order
`
import { recipe } from '@vanilla-extract/recipes';
const button = recipe({
base: {
display: 'flex',
backgroundColor: 'blue'
}
});
`,
],
invalid: [
// recipe with wrong concentric order
{
code: `
import { recipe } from '@vanilla-extract/recipes';
const button = recipe({
base: {
backgroundColor: 'blue',
display: 'flex'
}
});
`,
errors: [{ messageId: 'incorrectOrder' }],
},
],
});