diff --git a/tests/integration/test_read.py b/tests/integration/test_read.py index a9bb914..b612170 100644 --- a/tests/integration/test_read.py +++ b/tests/integration/test_read.py @@ -204,3 +204,23 @@ def test_thread(app, user, run): assert uuid1 in bits[0] assert uuid2 in bits[1] assert uuid3 in bits[2] + + +def test_thread_json(app, user, run): + uuid1 = str(uuid4()) + uuid2 = str(uuid4()) + uuid3 = str(uuid4()) + + s1 = api.post_status(app, user, uuid1).json() + s2 = api.post_status(app, user, uuid2, in_reply_to_id=s1["id"]).json() + s3 = api.post_status(app, user, uuid3, in_reply_to_id=s2["id"]).json() + + result = run(cli.thread, s2["id"], "--json") + assert result.exit_code == 0 + + result = json.loads(result.stdout) + [ancestor] = [from_dict(Status, s) for s in result["ancestors"]] + [descendent] = [from_dict(Status, s) for s in result["descendants"]] + + assert ancestor.id == s1["id"] + assert descendent.id == s3["id"] diff --git a/tests/integration/test_status.py b/tests/integration/test_status.py index f4b7f07..1e88ab0 100644 --- a/tests/integration/test_status.py +++ b/tests/integration/test_status.py @@ -2,18 +2,16 @@ import json import time import pytest -from toot import api +from toot import api, cli from toot.exceptions import NotFoundError -pytest.skip("TODO", allow_module_level=True) - - def test_delete(app, user, run): status = api.post_status(app, user, "foo").json() - out = run("delete", status["id"]) - assert out == "✓ Status deleted" + result = run(cli.delete, status["id"]) + assert result.exit_code == 0 + assert result.stdout.strip() == "✓ Status deleted" with pytest.raises(NotFoundError): api.fetch_status(app, user, status["id"]) @@ -22,7 +20,10 @@ def test_delete(app, user, run): def test_delete_json(app, user, run): status = api.post_status(app, user, "foo").json() - out = run("delete", status["id"], "--json") + result = run(cli.delete, status["id"], "--json") + assert result.exit_code == 0 + + out = result.stdout result = json.loads(out) assert result["id"] == status["id"] @@ -34,17 +35,19 @@ def test_favourite(app, user, run): status = api.post_status(app, user, "foo").json() assert not status["favourited"] - out = run("favourite", status["id"]) - assert out == "✓ Status favourited" + result = run(cli.favourite, status["id"]) + assert result.exit_code == 0 + assert result.stdout.strip() == "✓ Status favourited" status = api.fetch_status(app, user, status["id"]).json() assert status["favourited"] - out = run("unfavourite", status["id"]) - assert out == "✓ Status unfavourited" + result = run(cli.unfavourite, status["id"]) + assert result.exit_code == 0 + assert result.stdout.strip() == "✓ Status unfavourited" # A short delay is required before the server returns new data - time.sleep(0.1) + time.sleep(0.2) status = api.fetch_status(app, user, status["id"]).json() assert not status["favourited"] @@ -54,15 +57,17 @@ def test_favourite_json(app, user, run): status = api.post_status(app, user, "foo").json() assert not status["favourited"] - out = run("favourite", status["id"], "--json") - result = json.loads(out) + result = run(cli.favourite, status["id"], "--json") + assert result.exit_code == 0 + result = json.loads(result.stdout) assert result["id"] == status["id"] assert result["favourited"] is True - out = run("unfavourite", status["id"], "--json") - result = json.loads(out) + result = run(cli.unfavourite, status["id"], "--json") + assert result.exit_code == 0 + result = json.loads(result.stdout) assert result["id"] == status["id"] assert result["favourited"] is False @@ -71,17 +76,24 @@ def test_reblog(app, user, run): status = api.post_status(app, user, "foo").json() assert not status["reblogged"] - out = run("reblog", status["id"]) - assert out == "✓ Status reblogged" + result = run(cli.reblogged_by, status["id"]) + assert result.exit_code == 0 + assert result.stdout.strip() == "This status is not reblogged by anyone" + + result = run(cli.reblog, status["id"]) + assert result.exit_code == 0 + assert result.stdout.strip() == "✓ Status reblogged" status = api.fetch_status(app, user, status["id"]).json() assert status["reblogged"] - out = run("reblogged_by", status["id"]) - assert user.username in out + result = run(cli.reblogged_by, status["id"]) + assert result.exit_code == 0 + assert user.username in result.stdout - out = run("unreblog", status["id"]) - assert out == "✓ Status unreblogged" + result = run(cli.unreblog, status["id"]) + assert result.exit_code == 0 + assert result.stdout.strip() == "✓ Status unreblogged" status = api.fetch_status(app, user, status["id"]).json() assert not status["reblogged"] @@ -91,19 +103,23 @@ def test_reblog_json(app, user, run): status = api.post_status(app, user, "foo").json() assert not status["reblogged"] - out = run("reblog", status["id"], "--json") - result = json.loads(out) + result = run(cli.reblog, status["id"], "--json") + assert result.exit_code == 0 + result = json.loads(result.stdout) assert result["reblogged"] is True assert result["reblog"]["id"] == status["id"] - out = run("reblogged_by", status["id"], "--json") - [reblog] = json.loads(out) + result = run(cli.reblogged_by, status["id"], "--json") + assert result.exit_code == 0 + + [reblog] = json.loads(result.stdout) assert reblog["acct"] == user.username - out = run("unreblog", status["id"], "--json") - result = json.loads(out) + result = run(cli.unreblog, status["id"], "--json") + assert result.exit_code == 0 + result = json.loads(result.stdout) assert result["reblogged"] is False assert result["reblog"] is None @@ -112,14 +128,16 @@ def test_pin(app, user, run): status = api.post_status(app, user, "foo").json() assert not status["pinned"] - out = run("pin", status["id"]) - assert out == "✓ Status pinned" + result = run(cli.pin, status["id"]) + assert result.exit_code == 0 + assert result.stdout.strip() == "✓ Status pinned" status = api.fetch_status(app, user, status["id"]).json() assert status["pinned"] - out = run("unpin", status["id"]) - assert out == "✓ Status unpinned" + result = run(cli.unpin, status["id"]) + assert result.exit_code == 0 + assert result.stdout.strip() == "✓ Status unpinned" status = api.fetch_status(app, user, status["id"]).json() assert not status["pinned"] @@ -129,15 +147,17 @@ def test_pin_json(app, user, run): status = api.post_status(app, user, "foo").json() assert not status["pinned"] - out = run("pin", status["id"], "--json") - result = json.loads(out) + result = run(cli.pin, status["id"], "--json") + assert result.exit_code == 0 + result = json.loads(result.stdout) assert result["pinned"] is True assert result["id"] == status["id"] - out = run("unpin", status["id"], "--json") - result = json.loads(out) + result = run(cli.unpin, status["id"], "--json") + assert result.exit_code == 0 + result = json.loads(result.stdout) assert result["pinned"] is False assert result["id"] == status["id"] @@ -146,14 +166,16 @@ def test_bookmark(app, user, run): status = api.post_status(app, user, "foo").json() assert not status["bookmarked"] - out = run("bookmark", status["id"]) - assert out == "✓ Status bookmarked" + result = run(cli.bookmark, status["id"]) + assert result.exit_code == 0 + assert result.stdout.strip() == "✓ Status bookmarked" status = api.fetch_status(app, user, status["id"]).json() assert status["bookmarked"] - out = run("unbookmark", status["id"]) - assert out == "✓ Status unbookmarked" + result = run(cli.unbookmark, status["id"]) + assert result.exit_code == 0 + assert result.stdout.strip() == "✓ Status unbookmarked" status = api.fetch_status(app, user, status["id"]).json() assert not status["bookmarked"] @@ -163,14 +185,16 @@ def test_bookmark_json(app, user, run): status = api.post_status(app, user, "foo").json() assert not status["bookmarked"] - out = run("bookmark", status["id"], "--json") - result = json.loads(out) + result = run(cli.bookmark, status["id"], "--json") + assert result.exit_code == 0 + result = json.loads(result.stdout) assert result["id"] == status["id"] assert result["bookmarked"] is True - out = run("unbookmark", status["id"], "--json") - result = json.loads(out) + result = run(cli.unbookmark, status["id"], "--json") + assert result.exit_code == 0 + result = json.loads(result.stdout) assert result["id"] == status["id"] assert result["bookmarked"] is False diff --git a/toot/cli/__init__.py b/toot/cli/__init__.py index 87e6ace..d45892e 100644 --- a/toot/cli/__init__.py +++ b/toot/cli/__init__.py @@ -2,4 +2,5 @@ from toot.cli.base import cli, Context # noqa from toot.cli.post import * from toot.cli.read import * +from toot.cli.statuses import * from toot.cli.tags import * diff --git a/toot/cli/post.py b/toot/cli/post.py index fa93a19..92b839e 100644 --- a/toot/cli/post.py +++ b/toot/cli/post.py @@ -128,6 +128,7 @@ def post( poll_hide_totals: bool, json: bool ): + """Post a new status""" if editor and not sys.stdin.isatty(): raise click.ClickException("Cannot run editor if not in tty.") diff --git a/toot/cli/statuses.py b/toot/cli/statuses.py new file mode 100644 index 0000000..d675439 --- /dev/null +++ b/toot/cli/statuses.py @@ -0,0 +1,148 @@ +import click + +from toot import api +from toot.console import VISIBILITY_CHOICES, get_default_visibility +from toot.cli.base import cli, json_option, Context, pass_context +from toot.output import print_table + + +@cli.command() +@click.argument("status_id") +@json_option +@pass_context +def delete(ctx: Context, status_id: str, json: bool): + """Delete a status""" + response = api.delete_status(ctx.app, ctx.user, status_id) + if json: + click.echo(response.text) + else: + click.secho("✓ Status deleted", fg="green") + + +@cli.command() +@click.argument("status_id") +@json_option +@pass_context +def favourite(ctx: Context, status_id: str, json: bool): + """Favourite a status""" + response = api.favourite(ctx.app, ctx.user, status_id) + if json: + click.echo(response.text) + else: + click.secho("✓ Status favourited", fg="green") + + +@cli.command() +@click.argument("status_id") +@json_option +@pass_context +def unfavourite(ctx: Context, status_id: str, json: bool): + """Unfavourite a status""" + response = api.unfavourite(ctx.app, ctx.user, status_id) + if json: + click.echo(response.text) + else: + click.secho("✓ Status unfavourited", fg="green") + + +@cli.command() +@click.argument("status_id") +@click.option( + "--visibility", "-v", + help="Post visibility", + type=click.Choice(VISIBILITY_CHOICES), + default=get_default_visibility(), +) +@json_option +@pass_context +def reblog(ctx: Context, status_id: str, visibility: str, json: bool): + """Reblog (boost) a status""" + response = api.reblog(ctx.app, ctx.user, status_id, visibility=visibility) + if json: + click.echo(response.text) + else: + click.secho("✓ Status reblogged", fg="green") + + +@cli.command() +@click.argument("status_id") +@json_option +@pass_context +def unreblog(ctx: Context, status_id: str, json: bool): + """Unreblog (unboost) a status""" + response = api.unreblog(ctx.app, ctx.user, status_id) + if json: + click.echo(response.text) + else: + click.secho("✓ Status unreblogged", fg="green") + + +@cli.command() +@click.argument("status_id") +@json_option +@pass_context +def pin(ctx: Context, status_id: str, json: bool): + """Pin a status""" + response = api.pin(ctx.app, ctx.user, status_id) + if json: + click.echo(response.text) + else: + click.secho("✓ Status pinned", fg="green") + + +@cli.command() +@click.argument("status_id") +@json_option +@pass_context +def unpin(ctx: Context, status_id: str, json: bool): + """Unpin a status""" + response = api.unpin(ctx.app, ctx.user, status_id) + if json: + click.echo(response.text) + else: + click.secho("✓ Status unpinned", fg="green") + + +@cli.command() +@click.argument("status_id") +@json_option +@pass_context +def bookmark(ctx: Context, status_id: str, json: bool): + """Bookmark a status""" + response = api.bookmark(ctx.app, ctx.user, status_id) + if json: + click.echo(response.text) + else: + click.secho("✓ Status bookmarked", fg="green") + + +@cli.command() +@click.argument("status_id") +@json_option +@pass_context +def unbookmark(ctx: Context, status_id: str, json: bool): + """Unbookmark a status""" + response = api.unbookmark(ctx.app, ctx.user, status_id) + if json: + click.echo(response.text) + else: + click.secho("✓ Status unbookmarked", fg="green") + + +@cli.command(name="reblogged_by") +@click.argument("status_id") +@json_option +@pass_context +def reblogged_by(ctx: Context, status_id: str, json: bool): + """Show accounts that reblogged a status""" + response = api.reblogged_by(ctx.app, ctx.user, status_id) + + if json: + click.echo(response.text) + else: + rows = [[a["acct"], a["display_name"]] for a in response.json()] + if rows: + headers = ["Account", "Display name"] + print_table(headers, rows) + else: + click.echo("This status is not reblogged by anyone")