1+ import assert from "node:assert/strict" ;
2+ import { describe , it , TestContext } from "node:test" ;
3+ import path from "node:path" ;
4+ import fs from "node:fs" ;
5+ import os from "node:os" ;
6+
7+ import { EXAMPLES_DIR , findCMakeProjects } from "./cmake-projects.mjs" ;
8+
9+ function setupTempDirectory ( context : TestContext , files : Record < string , string > ) {
10+ const tempDirectoryPath = fs . realpathSync (
11+ fs . mkdtempSync ( path . join ( os . tmpdir ( ) , "cmake-projects-test-" ) )
12+ ) ;
13+
14+ context . after ( ( ) => {
15+ fs . rmSync ( tempDirectoryPath , { recursive : true , force : true } ) ;
16+ } ) ;
17+
18+ for ( const [ filePath , content ] of Object . entries ( files ) ) {
19+ const fullPath = path . join ( tempDirectoryPath , filePath ) ;
20+ fs . mkdirSync ( path . dirname ( fullPath ) , { recursive : true } ) ;
21+ fs . writeFileSync ( fullPath , content , "utf8" ) ;
22+ }
23+
24+ return tempDirectoryPath ;
25+ }
26+
27+ describe ( "EXAMPLES_DIR" , ( ) => {
28+ it ( "should resolve to a valid platform-specific path" , ( ) => {
29+ // Check that EXAMPLES_DIR is an absolute path
30+ assert ( path . isAbsolute ( EXAMPLES_DIR ) , "EXAMPLES_DIR should be absolute" ) ;
31+
32+ // On Windows, should not start with /
33+ if ( process . platform === "win32" ) {
34+ assert (
35+ ! EXAMPLES_DIR . startsWith ( "/" ) ,
36+ "Windows path should not start with /"
37+ ) ;
38+ // Should match Windows path pattern (e.g., C:\... or D:\...)
39+ assert (
40+ / ^ [ A - Z a - z ] : [ \\ / ] / . test ( EXAMPLES_DIR ) ,
41+ "Windows path should start with drive letter"
42+ ) ;
43+ } else {
44+ // On Unix-like systems, should start with /
45+ assert (
46+ EXAMPLES_DIR . startsWith ( "/" ) ,
47+ "Unix path should start with /"
48+ ) ;
49+ }
50+ } ) ;
51+
52+ it ( "should work correctly with path.join operations" , ( context ) => {
53+ const tempDir = setupTempDirectory ( context , {
54+ "test/subdir/file.txt" : "test content" ,
55+ } ) ;
56+
57+ // Simulate what happens in copy-examples.mts
58+ const relativePath = "test/subdir" ;
59+ const joinedPath = path . join ( tempDir , relativePath ) ;
60+
61+ // The joined path should exist and be accessible
62+ assert ( fs . existsSync ( joinedPath ) , "Joined path should exist" ) ;
63+ assert (
64+ fs . statSync ( joinedPath ) . isDirectory ( ) ,
65+ "Joined path should be a directory"
66+ ) ;
67+ } ) ;
68+
69+ it ( "should handle URL to path conversion correctly on all platforms" , ( ) => {
70+ // Create a test URL similar to how EXAMPLES_DIR is created
71+ const testUrl = new URL ( "../examples" , import . meta. url ) ;
72+ const convertedPath = testUrl . pathname ;
73+
74+ // The converted path should work with fs operations
75+ // We can't test the actual EXAMPLES_DIR since it might not exist,
76+ // but we can verify the conversion produces valid paths
77+ if ( process . platform === "win32" ) {
78+ // On Windows, URL.pathname returns /C:/... which is invalid
79+ // Our fix uses fileURLToPath which returns C:\...
80+ assert (
81+ ! path . isAbsolute ( convertedPath ) || convertedPath . startsWith ( "/" ) ,
82+ "Direct URL.pathname on Windows produces invalid absolute paths"
83+ ) ;
84+ }
85+ } ) ;
86+ } ) ;
87+
88+ describe ( "findCMakeProjects" , ( ) => {
89+ it ( "should find CMakeLists.txt files recursively" , ( context ) => {
90+ const tempDir = setupTempDirectory ( context , {
91+ "project1/CMakeLists.txt" : "# CMake file 1" ,
92+ "project2/subdir/CMakeLists.txt" : "# CMake file 2" ,
93+ "project3/CMakeLists.txt" : "# CMake file 3" ,
94+ "not-a-project/other.txt" : "not cmake" ,
95+ } ) ;
96+
97+ const projects = findCMakeProjects ( tempDir ) ;
98+
99+ assert . equal ( projects . length , 3 , "Should find 3 CMake projects" ) ;
100+
101+ // Sort for consistent comparison
102+ const sortedProjects = projects . sort ( ) ;
103+ const expectedProjects = [
104+ path . join ( tempDir , "project1" ) ,
105+ path . join ( tempDir , "project2" , "subdir" ) ,
106+ path . join ( tempDir , "project3" ) ,
107+ ] . sort ( ) ;
108+
109+ assert . deepEqual ( sortedProjects , expectedProjects ) ;
110+ } ) ;
111+
112+ it ( "should handle empty directories" , ( context ) => {
113+ const tempDir = setupTempDirectory ( context , { } ) ;
114+ const projects = findCMakeProjects ( tempDir ) ;
115+ assert . equal ( projects . length , 0 , "Should find no projects in empty dir" ) ;
116+ } ) ;
117+
118+ it ( "should handle nested CMake projects" , ( context ) => {
119+ const tempDir = setupTempDirectory ( context , {
120+ "parent/CMakeLists.txt" : "# Parent CMake" ,
121+ "parent/child/CMakeLists.txt" : "# Child CMake" ,
122+ "parent/child/grandchild/CMakeLists.txt" : "# Grandchild CMake" ,
123+ } ) ;
124+
125+ const projects = findCMakeProjects ( tempDir ) ;
126+
127+ assert . equal ( projects . length , 3 , "Should find all nested projects" ) ;
128+ assert (
129+ projects . includes ( path . join ( tempDir , "parent" ) ) ,
130+ "Should include parent project"
131+ ) ;
132+ assert (
133+ projects . includes ( path . join ( tempDir , "parent" , "child" ) ) ,
134+ "Should include child project"
135+ ) ;
136+ assert (
137+ projects . includes ( path . join ( tempDir , "parent" , "child" , "grandchild" ) ) ,
138+ "Should include grandchild project"
139+ ) ;
140+ } ) ;
141+
142+ it ( "should work with Windows-style paths" , { skip : process . platform !== "win32" } , ( context ) => {
143+ const tempDir = setupTempDirectory ( context , {
144+ "windows\\style\\path\\CMakeLists.txt" : "# CMake file" ,
145+ } ) ;
146+
147+ const projects = findCMakeProjects ( tempDir ) ;
148+ assert . equal ( projects . length , 1 , "Should find project with Windows path" ) ;
149+ } ) ;
150+ } ) ;
0 commit comments