@@ -7,6 +7,51 @@ import fs from 'fs';
77import { QA as CoreQA } from '@objectstack/core' ;
88import { QA } from '@objectstack/spec' ;
99
10+ /**
11+ * Resolve a glob-like pattern to matching file paths.
12+ * Supports `*` (single segment wildcard) and `**` (recursive wildcard).
13+ * Falls back to direct file path if no glob characters are present.
14+ */
15+ function resolveGlob ( pattern : string ) : string [ ] {
16+ // Direct file path — no wildcards
17+ if ( ! pattern . includes ( '*' ) ) {
18+ return fs . existsSync ( pattern ) ? [ pattern ] : [ ] ;
19+ }
20+
21+ // Split pattern into the static base directory and the glob portion
22+ const parts = pattern . split ( path . sep . replace ( '\\' , '/' ) ) ;
23+ // Also handle forward-slash on Windows
24+ const segments = pattern . includes ( '/' ) ? pattern . split ( '/' ) : parts ;
25+
26+ let baseDir = '.' ;
27+ let globStart = 0 ;
28+ for ( let i = 0 ; i < segments . length ; i ++ ) {
29+ if ( segments [ i ] . includes ( '*' ) ) {
30+ globStart = i ;
31+ break ;
32+ }
33+ baseDir = i === 0 ? segments [ i ] : path . join ( baseDir , segments [ i ] ) ;
34+ }
35+
36+ if ( ! fs . existsSync ( baseDir ) ) return [ ] ;
37+
38+ // Convert the glob portion into a RegExp
39+ const globPortion = segments . slice ( globStart ) . join ( '/' ) ;
40+ const regexStr = globPortion
41+ . replace ( / \. / g, '\\.' ) // escape dots
42+ . replace ( / \* \* \/ / g, '(.+/)?' ) // ** matches any directory depth
43+ . replace ( / \* \* / g, '.*' ) // trailing ** without slash
44+ . replace ( / \* / g, '[^/]*' ) ; // * matches within a single segment
45+ const regex = new RegExp ( `^${ regexStr } $` ) ;
46+
47+ // Recursively read all files under baseDir
48+ const entries = fs . readdirSync ( baseDir , { recursive : true , encoding : 'utf-8' } ) as string [ ] ;
49+ return entries
50+ . filter ( entry => regex . test ( entry . replace ( / \\ / g, '/' ) ) )
51+ . map ( entry => path . join ( baseDir , entry ) )
52+ . filter ( fullPath => fs . statSync ( fullPath ) . isFile ( ) ) ;
53+ }
54+
1055export const testCommand = new Command ( 'test' )
1156 . description ( 'Run Quality Protocol test scenarios against a running server' )
1257 . argument ( '[files]' , 'Glob pattern for test files (e.g. "qa/*.test.json")' , 'qa/*.test.json' )
@@ -21,23 +66,8 @@ export const testCommand = new Command('test')
2166 const adapter = new CoreQA . HttpTestAdapter ( options . url , options . token ) ;
2267 const runner = new CoreQA . TestRunner ( adapter ) ;
2368
24- // 2. Find Files (Simple implementation for now)
25- // TODO: Use glob
26- const cwd = process . cwd ( ) ;
27- const testFiles : string [ ] = [ ] ;
28-
29- // Very basic file finding for demo - assume explicit path or check local dir
30- if ( fs . existsSync ( filesPattern ) ) {
31- testFiles . push ( filesPattern ) ;
32- } else {
33- // Simple directory scan
34- const dir = path . dirname ( filesPattern ) ;
35- const ext = path . extname ( filesPattern ) ;
36- if ( fs . existsSync ( dir ) ) {
37- const files = fs . readdirSync ( dir ) . filter ( f => f . endsWith ( ext ) || f . endsWith ( '.json' ) ) ;
38- files . forEach ( f => testFiles . push ( path . join ( dir , f ) ) ) ;
39- }
40- }
69+ // 2. Find test files using glob-style pattern matching
70+ const testFiles : string [ ] = resolveGlob ( filesPattern ) ;
4171
4272 if ( testFiles . length === 0 ) {
4373 console . warn ( chalk . yellow ( `No test files found matching: ${ filesPattern } ` ) ) ;
0 commit comments