Compare commits
1 Commits
94f3120add
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
168c877970 |
@@ -20,7 +20,7 @@ class ListDetailAPITest(BaseAPITest):
|
|||||||
response = self.client.get(f"/api/lists/{list_.id}/")
|
response = self.client.get(f"/api/lists/{list_.id}/")
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEqual(response.data["id"], list_.id)
|
self.assertEqual(response.data["id"], str(list_.id))
|
||||||
self.assertEqual(len(response.data["items"]), 2)
|
self.assertEqual(len(response.data["items"]), 2)
|
||||||
|
|
||||||
class ListItemsAPITest(BaseAPITest):
|
class ListItemsAPITest(BaseAPITest):
|
||||||
@@ -48,7 +48,7 @@ class ListsAPITest(BaseAPITest):
|
|||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEqual(len(response.data), 1)
|
self.assertEqual(len(response.data), 1)
|
||||||
self.assertEqual(response.data[0]["id"], list1.id)
|
self.assertEqual(response.data[0]["id"], str(list1.id))
|
||||||
|
|
||||||
def test_post_creates_list_with_item(self):
|
def test_post_creates_list_with_item(self):
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from . import views
|
|||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', views.ListsAPI.as_view(), name='api_lists'),
|
path('', views.ListsAPI.as_view(), name='api_lists'),
|
||||||
path('<int:list_id>/', views.ListDetailAPI.as_view(), name='api_list_detail'),
|
path('<uuid:list_id>/', views.ListDetailAPI.as_view(), name='api_list_detail'),
|
||||||
path('<int:list_id>/items/', views.ListItemsAPI.as_view(), name='api_list_items'),
|
path('<uuid:list_id>/items/', views.ListItemsAPI.as_view(), name='api_list_items'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
# Generated by Django 6.0 on 2026-02-08 01:19
|
# Generated by Django 6.0 on 2026-02-23 04:30
|
||||||
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
import uuid
|
||||||
|
from django.conf import settings
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
@@ -9,13 +11,16 @@ class Migration(migrations.Migration):
|
|||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='List',
|
name='List',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||||
|
('owner', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='lists', to=settings.AUTH_USER_MODEL)),
|
||||||
|
('shared_with', models.ManyToManyField(blank=True, related_name='shared_lists', to=settings.AUTH_USER_MODEL)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
# Generated by Django 6.0 on 2026-02-09 03:29
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('dashboard', '0001_initial'),
|
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='list',
|
|
||||||
name='owner',
|
|
||||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='lists', to=settings.AUTH_USER_MODEL),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
# Generated by Django 6.0 on 2026-02-18 18:13
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('dashboard', '0002_list_owner'),
|
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='list',
|
|
||||||
name='shared_with',
|
|
||||||
field=models.ManyToManyField(blank=True, related_name='shared_lists', to=settings.AUTH_USER_MODEL),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
|
import uuid
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
|
|
||||||
class List(models.Model):
|
class List(models.Model):
|
||||||
|
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||||
owner = models.ForeignKey(
|
owner = models.ForeignKey(
|
||||||
"lyric.User",
|
"lyric.User",
|
||||||
related_name="lists",
|
related_name="lists",
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class ItemModelTest(TestCase):
|
|||||||
class ListModelTest(TestCase):
|
class ListModelTest(TestCase):
|
||||||
def test_get_absolute_url(self):
|
def test_get_absolute_url(self):
|
||||||
mylist = List.objects.create()
|
mylist = List.objects.create()
|
||||||
self.assertEqual(mylist.get_absolute_url(), f"/dashboard/{mylist.id}/")
|
self.assertEqual(mylist.get_absolute_url(), f"/dashboard/list/{mylist.id}/")
|
||||||
|
|
||||||
def test_list_items_order(self):
|
def test_list_items_order(self):
|
||||||
list1 = List.objects.create()
|
list1 = List.objects.create()
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class NewListTest(TestCase):
|
|||||||
def test_redirects_after_POST(self):
|
def test_redirects_after_POST(self):
|
||||||
response = self.client.post("/dashboard/new_list", data={"text": "A new list item"})
|
response = self.client.post("/dashboard/new_list", data={"text": "A new list item"})
|
||||||
new_list = List.objects.get()
|
new_list = List.objects.get()
|
||||||
self.assertRedirects(response, f"/dashboard/{new_list.id}/")
|
self.assertRedirects(response, f"/dashboard/list/{new_list.id}/")
|
||||||
|
|
||||||
# Post invalid input helper
|
# Post invalid input helper
|
||||||
def post_invalid_input(self):
|
def post_invalid_input(self):
|
||||||
@@ -58,12 +58,12 @@ class NewListTest(TestCase):
|
|||||||
class ListViewTest(TestCase):
|
class ListViewTest(TestCase):
|
||||||
def test_uses_list_template(self):
|
def test_uses_list_template(self):
|
||||||
mylist = List.objects.create()
|
mylist = List.objects.create()
|
||||||
response = self.client.get(f"/dashboard/{mylist.id}/")
|
response = self.client.get(f"/dashboard/list/{mylist.id}/")
|
||||||
self.assertTemplateUsed(response, "apps/dashboard/list.html")
|
self.assertTemplateUsed(response, "apps/dashboard/list.html")
|
||||||
|
|
||||||
def test_renders_input_form(self):
|
def test_renders_input_form(self):
|
||||||
mylist = List.objects.create()
|
mylist = List.objects.create()
|
||||||
url = f"/dashboard/{mylist.id}/"
|
url = f"/dashboard/list/{mylist.id}/"
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
parsed = lxml.html.fromstring(response.content)
|
parsed = lxml.html.fromstring(response.content)
|
||||||
forms = parsed.cssselect("form[method=POST]")
|
forms = parsed.cssselect("form[method=POST]")
|
||||||
@@ -80,7 +80,7 @@ class ListViewTest(TestCase):
|
|||||||
other_list = List.objects.create()
|
other_list = List.objects.create()
|
||||||
Item.objects.create(text="other list item", list=other_list)
|
Item.objects.create(text="other list item", list=other_list)
|
||||||
# When/Act
|
# When/Act
|
||||||
response = self.client.get(f"/dashboard/{correct_list.id}/")
|
response = self.client.get(f"/dashboard/list/{correct_list.id}/")
|
||||||
# Then/Assert
|
# Then/Assert
|
||||||
self.assertContains(response, "itemey 1")
|
self.assertContains(response, "itemey 1")
|
||||||
self.assertContains(response, "itemey 2")
|
self.assertContains(response, "itemey 2")
|
||||||
@@ -91,7 +91,7 @@ class ListViewTest(TestCase):
|
|||||||
correct_list = List.objects.create()
|
correct_list = List.objects.create()
|
||||||
|
|
||||||
self.client.post(
|
self.client.post(
|
||||||
f"/dashboard/{correct_list.id}/",
|
f"/dashboard/list/{correct_list.id}/",
|
||||||
data={"text": "A new item for an existing list"},
|
data={"text": "A new item for an existing list"},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -105,16 +105,16 @@ class ListViewTest(TestCase):
|
|||||||
correct_list = List.objects.create()
|
correct_list = List.objects.create()
|
||||||
|
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
f"/dashboard/{correct_list.id}/",
|
f"/dashboard/list/{correct_list.id}/",
|
||||||
data={"text": "A new item for an existing list"},
|
data={"text": "A new item for an existing list"},
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertRedirects(response, f"/dashboard/{correct_list.id}/")
|
self.assertRedirects(response, f"/dashboard/list/{correct_list.id}/")
|
||||||
|
|
||||||
# Post invalid input helper
|
# Post invalid input helper
|
||||||
def post_invalid_input(self):
|
def post_invalid_input(self):
|
||||||
mylist = List.objects.create()
|
mylist = List.objects.create()
|
||||||
return self.client.post(f"/dashboard/{mylist.id}/", data={"text": ""})
|
return self.client.post(f"/dashboard/list/{mylist.id}/", data={"text": ""})
|
||||||
|
|
||||||
def test_for_invalid_input_nothing_saved_to_db(self):
|
def test_for_invalid_input_nothing_saved_to_db(self):
|
||||||
self.post_invalid_input()
|
self.post_invalid_input()
|
||||||
@@ -140,7 +140,7 @@ class ListViewTest(TestCase):
|
|||||||
Item.objects.create(list=list1, text="lorem ipsum")
|
Item.objects.create(list=list1, text="lorem ipsum")
|
||||||
|
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
f"/dashboard/{list1.id}/",
|
f"/dashboard/list/{list1.id}/",
|
||||||
data={"text": "lorem ipsum"},
|
data={"text": "lorem ipsum"},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -189,16 +189,16 @@ class ShareListTest(TestCase):
|
|||||||
our_list = List.objects.create()
|
our_list = List.objects.create()
|
||||||
alice = User.objects.create(email="alice@example.com")
|
alice = User.objects.create(email="alice@example.com")
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
f"/dashboard/{our_list.id}/share_list",
|
f"/dashboard/list/{our_list.id}/share_list",
|
||||||
data={"recipient": "alice@example.com"},
|
data={"recipient": "alice@example.com"},
|
||||||
)
|
)
|
||||||
self.assertRedirects(response, f"/dashboard/{our_list.id}/")
|
self.assertRedirects(response, f"/dashboard/list/{our_list.id}/")
|
||||||
|
|
||||||
def test_post_with_email_adds_user_to_shared_with(self):
|
def test_post_with_email_adds_user_to_shared_with(self):
|
||||||
our_list = List.objects.create()
|
our_list = List.objects.create()
|
||||||
alice = User.objects.create(email="alice@example.com")
|
alice = User.objects.create(email="alice@example.com")
|
||||||
self.client.post(
|
self.client.post(
|
||||||
f"/dashboard/{our_list.id}/share_list",
|
f"/dashboard/list/{our_list.id}/share_list",
|
||||||
data={"recipient": "alice@example.com"},
|
data={"recipient": "alice@example.com"},
|
||||||
)
|
)
|
||||||
self.assertIn(alice, our_list.shared_with.all())
|
self.assertIn(alice, our_list.shared_with.all())
|
||||||
@@ -206,10 +206,10 @@ class ShareListTest(TestCase):
|
|||||||
def test_post_with_nonexistent_email_redirects_to_list(self):
|
def test_post_with_nonexistent_email_redirects_to_list(self):
|
||||||
our_list = List.objects.create()
|
our_list = List.objects.create()
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
f"/dashboard/{our_list.id}/share_list",
|
f"/dashboard/list/{our_list.id}/share_list",
|
||||||
data={"recipient": "nobody@example.com"},
|
data={"recipient": "nobody@example.com"},
|
||||||
)
|
)
|
||||||
self.assertRedirects(response, f"/dashboard/{our_list.id}/")
|
self.assertRedirects(response, f"/dashboard/list/{our_list.id}/")
|
||||||
|
|
||||||
def test_share_list_does_not_add_owner_as_recipient(self):
|
def test_share_list_does_not_add_owner_as_recipient(self):
|
||||||
owner = User.objects.create(email="owner@example.com")
|
owner = User.objects.create(email="owner@example.com")
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from . import views
|
|||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('new_list', views.new_list, name='new_list'),
|
path('new_list', views.new_list, name='new_list'),
|
||||||
path('<int:list_id>/', views.view_list, name='view_list'),
|
path('list/<uuid:list_id>/', views.view_list, name='view_list'),
|
||||||
path('users/<uuid:user_id>/', views.my_lists, name='my_lists'),
|
path('users/<uuid:user_id>/', views.my_lists, name='my_lists'),
|
||||||
path('<int:list_id>/share_list', views.share_list, name="share_list"),
|
path('list/<uuid:list_id>/share_list', views.share_list, name="share_list"),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -34,7 +34,10 @@ class NewVisitorTest(FunctionalTest):
|
|||||||
list_page.add_list_item("Buy peacock feathers")
|
list_page.add_list_item("Buy peacock feathers")
|
||||||
|
|
||||||
edith_dash_url = self.browser.current_url
|
edith_dash_url = self.browser.current_url
|
||||||
self.assertRegex(edith_dash_url, '/dashboard/.+')
|
self.assertRegex(
|
||||||
|
edith_dash_url,
|
||||||
|
r'/dashboard/list/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/$',
|
||||||
|
)
|
||||||
|
|
||||||
self.browser.delete_all_cookies()
|
self.browser.delete_all_cookies()
|
||||||
|
|
||||||
@@ -46,7 +49,10 @@ class NewVisitorTest(FunctionalTest):
|
|||||||
list_page.add_list_item("Buy milk")
|
list_page.add_list_item("Buy milk")
|
||||||
|
|
||||||
francis_dash_url = self.browser.current_url
|
francis_dash_url = self.browser.current_url
|
||||||
self.assertRegex(francis_dash_url, '/dashboard/.+')
|
self.assertRegex(
|
||||||
|
francis_dash_url,
|
||||||
|
r'/dashboard/list/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/$',
|
||||||
|
)
|
||||||
self.assertNotEqual(francis_dash_url, edith_dash_url)
|
self.assertNotEqual(francis_dash_url, edith_dash_url)
|
||||||
|
|
||||||
page_text = self.browser.find_element(By.TAG_NAME, 'body').text
|
page_text = self.browser.find_element(By.TAG_NAME, 'body').text
|
||||||
|
|||||||
Reference in New Issue
Block a user