import { readDetoxConfig, listConfigurations } from "../utils/config-parser.js";
import { listIOSSimulators, listAndroidEmulators } from "../utils/cli-executor.js";
import { API_REFERENCE } from "../templates/test-templates.js";
import { readdir, readFile, stat } from "fs/promises";
import { join } from "path";
export interface Resource {
uri: string;
name: string;
description: string;
mimeType: string;
}
export interface ResourceContent {
uri: string;
mimeType: string;
text?: string;
blob?: string;
}
/**
* List all available resources
*/
export async function listResources(projectPath: string): Promise<Resource[]> {
const resources: Resource[] = [
{
uri: "detox://config",
name: "Detox Configuration",
description: "Current Detox configuration from .detoxrc.js or similar",
mimeType: "application/json",
},
{
uri: "detox://devices",
name: "Available Devices",
description: "List of iOS simulators and Android emulators",
mimeType: "application/json",
},
{
uri: "detox://tests",
name: "Test Files",
description: "List of Detox test files in the e2e directory",
mimeType: "application/json",
},
{
uri: "detox://api/matchers",
name: "Matchers API",
description: "Detox element matchers reference (by.id, by.text, etc.)",
mimeType: "application/json",
},
{
uri: "detox://api/actions",
name: "Actions API",
description: "Detox element actions reference (tap, typeText, etc.)",
mimeType: "application/json",
},
{
uri: "detox://api/expectations",
name: "Expectations API",
description: "Detox assertions reference (toBeVisible, toExist, etc.)",
mimeType: "application/json",
},
{
uri: "detox://api/device",
name: "Device API",
description: "Detox device control reference (launchApp, setLocation, etc.)",
mimeType: "application/json",
},
];
return resources;
}
/**
* Read resource content
*/
export async function readResource(
uri: string,
projectPath: string
): Promise<ResourceContent> {
const parsedUri = new URL(uri);
const path = parsedUri.pathname.replace(/^\//, "");
switch (parsedUri.host) {
case "config":
return await getConfigResource(projectPath);
case "devices":
return await getDevicesResource();
case "tests":
return await getTestsResource(projectPath);
case "api":
return getApiResource(path);
default:
throw new Error(`Unknown resource: ${uri}`);
}
}
async function getConfigResource(projectPath: string): Promise<ResourceContent> {
const result = await readDetoxConfig(projectPath);
if (!result) {
return {
uri: "detox://config",
mimeType: "application/json",
text: JSON.stringify(
{ error: "No Detox configuration found" },
null,
2
),
};
}
const configurations = listConfigurations(result.config);
return {
uri: "detox://config",
mimeType: "application/json",
text: JSON.stringify(
{
configPath: result.configPath,
configurations,
devices: result.config.devices,
apps: result.config.apps,
testRunner: result.config.testRunner,
artifacts: result.config.artifacts,
},
null,
2
),
};
}
async function getDevicesResource(): Promise<ResourceContent> {
const iosDevices = await listIOSSimulators();
const androidDevices = await listAndroidEmulators();
return {
uri: "detox://devices",
mimeType: "application/json",
text: JSON.stringify(
{
ios: iosDevices,
android: androidDevices,
total: iosDevices.length + androidDevices.length,
},
null,
2
),
};
}
async function getTestsResource(projectPath: string): Promise<ResourceContent> {
const e2ePath = join(projectPath, "e2e");
const tests: Array<{ name: string; path: string; size: number }> = [];
try {
const files = await readdir(e2ePath);
for (const file of files) {
if (file.endsWith(".test.ts") || file.endsWith(".test.js") ||
file.endsWith(".e2e.ts") || file.endsWith(".e2e.js")) {
const filePath = join(e2ePath, file);
const stats = await stat(filePath);
tests.push({
name: file,
path: filePath,
size: stats.size,
});
}
}
} catch {
// e2e folder doesn't exist
}
return {
uri: "detox://tests",
mimeType: "application/json",
text: JSON.stringify(
{
e2ePath,
tests,
total: tests.length,
},
null,
2
),
};
}
function getApiResource(path: string): ResourceContent {
let content: any;
switch (path) {
case "matchers":
content = {
title: "Detox Matchers API",
description: "Methods for finding elements in your app",
matchers: API_REFERENCE.matchers,
};
break;
case "actions":
content = {
title: "Detox Actions API",
description: "Methods for interacting with elements",
actions: API_REFERENCE.actions,
};
break;
case "expectations":
content = {
title: "Detox Expectations API",
description: "Methods for asserting element state",
expectations: API_REFERENCE.expectations,
};
break;
case "device":
content = {
title: "Detox Device API",
description: "Methods for controlling the device/simulator",
device: API_REFERENCE.device,
};
break;
default:
content = {
title: "Detox API Reference",
matchers: API_REFERENCE.matchers,
actions: API_REFERENCE.actions,
expectations: API_REFERENCE.expectations,
device: API_REFERENCE.device,
};
}
return {
uri: `detox://api/${path}`,
mimeType: "application/json",
text: JSON.stringify(content, null, 2),
};
}