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
14 changes: 13 additions & 1 deletion crates/lineark/src/commands/issues.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ pub enum IssuesAction {
/// Priority: 0=none, 1=urgent, 2=high, 3=medium, 4=low.
#[arg(short = 'p', long, value_parser = clap::value_parser!(i64).range(0..=4))]
priority: Option<i64>,
/// Estimate points (valid values depend on the team's estimation scale).
#[arg(short = 'e', long)]
estimate: Option<i64>,
/// Issue description (markdown).
#[arg(short = 'd', long)]
description: Option<String>,
Expand Down Expand Up @@ -149,6 +152,9 @@ pub enum IssuesAction {
/// Priority: 0=none, 1=urgent, 2=high, 3=medium, 4=low.
#[arg(short = 'p', long, value_parser = clap::value_parser!(i64).range(0..=4))]
priority: Option<i64>,
/// Estimate points (valid values depend on the team's estimation scale).
#[arg(short = 'e', long)]
estimate: Option<i64>,
/// Comma-separated label names or UUIDs. Behavior depends on --label-by.
#[arg(long, value_delimiter = ',')]
labels: Option<Vec<String>>,
Expand Down Expand Up @@ -316,6 +322,7 @@ pub struct IssueDetail {
pub description: Option<String>,
pub priority: Option<f64>,
pub priority_label: Option<String>,
pub estimate: Option<f64>,
pub url: Option<String>,
pub created_at: Option<String>,
pub updated_at: Option<String>,
Expand Down Expand Up @@ -551,6 +558,7 @@ pub async fn run(cmd: IssuesCmd, client: &Client, format: Format) -> anyhow::Res
assignee,
labels,
priority,
estimate,
description,
parent,
status,
Expand Down Expand Up @@ -595,6 +603,7 @@ pub async fn run(cmd: IssuesCmd, client: &Client, format: Format) -> anyhow::Res
assignee_id,
label_ids,
priority,
estimate,
description,
parent_id,
state_id,
Expand Down Expand Up @@ -648,6 +657,7 @@ pub async fn run(cmd: IssuesCmd, client: &Client, format: Format) -> anyhow::Res
identifier,
status,
priority,
estimate,
labels,
label_by,
clear_labels,
Expand All @@ -661,6 +671,7 @@ pub async fn run(cmd: IssuesCmd, client: &Client, format: Format) -> anyhow::Res
} => {
if status.is_none()
&& priority.is_none()
&& estimate.is_none()
&& labels.is_none()
&& !clear_labels
&& assignee.is_none()
Expand All @@ -672,7 +683,7 @@ pub async fn run(cmd: IssuesCmd, client: &Client, format: Format) -> anyhow::Res
&& cycle.is_none()
{
return Err(anyhow::anyhow!(
"No update fields provided. Use --status, --priority, --assignee, --labels, --title, --description, --parent, --project, or --cycle to specify changes."
"No update fields provided. Use --status, --priority, --estimate, --assignee, --labels, --title, --description, --parent, --project, or --cycle to specify changes."
));
}

Expand Down Expand Up @@ -744,6 +755,7 @@ pub async fn run(cmd: IssuesCmd, client: &Client, format: Format) -> anyhow::Res
description,
assignee_id,
priority,
estimate,
state_id,
parent_id,
label_ids,
Expand Down
5 changes: 3 additions & 2 deletions crates/lineark/src/commands/usage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,13 @@ COMMANDS:
[--team KEY] [--assignee NAME-OR-ID|me] Filter by team, assignee, status
[--status NAME,...] [--show-done] Comma-separated status names
lineark issues create <TITLE> --team KEY Create an issue
[-p 0-4] [--assignee NAME-OR-ID|me] 0=none 1=urgent 2=high 3=medium 4=low
[-p 0-4] [-e N] [--assignee NAME-OR-ID|me] 0=none 1=urgent 2=high 3=medium 4=low
[--labels NAME,...] [-d TEXT] [-s NAME] Label names (team-scoped), status name
[--parent ID] [--project NAME-OR-ID] Parent issue, project, cycle
[--cycle NAME-OR-ID]
lineark issues update <IDENTIFIER> Update an issue
[-s NAME] [-p 0-4] [--assignee NAME-OR-ID|me] Status, priority, assignee
[-s NAME] [-p 0-4] [-e N] Status, priority, estimate
[--assignee NAME-OR-ID|me] Assignee
[--labels NAME,...] [--label-by adding|replacing|removing]
[--clear-labels] [-t TEXT] [-d TEXT] Title, description
[--parent ID] [--clear-parent] Set or remove parent
Expand Down
31 changes: 31 additions & 0 deletions crates/lineark/tests/offline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -907,3 +907,34 @@ fn usage_includes_issues_list_project_filter() {
.success()
.stdout(predicate::str::contains("--project"));
}

// ── Estimate flag ───────────────────────────────────────────────────────────

#[test]
fn issues_create_help_shows_estimate() {
lineark()
.args(["issues", "create", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("--estimate"))
.stdout(predicate::str::contains("-e"));
}

#[test]
fn issues_update_help_shows_estimate() {
lineark()
.args(["issues", "update", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("--estimate"))
.stdout(predicate::str::contains("-e"));
}

#[test]
fn issues_update_no_flags_error_mentions_estimate() {
lineark()
.args(["--api-token", "fake-token", "issues", "update", "ENG-123"])
.assert()
.failure()
.stderr(predicate::str::contains("--estimate"));
}
82 changes: 82 additions & 0 deletions crates/lineark/tests/online.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3592,4 +3592,86 @@ mod online {
})
.expect("issues list --project should return at least one issue");
}

// ── Issues create with --estimate ───────────────────────────────────────

#[test_with::runtime_ignore_if(no_online_test_token)]
fn issues_create_with_estimate() {
let token = api_token();
let unique_name = format!(
"[test] CLI estimate flag {}",
&uuid::Uuid::new_v4().to_string()[..8]
);

// Get a team key.
let output = lineark()
.args(["--api-token", &token, "--format", "json", "teams", "list"])
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
output.status.success(),
"teams list should succeed.\nstdout: {stdout}\nstderr: {stderr}"
);
let teams: serde_json::Value = serde_json::from_str(&stdout).unwrap();
let team_key = teams[0]["key"].as_str().unwrap().to_string();

// Create an issue with --estimate.
let output = lineark()
.args([
"--api-token",
&token,
"--format",
"json",
"issues",
"create",
&unique_name,
"--team",
&team_key,
"--priority",
"4",
"--estimate",
"3",
])
.output()
.expect("failed to execute lineark");
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
output.status.success(),
"issues create --estimate should succeed.\nstdout: {stdout}\nstderr: {stderr}"
);
let created: serde_json::Value = serde_json::from_str(&stdout).unwrap();
let issue_id = created["id"]
.as_str()
.expect("created issue should have id")
.to_string();
let _issue_guard = IssueGuard {
token: token.clone(),
id: issue_id.clone(),
};

// Update the issue with a different estimate.
let output = lineark()
.args([
"--api-token",
&token,
"--format",
"json",
"issues",
"update",
&issue_id,
"--estimate",
"5",
])
.output()
.expect("failed to execute lineark");
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
output.status.success(),
"issues update --estimate should succeed.\nstdout: {stdout}\nstderr: {stderr}"
);
}
}