Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bin/commands/issue.js
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ async function getIssue(client, io, issueKey, options = {}) {
const markdown = formatIssueAsMarkdown(issue);
io.out('\n' + markdown);
} else {
displayIssueDetails(issue);
displayIssueDetails(issue, io);
}

} catch (err) {
Expand Down
70 changes: 14 additions & 56 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -233,31 +233,31 @@ function formatIssueAsMarkdown(issue) {
}

// Display issue details
function displayIssueDetails(issue) {
function displayIssueDetails(issue, io) {
if (!issue || !issue.fields) {
throw new Error('Unexpected API response: issue fields missing. Try setting API version with: jira config --api-version 2');
}
console.log(chalk.bold(`\n${issue.key}: ${issue.fields.summary}`));
console.log(chalk.gray('─'.repeat(60)));
io.println(chalk.bold(`\n${issue.key}: ${issue.fields.summary}`));
io.println(chalk.gray('─'.repeat(60)));

console.log(`${chalk.bold('Status:')} ${chalk.yellow(issue.fields.status.name)}`);
console.log(`${chalk.bold('Type:')} ${issue.fields.issuetype.name}`);
console.log(`${chalk.bold('Priority:')} ${issue.fields.priority ? issue.fields.priority.name : 'N/A'}`);
console.log(`${chalk.bold('Assignee:')} ${issue.fields.assignee ? issue.fields.assignee.displayName : 'Unassigned'}`);
console.log(`${chalk.bold('Reporter:')} ${issue.fields.reporter ? issue.fields.reporter.displayName : 'N/A'}`);
console.log(`${chalk.bold('Created:')} ${formatDate(issue.fields.created)}`);
console.log(`${chalk.bold('Updated:')} ${formatDate(issue.fields.updated)}`);
io.println(`${chalk.bold('Status:')} ${chalk.yellow(issue.fields.status.name)}`);
io.println(`${chalk.bold('Type:')} ${issue.fields.issuetype.name}`);
io.println(`${chalk.bold('Priority:')} ${issue.fields.priority ? issue.fields.priority.name : 'N/A'}`);
io.println(`${chalk.bold('Assignee:')} ${issue.fields.assignee ? issue.fields.assignee.displayName : 'Unassigned'}`);
io.println(`${chalk.bold('Reporter:')} ${issue.fields.reporter ? issue.fields.reporter.displayName : 'N/A'}`);
io.println(`${chalk.bold('Created:')} ${formatDate(issue.fields.created)}`);
io.println(`${chalk.bold('Updated:')} ${formatDate(issue.fields.updated)}`);

if (issue.fields.description) {
console.log(`\n${chalk.bold('Description:')}`);
console.log(resolveDescription(issue.fields.description));
io.println(`\n${chalk.bold('Description:')}`);
io.println(resolveDescription(issue.fields.description));
}

if (issue.fields.labels && issue.fields.labels.length > 0) {
console.log(`\n${chalk.bold('Labels:')} ${issue.fields.labels.join(', ')}`);
io.println(`\n${chalk.bold('Labels:')} ${issue.fields.labels.join(', ')}`);
}

console.log(`\n${chalk.bold('URL:')} ${issue.self.replace(new RegExp(`/rest/api/(2|3)/issue/${issue.id}`), '/browse/' + issue.key)}`);
io.println(`\n${chalk.bold('URL:')} ${issue.self.replace(new RegExp(`/rest/api/(2|3)/issue/${issue.id}`), '/browse/' + issue.key)}`);
}

// Escape a value for safe use inside a JQL double-quoted string literal.
Expand Down Expand Up @@ -396,26 +396,6 @@ function buildJQL(options) {
return base || 'ORDER BY updated DESC';
}

// Success message
function success(message) {
console.log(chalk.green('✓'), message);
}

// Error message
function error(message) {
console.error(chalk.red('✗'), message);
}

// Warning message
function warning(message) {
console.log(chalk.yellow('⚠'), message);
}

// Info message
function info(message) {
console.log(chalk.blue('ℹ'), message);
}

// Create table for comments
function createCommentsTable(comments) {
const table = new Table({
Expand Down Expand Up @@ -474,23 +454,6 @@ function createRemoteLinksTable(remoteLinks) {
return table;
}

// Display single comment details
function displayCommentDetails(comment) {
console.log(chalk.bold(`\nComment ID: ${comment.id}`));
console.log(chalk.gray('─'.repeat(60)));
console.log(`${chalk.bold('Author:')} ${comment.author ? comment.author.displayName : 'Unknown'}`);
console.log(`${chalk.bold('Created:')} ${formatDate(comment.created)}`);
console.log(`${chalk.bold('Updated:')} ${formatDate(comment.updated)}`);

if (comment.visibility) {
console.log(`${chalk.bold('Visibility:')} ${comment.visibility.type} - ${comment.visibility.value}`);
}

console.log(`\n${chalk.bold('Body:')}`);
console.log(comment.body || 'No content');
console.log('');
}

module.exports = {
formatDate,
formatIssueForTable,
Expand All @@ -501,13 +464,8 @@ module.exports = {
formatIssueAsMarkdown,
buildJQL,
escapeJqlString,
success,
error,
warning,
info,
createCommentsTable,
createRemoteLinksTable,
displayCommentDetails,
convertAdfToText,
resolveDescription,
expandHomePath
Expand Down
41 changes: 41 additions & 0 deletions tests/utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,47 @@ describe('Utils', () => {
});
});

describe('displayIssueDetails', () => {
function createIo() {
const lines = [];
return {
lines,
println: (msg = '') => lines.push(msg)
};
}

const baseIssue = {
key: 'TEST-1',
id: '10001',
self: 'https://jira.example.com/rest/api/3/issue/10001',
fields: {
summary: 'Sample',
status: { name: 'Open' },
issuetype: { name: 'Bug' },
priority: { name: 'High' },
assignee: { displayName: 'John' },
reporter: { displayName: 'Jane' },
created: '2024-01-01T00:00:00.000Z',
updated: '2024-01-02T00:00:00.000Z'
}
};

it('should write to the provided IOStreams instead of console', () => {
const io = createIo();
Utils.displayIssueDetails(baseIssue, io);
const output = io.lines.join('\n');
expect(output).toContain('TEST-1');
expect(output).toContain('Sample');
expect(output).toContain('Open');
expect(output).toContain('/browse/TEST-1');
});

it('should throw if issue fields are missing', () => {
const io = createIo();
expect(() => Utils.displayIssueDetails({}, io)).toThrow(/issue fields missing/);
});
});

describe('formatIssueAsMarkdown with ADF description', () => {
it('should render ADF description as text instead of [object Object]', () => {
const issue = {
Expand Down
Loading