Add markdown exports and tighten tag visibility
This commit is contained in:
+122
-1
@@ -241,13 +241,18 @@ class NavigationAndLayoutTests(TestCase):
|
||||
response = self.client.get(reverse('edit_item', args=[item.id]))
|
||||
self.assertContains(response, 'inline-edit-form')
|
||||
|
||||
def test_tag_filter_list_depends_on_current_filters_but_composer_gets_all_tags(self):
|
||||
def test_tag_filter_list_depends_on_current_filters_but_composer_gets_visible_tags(self):
|
||||
open_todo = Item.objects.create(owner=self.user, kind=Item.Kind.TODO, content='Open #openonly')
|
||||
open_todo.sync_metadata()
|
||||
done_todo = Item.objects.create(owner=self.user, kind=Item.Kind.TODO, content='Done #archiveonly', is_done=True)
|
||||
done_todo.sync_metadata()
|
||||
note = Item.objects.create(owner=self.user, kind=Item.Kind.NOTE, content='Note #noteonly')
|
||||
note.sync_metadata()
|
||||
other = get_user_model().objects.create_user(username='other-tags', password='secret')
|
||||
foreign = Item.objects.create(owner=other, kind=Item.Kind.NOTE, content='Foreign #foreignonly')
|
||||
foreign.sync_metadata()
|
||||
public = Item.objects.create(owner=other, kind=Item.Kind.NOTE, content='Public #publiconly', visibility=Item.Visibility.PUBLIC)
|
||||
public.sync_metadata()
|
||||
|
||||
response = self.client.get(f'{reverse("index")}?status=archive')
|
||||
html = response.content.decode()
|
||||
@@ -257,11 +262,30 @@ class NavigationAndLayoutTests(TestCase):
|
||||
self.assertNotIn('#noteonly', filter_tags)
|
||||
self.assertIn("'openonly'", html)
|
||||
self.assertIn("'noteonly'", html)
|
||||
self.assertIn("'publiconly'", html)
|
||||
self.assertNotIn('foreignonly', html)
|
||||
|
||||
response = self.client.get(f'{reverse("tag_list")}?status=archive')
|
||||
self.assertContains(response, '#archiveonly')
|
||||
self.assertNotContains(response, '#openonly')
|
||||
|
||||
def test_tag_api_returns_only_visible_tags(self):
|
||||
own = Item.objects.create(owner=self.user, content='Own #ownapi')
|
||||
own.sync_metadata()
|
||||
other = get_user_model().objects.create_user(username='other-api-tags', password='secret')
|
||||
foreign = Item.objects.create(owner=other, content='Foreign #foreignapi')
|
||||
foreign.sync_metadata()
|
||||
public = Item.objects.create(owner=other, content='Public #publicapi', visibility=Item.Visibility.PUBLIC)
|
||||
public.sync_metadata()
|
||||
|
||||
response = self.client.get(reverse('api_tags'))
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
tags = response.json()['tags']
|
||||
self.assertIn('ownapi', tags)
|
||||
self.assertIn('publicapi', tags)
|
||||
self.assertNotIn('foreignapi', tags)
|
||||
|
||||
def test_due_filter_also_shows_next_seven_days_separately(self):
|
||||
today = timezone.localdate()
|
||||
due = timezone.make_aware(datetime.combine(today, time(12, 0)))
|
||||
@@ -589,6 +613,36 @@ class CollectionTests(TestCase):
|
||||
manage_response = self.client.get(reverse('collection_manage', args=[collection.id]))
|
||||
self.assertRedirects(manage_response, reverse('collection_edit', args=[collection.id]))
|
||||
|
||||
def test_tag_collection_todos_are_rendered_with_checkbox(self):
|
||||
todo = Item.objects.create(owner=self.user, kind=Item.Kind.TODO, content='Check me #project')
|
||||
todo.sync_metadata()
|
||||
collection = Collection.objects.create(
|
||||
owner=self.user, title='Typed tags', mode=Collection.Mode.TAGS,
|
||||
include_notes=False, include_todos=True,
|
||||
)
|
||||
collection.filter_tags.add(Tag.objects.get(name='project'))
|
||||
|
||||
response = self.client.get(collection.get_absolute_url())
|
||||
|
||||
self.assertContains(response, 'type="checkbox"')
|
||||
self.assertContains(response, reverse('toggle_done', args=[todo.id]))
|
||||
|
||||
def test_tag_collection_todo_checkbox_toggles_and_returns_to_collection(self):
|
||||
todo = Item.objects.create(owner=self.user, kind=Item.Kind.TODO, content='Check me #project')
|
||||
todo.sync_metadata()
|
||||
collection = Collection.objects.create(
|
||||
owner=self.user, title='Typed tags', mode=Collection.Mode.TAGS,
|
||||
include_notes=False, include_todos=True,
|
||||
)
|
||||
collection.filter_tags.add(Tag.objects.get(name='project'))
|
||||
next_url = f'{collection.get_absolute_url()}#item-{todo.id}'
|
||||
|
||||
response = self.client.post(reverse('toggle_done', args=[todo.id]), {'next': next_url})
|
||||
|
||||
self.assertRedirects(response, next_url, fetch_redirect_response=False)
|
||||
todo.refresh_from_db()
|
||||
self.assertTrue(todo.is_done)
|
||||
|
||||
def test_tag_collection_can_filter_included_item_types(self):
|
||||
note = Item.objects.create(owner=self.user, kind=Item.Kind.NOTE, content='Matching note #project')
|
||||
note.sync_metadata()
|
||||
@@ -617,6 +671,35 @@ class CollectionTests(TestCase):
|
||||
self.assertContains(response, 'name="include_links"')
|
||||
self.assertContains(response, 'name="include_journal"')
|
||||
|
||||
def test_current_list_can_be_exported_as_markdown(self):
|
||||
Item.objects.create(owner=self.user, kind=Item.Kind.TODO, content='Open task #project').sync_metadata()
|
||||
Item.objects.create(owner=self.user, kind=Item.Kind.NOTE, content='Other note')
|
||||
|
||||
response = self.client.get(f'{reverse("export_items")}?kind=todo&tag=project')
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response['Content-Type'], 'text/markdown; charset=utf-8')
|
||||
body = response.content.decode()
|
||||
self.assertIn('- [ ] Open task #project', body)
|
||||
self.assertNotIn('Other note', body)
|
||||
|
||||
def test_collection_can_be_exported_as_markdown(self):
|
||||
collection = Collection.objects.create(owner=self.user, title='Reading view', description='Guide')
|
||||
todo = Item.objects.create(owner=self.user, kind=Item.Kind.TODO, content='Todo section #project')
|
||||
note = Item.objects.create(owner=self.user, kind=Item.Kind.NOTE, content='## Note section #project')
|
||||
CollectionSection.objects.create(collection=collection, item=todo, position=0)
|
||||
CollectionSection.objects.create(collection=collection, item=note, position=1)
|
||||
|
||||
response = self.client.get(reverse('collection_export', args=[collection.id]))
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
body = response.content.decode()
|
||||
self.assertIn('# Reading view', body)
|
||||
self.assertIn('Guide', body)
|
||||
self.assertIn('- [ ] Todo section', body)
|
||||
self.assertIn('## Note section', body)
|
||||
self.assertNotIn('#project', body)
|
||||
|
||||
def test_collection_opens_as_continuous_reading_view(self):
|
||||
collection = Collection.objects.create(owner=self.user, title='Reading view')
|
||||
first = Item.objects.create(owner=self.user, kind=Item.Kind.NOTE, content='First paragraph')
|
||||
@@ -761,6 +844,31 @@ class ApiItemFilterTests(TestCase):
|
||||
item.sync_metadata()
|
||||
return item
|
||||
|
||||
def test_api_key_settings_only_offer_own_tags(self):
|
||||
self.client.force_login(self.user)
|
||||
self.create_item('Own tag #own')
|
||||
other = get_user_model().objects.create_user(username='api-other', password='secret')
|
||||
other_item = Item.objects.create(owner=other, content='Other tag #foreign')
|
||||
other_item.sync_metadata()
|
||||
|
||||
response = self.client.get(reverse('settings'))
|
||||
api_section = response.content.decode().split('id="api"', 1)[1]
|
||||
|
||||
self.assertIn('#own', api_section)
|
||||
self.assertNotIn('#foreign', api_section)
|
||||
|
||||
def test_api_key_cannot_be_created_with_foreign_tag(self):
|
||||
self.client.force_login(self.user)
|
||||
other = get_user_model().objects.create_user(username='api-other', password='secret')
|
||||
other_item = Item.objects.create(owner=other, content='Other tag #foreign')
|
||||
other_item.sync_metadata()
|
||||
foreign_tag = Tag.objects.get(name='foreign')
|
||||
|
||||
response = self.client.post(reverse('settings'), {'action': 'apikey', 'name': 'Bad key', 'tags': [foreign_tag.id]})
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertFalse(ApiKey.objects.filter(owner=self.user, name='Bad key').exists())
|
||||
|
||||
def test_api_key_scope_and_request_tags_are_combined(self):
|
||||
matching = self.create_item('Keyboard shortcuts #help #tasten')
|
||||
self.create_item('Git help #help #git')
|
||||
@@ -775,6 +883,19 @@ class ApiItemFilterTests(TestCase):
|
||||
ids = [item['id'] for item in response.json()['items']]
|
||||
self.assertEqual(ids, [matching.id])
|
||||
|
||||
def test_api_key_only_returns_owner_items_not_other_public_items(self):
|
||||
own = self.create_item('Own public help #help')
|
||||
other = get_user_model().objects.create_user(username='api-public-other', password='secret')
|
||||
public = Item.objects.create(owner=other, content='Other public help #help', visibility=Item.Visibility.PUBLIC)
|
||||
public.sync_metadata()
|
||||
|
||||
response = self.client.get(reverse('api_items'), HTTP_X_API_KEY=self.api_key.token)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
ids = [item['id'] for item in response.json()['items']]
|
||||
self.assertIn(own.id, ids)
|
||||
self.assertNotIn(public.id, ids)
|
||||
|
||||
def test_api_items_supports_limit_and_search(self):
|
||||
self.create_item('First #help #shell')
|
||||
second = self.create_item('Second #help #shell keyboard')
|
||||
|
||||
Reference in New Issue
Block a user