@nicodoggie/node-kiwi-tcms-api
Version:
Vibe-coded Node.js wrapper for Kiwi TCMS XML-RPC API. Use at your own risk.
224 lines • 7.22 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.UrlAPI = exports.KiwiUtilsAPI = exports.MarkdownAPI = exports.AttachmentAPI = void 0;
/**
* Attachment API module
*/
class AttachmentAPI {
client;
constructor(client) {
this.client = client;
}
/**
* Remove an attachment
*/
async remove(attachmentId) {
await this.client.authenticatedCall('Attachment.remove_attachment', [attachmentId]);
}
}
exports.AttachmentAPI = AttachmentAPI;
/**
* Markdown API module
*/
class MarkdownAPI {
client;
constructor(client) {
this.client = client;
}
/**
* Render markdown to HTML
*/
async render(markdownText) {
return await this.client.authenticatedCall('Markdown.render', [markdownText]);
}
}
exports.MarkdownAPI = MarkdownAPI;
/**
* Kiwi TCMS system utilities
*/
class KiwiUtilsAPI {
client;
constructor(client) {
this.client = client;
}
/**
* Get Kiwi TCMS version
*/
async version() {
return await this.client.call('Kiwi.version');
}
/**
* Extract tracker information from URL
*/
async trackerFromUrl(url) {
return await this.client.authenticatedCall('Utils.tracker_from_url', [url]);
}
}
exports.KiwiUtilsAPI = KiwiUtilsAPI;
/**
* URL generation utilities for creating permalinks and slugs
*/
class UrlAPI {
client;
constructor(client) {
this.client = client;
}
/**
* Generate a permalink URL for a test case
* @param testCaseId - The ID of the test case
* @returns The full URL to the test case
*/
generateTestCaseUrl(testCaseId) {
const baseUrl = this.getBaseWebUrl();
return `${baseUrl}/case/${testCaseId}/`;
}
/**
* Generate a permalink URL for a test plan
* @param testPlanId - The ID of the test plan
* @returns The full URL to the test plan
*/
generateTestPlanUrl(testPlanId) {
const baseUrl = this.getBaseWebUrl();
return `${baseUrl}/plan/${testPlanId}/`;
}
/**
* Generate a permalink URL for a test run
* @param testRunId - The ID of the test run
* @returns The full URL to the test run
*/
generateTestRunUrl(testRunId) {
const baseUrl = this.getBaseWebUrl();
return `${baseUrl}/run/${testRunId}/`;
}
/**
* Generate a permalink URL for a test execution
* @param testRunId - The ID of the test run
* @param testCaseId - The ID of the test case
* @returns The full URL to the test execution
*/
generateTestExecutionUrl(testRunId, testCaseId) {
const baseUrl = this.getBaseWebUrl();
return `${baseUrl}/run/${testRunId}/#testcase-${testCaseId}`;
}
/**
* Generate a permalink URL for a bug report
* @param bugId - The ID of the bug
* @returns The full URL to the bug report
*/
generateBugUrl(bugId) {
const baseUrl = this.getBaseWebUrl();
return `${baseUrl}/bugs/${bugId}/`;
}
/**
* Create a URL-friendly slug from a string
* @param text - The text to convert to a slug
* @param maxLength - Maximum length of the slug (default: 50)
* @returns A URL-friendly slug
*/
createSlug(text, maxLength = 50) {
return text
.toLowerCase()
.trim()
// Replace spaces and special characters with hyphens
.replace(/[^\w\s-]/g, '')
.replace(/[\s_-]+/g, '-')
// Remove leading/trailing hyphens
.replace(/^-+|-+$/g, '')
// Limit length
// .substring(0, maxLength)
// Remove trailing hyphen if truncated
.replace(/-+$/, '');
}
/**
* Generate a short link for sharing
* @param entityType - Type of entity ('case', 'plan', 'run', 'bug')
* @param entityId - ID of the entity
* @param slug - Optional slug for readability
* @returns A shareable short URL format
*/
generateShortLink(entityType, entityId, slug) {
const baseUrl = this.getBaseWebUrl();
return `${baseUrl}/${entityType}/${entityId}/${slug}`;
}
/**
* Parse a Kiwi TCMS URL to extract entity information
* @param url - The Kiwi TCMS URL to parse
* @returns Object with entity type and ID, or null if not a valid URL
*/
parseKiwiUrl(url) {
try {
const urlObj = new URL(url);
const pathMatch = urlObj.pathname.match(/\/(case|plan|run|bugs?)\/(\d+)(?:\/([^\/]+))?/);
if (pathMatch) {
const [, type, idStr, slug] = pathMatch;
const normalizedType = type === 'bugs' ? 'bug' : type;
return {
type: normalizedType,
id: parseInt(idStr, 10),
...(slug && { slug })
};
}
return null;
}
catch {
return null;
}
}
/**
* Get the base web URL from the XML-RPC URL
* @private
*/
getBaseWebUrl() {
// Extract base URL from client config
const xmlRpcUrl = this.client.getConfig().baseUrl;
// Remove '/xml-rpc/' suffix to get base web URL
return xmlRpcUrl.replace(/\/xml-rpc\/?$/, '');
}
/**
* Inject permalink properties into an entity object
* @param entityType - Type of entity ('case', 'plan', 'run', 'bug')
* @param entity - The entity object to enhance
* @param nameField - Field name to use for slug generation (e.g., 'summary', 'name')
* @returns The entity with permalink properties added
*/
injectPermalinks(entityType, entity, nameField) {
const name = entity[nameField];
const slug = this.createSlug(name || `${entityType}/${entity.id}`);
let permalink;
switch (entityType) {
case 'case':
permalink = this.generateTestCaseUrl(entity.id);
break;
case 'plan':
permalink = this.generateTestPlanUrl(entity.id);
break;
case 'run':
permalink = this.generateTestRunUrl(entity.id);
break;
case 'bug':
permalink = this.generateBugUrl(entity.id);
break;
default:
permalink = `${this.getBaseWebUrl()}/${entityType}/${entity.id}/`;
}
const shortLink = this.generateShortLink(entityType, entity.id, slug);
return {
...entity,
permalink,
shortLink,
slug
};
}
/**
* Inject permalinks into an array of entities
* @param entityType - Type of entity ('case', 'plan', 'run', 'bug')
* @param entities - Array of entities to enhance
* @param nameField - Field name to use for slug generation
* @returns Array of entities with permalink properties added
*/
injectPermalinksArray(entityType, entities, nameField) {
return entities.map(entity => this.injectPermalinks(entityType, entity, nameField));
}
}
exports.UrlAPI = UrlAPI;
//# sourceMappingURL=utilities.js.map