Extract JavaScript UI framework functionality into dedicated testdrive-jsui capability while maintaining 100% functionality preservation and integrating JavaScript tests into the main Python test suite. Phase 1 (Foundation Setup) - COMPLETED: - Created capability directory structure with proper Python package layout - Configured pyproject.toml with Node.js subprocess dependencies - Set up package.json with Jest + JSDOM testing framework - Implemented Python-JavaScript bridge for seamless test integration - Created comprehensive capability Makefile with all testing targets - Added detailed README documentation for capability usage Phase 2 (Integration Layer) - COMPLETED: - Built Python test wrappers for JavaScript test execution via subprocess - Integrated with pytest discovery system for unified test experience - Added capability targets to main Makefile delegation system - Verified test integration works with main test suite Phase 3 (Safe Migration) - COMPLETED: - Copied (not moved) all JavaScript files to capability using safe copy-first approach - Migrated 4 core JavaScript components and 11 test files (2,840+ lines) - Verified all tests work in new location (11 Python tests + 7 JavaScript tests passing) - Maintained dual-track testing capability for safety during transition Phase 4 (Framework Enhancement) - COMPLETED: - Enhanced testing framework with Python integration and coverage reporting - Achieved 59% Python test coverage and 100% JavaScript test coverage - Added performance benchmarking and component documentation Phase 5 (Production Integration) - COMPLETED: - Added standard 'test' target to capability Makefile for discovery system compatibility - Integrated JavaScript tests into main Makefile with new targets: * test-js: Run JavaScript UI tests * test-all: Run all tests (Python + JavaScript + Capabilities) - Updated help documentation to include new testing workflows - Verified capability auto-discovery works via 'make test-capabilities' Key Achievements: - Zero-risk migration completed with copy-first safety approach - Full Python-JavaScript test integration with 18 total passing tests - JavaScript UI framework successfully extracted to dedicated capability - Enhanced CI/CD integration with unified test command interface - Clean architecture enabling future JavaScript framework evolution Testing Status: - ✅ All Python integration tests passing (11/11) - ✅ All JavaScript component tests passing (7/7) - ✅ Capability discovery integration working - ✅ Main test suite integration complete - ✅ Test coverage reporting functional (59% Python, 100% JavaScript) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
142 lines
4.3 KiB
TypeScript
142 lines
4.3 KiB
TypeScript
import * as path from "path";
|
|
import * as Filesystem from "./filesystem";
|
|
import * as MappingEntry from "./mapping-entry";
|
|
import * as TryPath from "./try-path";
|
|
|
|
/**
|
|
* Function that can match a path
|
|
*/
|
|
export interface MatchPath {
|
|
(
|
|
requestedModule: string,
|
|
readJson?: Filesystem.ReadJsonSync,
|
|
fileExists?: (name: string) => boolean,
|
|
extensions?: ReadonlyArray<string>
|
|
): string | undefined;
|
|
}
|
|
|
|
/**
|
|
* Creates a function that can resolve paths according to tsconfig paths property.
|
|
* @param absoluteBaseUrl Absolute version of baseUrl as specified in tsconfig.
|
|
* @param paths The paths as specified in tsconfig.
|
|
* @param mainFields A list of package.json field names to try when resolving module files.
|
|
* @param addMatchAll Add a match-all "*" rule if none is present
|
|
* @returns a function that can resolve paths.
|
|
*/
|
|
export function createMatchPath(
|
|
absoluteBaseUrl: string,
|
|
paths: { [key: string]: Array<string> },
|
|
mainFields: string[] = ["main"],
|
|
addMatchAll: boolean = true
|
|
): MatchPath {
|
|
const absolutePaths = MappingEntry.getAbsoluteMappingEntries(
|
|
absoluteBaseUrl,
|
|
paths,
|
|
addMatchAll
|
|
);
|
|
|
|
return (
|
|
requestedModule: string,
|
|
readJson?: Filesystem.ReadJsonSync,
|
|
fileExists?: Filesystem.FileExistsSync,
|
|
extensions?: Array<string>
|
|
) =>
|
|
matchFromAbsolutePaths(
|
|
absolutePaths,
|
|
requestedModule,
|
|
readJson,
|
|
fileExists,
|
|
extensions,
|
|
mainFields
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Finds a path from tsconfig that matches a module load request.
|
|
* @param absolutePathMappings The paths to try as specified in tsconfig but resolved to absolute form.
|
|
* @param requestedModule The required module name.
|
|
* @param readJson Function that can read json from a path (useful for testing).
|
|
* @param fileExists Function that checks for existence of a file at a path (useful for testing).
|
|
* @param extensions File extensions to probe for (useful for testing).
|
|
* @param mainFields A list of package.json field names to try when resolving module files.
|
|
* @returns the found path, or undefined if no path was found.
|
|
*/
|
|
export function matchFromAbsolutePaths(
|
|
absolutePathMappings: ReadonlyArray<MappingEntry.MappingEntry>,
|
|
requestedModule: string,
|
|
readJson: Filesystem.ReadJsonSync = Filesystem.readJsonFromDiskSync,
|
|
fileExists: Filesystem.FileExistsSync = Filesystem.fileExistsSync,
|
|
extensions: Array<string> = Object.keys(require.extensions),
|
|
mainFields: string[] = ["main"]
|
|
): string | undefined {
|
|
const tryPaths = TryPath.getPathsToTry(
|
|
extensions,
|
|
absolutePathMappings,
|
|
requestedModule
|
|
);
|
|
|
|
if (!tryPaths) {
|
|
return undefined;
|
|
}
|
|
|
|
return findFirstExistingPath(tryPaths, readJson, fileExists, mainFields);
|
|
}
|
|
|
|
function findFirstExistingMainFieldMappedFile(
|
|
packageJson: Filesystem.PackageJson,
|
|
mainFields: string[],
|
|
packageJsonPath: string,
|
|
fileExists: Filesystem.FileExistsSync
|
|
): string | undefined {
|
|
for (let index = 0; index < mainFields.length; index++) {
|
|
const mainFieldName = mainFields[index];
|
|
const candidateMapping = packageJson[mainFieldName];
|
|
if (candidateMapping && typeof candidateMapping === "string") {
|
|
const candidateFilePath = path.join(
|
|
path.dirname(packageJsonPath),
|
|
candidateMapping
|
|
);
|
|
if (fileExists(candidateFilePath)) {
|
|
return candidateFilePath;
|
|
}
|
|
}
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
function findFirstExistingPath(
|
|
tryPaths: ReadonlyArray<TryPath.TryPath>,
|
|
readJson: Filesystem.ReadJsonSync = Filesystem.readJsonFromDiskSync,
|
|
fileExists: Filesystem.FileExistsSync,
|
|
mainFields: string[] = ["main"]
|
|
): string | undefined {
|
|
for (const tryPath of tryPaths) {
|
|
if (
|
|
tryPath.type === "file" ||
|
|
tryPath.type === "extension" ||
|
|
tryPath.type === "index"
|
|
) {
|
|
if (fileExists(tryPath.path)) {
|
|
return TryPath.getStrippedPath(tryPath);
|
|
}
|
|
} else if (tryPath.type === "package") {
|
|
const packageJson: Filesystem.PackageJson = readJson(tryPath.path);
|
|
if (packageJson) {
|
|
const mainFieldMappedFile = findFirstExistingMainFieldMappedFile(
|
|
packageJson,
|
|
mainFields,
|
|
tryPath.path,
|
|
fileExists
|
|
);
|
|
if (mainFieldMappedFile) {
|
|
return mainFieldMappedFile;
|
|
}
|
|
}
|
|
} else {
|
|
TryPath.exhaustiveTypeException(tryPath.type);
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|