11import { describe , it , expect , vi , beforeAll , afterAll } from 'vitest' ;
22import { Command } from 'commander' ;
3- import { registerLintCommand } from '../../commands/lint.js' ;
3+ import { registerLintCommand , type LintIssue } from '../../commands/lint.js' ;
44import { mkdirSync , writeFileSync , rmSync } from 'node:fs' ;
55import { join } from 'node:path' ;
66import { tmpdir } from 'node:os' ;
@@ -14,10 +14,11 @@ function makeTmpFile(name: string, content: string): void {
1414async function runLint (
1515 args : string [ ] = [ ] ,
1616 format : string = 'json' ,
17+ version : string = '5.20.0' ,
1718) : Promise < string > {
1819 const program = new Command ( ) ;
1920 program . option ( '--format <format>' , '' , format ) ;
20- program . option ( '--version <version>' , '' , '5.20.0' ) ;
21+ program . option ( '--version <version>' , '' , version ) ;
2122 program . option ( '--lang <lang>' , '' , 'en' ) ;
2223 registerLintCommand ( program ) ;
2324 const logSpy = vi . spyOn ( console , 'log' ) . mockImplementation ( ( ) => { } ) ;
@@ -455,6 +456,153 @@ const App = () => (
455456 } ) ;
456457 } ) ;
457458
459+ // --- False positive regression tests (issue #45) ---
460+ describe ( 'deprecated false positives (#45)' , ( ) => {
461+ async function runLintV6 (
462+ file : string ,
463+ args : string [ ] = [ ] ,
464+ ) : Promise < { issues : LintIssue [ ] ; summary : any } > {
465+ return parseJson ( await runLint ( [ file , ...args ] , 'json' , '6.0.0' ) ) ;
466+ }
467+
468+ it ( 'should not flag non-antd component props as deprecated (SendTo message)' , async ( ) => {
469+ // Alert.message is deprecated in v6, but SendTo is a custom component
470+ makeTmpFile (
471+ 'false-pos-1.tsx' ,
472+ `import { Alert } from 'antd';
473+
474+ const SendTo = ({ message }: { message: string }) => <div>{message}</div>;
475+
476+ const App = () => (
477+ <div>
478+ <SendTo message="hello" />
479+ <Alert title="Warning" />
480+ </div>
481+ );
482+ ` ,
483+ ) ;
484+ const data = await runLintV6 ( join ( tmpDir , 'false-pos-1.tsx' ) , [ '--only' , 'deprecated' ] ) ;
485+ // SendTo is not an antd component, so no deprecation should be reported
486+ const messageIssues = data . issues . filter ( ( i : any ) => i . message . includes ( 'message' ) ) ;
487+ expect ( messageIssues ) . toHaveLength ( 0 ) ;
488+ } ) ;
489+
490+ it ( 'should not flag Button type as Divider deprecated type' , async ( ) => {
491+ // Divider.type is deprecated in v6, but Button.type is NOT
492+ makeTmpFile (
493+ 'false-pos-2.tsx' ,
494+ `import { Button, Divider } from 'antd';
495+
496+ const App = () => (
497+ <div>
498+ <Button type="dashed">Click</Button>
499+ <Button type="primary">OK</Button>
500+ <Divider />
501+ </div>
502+ );
503+ ` ,
504+ ) ;
505+ const data = await runLintV6 ( join ( tmpDir , 'false-pos-2.tsx' ) , [ '--only' , 'deprecated' ] ) ;
506+ // Button.type is NOT deprecated, should not be flagged
507+ const typeIssues = data . issues . filter ( ( i : any ) => i . message . includes ( 'type' ) ) ;
508+ expect ( typeIssues ) . toHaveLength ( 0 ) ;
509+ } ) ;
510+
511+ it ( 'should correctly flag Divider deprecated type prop' , async ( ) => {
512+ // Divider.type IS deprecated in v6
513+ makeTmpFile (
514+ 'false-pos-2b.tsx' ,
515+ `import { Divider } from 'antd';
516+
517+ const App = () => <Divider type="vertical" />;
518+ ` ,
519+ ) ;
520+ const data = await runLintV6 ( join ( tmpDir , 'false-pos-2b.tsx' ) , [ '--only' , 'deprecated' ] ) ;
521+ const typeIssues = data . issues . filter ( ( i : any ) => i . message . includes ( 'type' ) ) ;
522+ expect ( typeIssues ) . toHaveLength ( 1 ) ;
523+ expect ( typeIssues [ 0 ] . message ) . toContain ( 'Divider' ) ;
524+ } ) ;
525+
526+ it ( 'should correctly flag Alert deprecated message prop' , async ( ) => {
527+ // Alert.message IS deprecated in v6
528+ makeTmpFile (
529+ 'false-pos-1b.tsx' ,
530+ `import { Alert } from 'antd';
531+
532+ const App = () => <Alert message="Warning" />;
533+ ` ,
534+ ) ;
535+ const data = await runLintV6 ( join ( tmpDir , 'false-pos-1b.tsx' ) , [ '--only' , 'deprecated' ] ) ;
536+ const messageIssues = data . issues . filter ( ( i : any ) => i . message . includes ( 'message' ) ) ;
537+ expect ( messageIssues ) . toHaveLength ( 1 ) ;
538+ expect ( messageIssues [ 0 ] . message ) . toContain ( 'Alert' ) ;
539+ } ) ;
540+
541+ it ( 'should not flag Space split when only JS .split() method is used' , async ( ) => {
542+ // Space.split is deprecated in v6, but JS .split() is a method call
543+ makeTmpFile (
544+ 'false-pos-3.tsx' ,
545+ `import { Space, List } from 'antd';
546+
547+ const items = "a,b,c".split(",");
548+ const App = () => (
549+ <Space>
550+ <List split={false} />
551+ </Space>
552+ );
553+ ` ,
554+ ) ;
555+ const data = await runLintV6 ( join ( tmpDir , 'false-pos-3.tsx' ) , [ '--only' , 'deprecated' ] ) ;
556+ // List.split is NOT deprecated, Space doesn't use split prop here
557+ // JS .split() method call should not be flagged
558+ const splitIssues = data . issues . filter ( ( i : any ) => i . message . includes ( 'split' ) ) ;
559+ expect ( splitIssues ) . toHaveLength ( 0 ) ;
560+ } ) ;
561+
562+ it ( 'should correctly flag Space deprecated split prop' , async ( ) => {
563+ // Space.split IS deprecated in v6
564+ makeTmpFile (
565+ 'false-pos-3b.tsx' ,
566+ `import { Space } from 'antd';
567+
568+ const App = () => <Space split="|">items</Space>;
569+ ` ,
570+ ) ;
571+ const data = await runLintV6 ( join ( tmpDir , 'false-pos-3b.tsx' ) , [ '--only' , 'deprecated' ] ) ;
572+ const splitIssues = data . issues . filter ( ( i : any ) => i . message . includes ( 'split' ) ) ;
573+ expect ( splitIssues ) . toHaveLength ( 1 ) ;
574+ expect ( splitIssues [ 0 ] . message ) . toContain ( 'Space' ) ;
575+ } ) ;
576+
577+ it ( 'should report correct line numbers for deprecated props' , async ( ) => {
578+ makeTmpFile (
579+ 'false-pos-lines.tsx' ,
580+ `import { Alert, Divider, Space } from 'antd';
581+
582+ const App = () => (
583+ <div>
584+ <Alert message="Warning" />
585+ <Divider type="vertical" />
586+ <Space split="|">items</Space>
587+ </div>
588+ );
589+ ` ,
590+ ) ;
591+ const data = await runLintV6 ( join ( tmpDir , 'false-pos-lines.tsx' ) , [ '--only' , 'deprecated' ] ) ;
592+ expect ( data . issues ) . toHaveLength ( 3 ) ;
593+ // Line numbers should be non-zero and correctly point to each element
594+ for ( const issue of data . issues ) {
595+ expect ( issue . line ) . toBeGreaterThan ( 0 ) ;
596+ }
597+ const alertIssue = data . issues . find ( ( i : any ) => i . message . includes ( 'Alert' ) ) ;
598+ const dividerIssue = data . issues . find ( ( i : any ) => i . message . includes ( 'Divider' ) ) ;
599+ const spaceIssue = data . issues . find ( ( i : any ) => i . message . includes ( 'Space' ) ) ;
600+ expect ( alertIssue ! . line ) . toBe ( 5 ) ;
601+ expect ( dividerIssue ! . line ) . toBe ( 6 ) ;
602+ expect ( spaceIssue ! . line ) . toBe ( 7 ) ;
603+ } ) ;
604+ } ) ;
605+
458606 // --- Edge cases ---
459607 describe ( 'edge cases' , ( ) => {
460608 it ( 'skips files without antd reference' , async ( ) => {
0 commit comments