eslint-plugin-vibe-coder
Version:
ESLint plugin with custom rules to prevent common bad practices made by robots (and humans who code like robots)
284 lines (283 loc) • 8.26 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// @ts-expect-error No types published for @typescript-eslint/rule-tester
const rule_tester_1 = require("@typescript-eslint/rule-tester");
const no_optional_properties_1 = require("./no-optional-properties");
// Provide afterAll for RuleTester compatibility with Mocha
globalThis.afterAll = after;
const ruleTester = new rule_tester_1.RuleTester({
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 2022,
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
},
});
describe('no-optional-properties', () => {
it('should pass for valid cases', () => {
const validCases = [
// Valid: Required properties
{
code: `
interface User {
name: string;
email: string;
}
`,
},
{
code: `
type Config = {
port: number;
host: string;
}
`,
},
// Valid: Union types without undefined/null
{
code: `
interface User {
role: 'admin' | 'user' | 'guest';
status: 'active' | 'inactive';
}
`,
},
// Valid: Complex types without optional
{
code: `
interface Product {
id: string;
name: string;
price: number;
category: 'electronics' | 'clothing' | 'books';
tags: string[];
metadata: Record<string, unknown>;
}
`,
},
// Valid: Nested interfaces
{
code: `
interface Address {
street: string;
city: string;
country: string;
}
interface Customer {
id: string;
name: string;
address: Address;
preferences: {
theme: 'light' | 'dark';
language: 'en' | 'es' | 'fr';
};
}
`,
},
// Valid: Generic types
{
code: `
interface ApiResponse<T> {
data: T;
status: number;
message: string;
}
`,
},
// Valid: Function types
{
code: `
interface EventHandler {
onSuccess: (data: unknown) => void;
onError: (error: Error) => void;
onComplete: () => void;
}
`,
},
// Valid: Array types
{
code: `
interface Collection {
items: string[];
count: number;
isEmpty: boolean;
}
`,
},
];
ruleTester.run('eslint-plugin-vibe-coder/no-optional-properties', no_optional_properties_1.noOptionalProperties, {
valid: validCases,
invalid: [],
});
});
it('should fail for invalid cases', () => {
const invalidCases = [
// Invalid: Optional property
{
code: `
interface User {
name?: string;
email: string;
}
`,
errors: [{ messageId: 'noOptionalProperty' }],
},
{
code: `
type Config = {
port?: number;
host: string;
}
`,
errors: [{ messageId: 'noOptionalProperty' }],
},
// Invalid: Union with undefined
{
code: `
interface User {
name: string | undefined;
email: string;
}
`,
errors: [{ messageId: 'noOptionalProperty' }],
},
// Invalid: Union with null
{
code: `
interface User {
avatar: string | null;
email: string;
}
`,
errors: [{ messageId: 'noOptionalProperty' }],
},
// Invalid: Optional property with comment (should still fail)
{
code: `
interface User {
//optional: This field is only set after email verification
verifiedAt?: Date;
name: string;
email: string;
}
`,
errors: [{ messageId: 'noOptionalProperty' }],
},
// Invalid: Multiple optional properties
{
code: `
interface FormData {
firstName?: string;
lastName?: string;
email?: string;
phone?: string;
}
`,
errors: [
{ messageId: 'noOptionalProperty' },
{ messageId: 'noOptionalProperty' },
{ messageId: 'noOptionalProperty' },
{ messageId: 'noOptionalProperty' },
],
},
// Invalid: Complex union with undefined
{
code: `
interface ApiResponse {
data: User[] | undefined;
error: string | undefined;
timestamp: number;
}
`,
errors: [
{ messageId: 'noOptionalProperty' },
{ messageId: 'noOptionalProperty' },
],
},
// Invalid: Nested optional properties
{
code: `
interface Settings {
theme: {
primary?: string;
secondary?: string;
};
notifications: {
email?: boolean;
push?: boolean;
};
}
`,
errors: [
{ messageId: 'noOptionalProperty' },
{ messageId: 'noOptionalProperty' },
{ messageId: 'noOptionalProperty' },
{ messageId: 'noOptionalProperty' },
],
},
// Invalid: Generic with optional
{
code: `
interface CacheEntry<T> {
key: string;
value?: T;
expiresAt: number;
}
`,
errors: [{ messageId: 'noOptionalProperty' }],
},
// Invalid: Function with optional parameters
{
code: `
interface Callback {
onSuccess?: (data: unknown) => void;
onError?: (error: Error) => void;
onComplete?: () => void;
}
`,
errors: [
{ messageId: 'noOptionalProperty' },
{ messageId: 'noOptionalProperty' },
{ messageId: 'noOptionalProperty' },
],
},
// Invalid: Array with optional elements
{
code: `
interface TodoList {
items: (string | undefined)[];
completed: (boolean | null)[];
priority: number;
}
`,
errors: [
{ messageId: 'noOptionalProperty' },
{ messageId: 'noOptionalProperty' },
],
},
// Invalid: Mixed valid and invalid
{
code: `
interface Mixed {
required: string;
optional?: number;
alsoRequired: boolean;
nullable: string | null;
validUnion: 'a' | 'b' | 'c';
}
`,
errors: [
{ messageId: 'noOptionalProperty' },
{ messageId: 'noOptionalProperty' },
],
},
];
ruleTester.run('eslint-plugin-vibe-coder/no-optional-properties', no_optional_properties_1.noOptionalProperties, {
valid: [],
invalid: invalidCases,
});
});
});
//# sourceMappingURL=no-optional-properties.test.js.map