"""The example flow from docs/concept.md, steps 1–7, driven through the views like a browser.""" from django.urls import reverse from intent.comments.models import CommentThread from intent.features.models import Feature from intent.issues.models import Issue, IssueStatus P = {"org_slug": "catbriar", "project_slug": "intent"} def issue_url(name, number, **kwargs): return reverse(f"issues:{name}", kwargs={**P, "number": number, **kwargs}) def feature_url(name, key, **kwargs): return reverse(f"features:{name}", kwargs={**P, "feature_key": key, **kwargs}) def reply_url(thread): return reverse("comments:reply", kwargs={**P, "thread_id": thread.pk}) def test_example_flow(client, open_project, outsider, editor): # 1. A general user creates a new issue in a project. client.force_login(outsider) client.post( reverse("issues:create", kwargs=P), {"title": "Remember me on shared computers", "body": "Sessions last too long."}, ) issue = Issue.objects.get() # 2. Users, including team members, discuss in comments on the issue. issue_thread = issue.threads.get() client.post(reply_url(issue_thread), {"body": "Public library computers especially."}) client.force_login(editor) client.post(reply_url(issue_thread), {"body": "Agreed, let's define this."}) # 3. A team member accepts the issue and creates a feature from it, linked to the issue. create = reverse("features:create", kwargs=P) + f"?issue={issue.number}" client.post( create, { "title": "Session length", "description": "Sessions end after 12 hours.\n\nShared computers end at close.\n", "note": "", "accept_issue": "on", }, ) issue.refresh_from_db() feature = Feature.objects.get() assert issue.status == IssueStatus.ACCEPTED assert list(issue.features.all()) == [feature] assert feature.revisions.get().issues.get() == issue # 4. The team member updates the feature's status manually. client.post( feature_url("edit", feature.key), { "title": feature.title, "description": feature.description, "status": "withdrawn", "note": "Paused until the login rework.", }, ) feature.refresh_from_db() assert (feature.status, feature.current_revision) == ("withdrawn", 2) client.post( feature_url("edit", feature.key), { "title": feature.title, "description": feature.description, "status": "active", "note": "Back on.", }, ) # 5. Later, a user starts a comment thread on lines of the feature. client.force_login(outsider) client.post( feature_url("thread_create", feature.key), {"body": "What counts as close?", "revision": 3, "line_start": 3, "line_end": 3}, ) thread = CommentThread.objects.get(feature=feature) # 6. After some discussion, the user promotes the thread to an issue. client.force_login(editor) client.post(reply_url(thread), {"body": "Browser close, I think."}) client.force_login(outsider) promote = reverse("comments:promote", kwargs={**P, "thread_id": thread.pk}) client.post(promote, {"title": "Define 'close'", "body": "From the thread."}) follow_up = Issue.objects.get(number=2) thread.refresh_from_db() assert thread.promoted_to == follow_up and thread.is_resolved assert list(follow_up.features.all()) == [feature] # 7. More discussion, then a team member accepts the issue and updates the feature. client.post(reply_url(follow_up.threads.get()), {"body": "Tab close too?"}) client.force_login(editor) client.post( feature_url("edit", feature.key) + f"?issue={follow_up.number}", { "title": feature.title, "description": ( "Sessions end after 12 hours.\n\nShared computers end when the browser closes.\n" ), "status": "active", "note": "Define close as browser close.", "accept_issue": "on", }, ) follow_up.refresh_from_db() feature.refresh_from_db() assert follow_up.status == IssueStatus.ACCEPTED assert feature.current_revision == 4 assert feature.revisions.get(revision=4).issues.get() == follow_up # The line thread is now outdated, and the feature page shows both issues. page = client.get(feature_url("detail", feature.key)).content.decode() assert "Comments on earlier revisions" in page assert "#1 Remember me on shared computers" in page and "#2 Define" in page # 7, alternative: a third issue is rejected but stays linked, so the proposal is visible. client.force_login(outsider) client.post( reverse("issues:create", kwargs=P), {"title": "Never expire sessions", "features": [feature.pk]}, ) client.force_login(editor) client.post(issue_url("status", 3), {"status": "rejected"}) rejected = Issue.objects.get(number=3) assert rejected.status == IssueStatus.REJECTED assert ( "#3 Never expire sessions" in client.get(feature_url("detail", feature.key)).content.decode() )