diff --git a/functional_tests.py b/functional_tests.py index 4723f38825de4b8f94e890e83cf2d7f18c3aa502..49b742eb0827ba9e8b3e6bb9db4647c15c2d18d4 100644 --- a/functional_tests.py +++ b/functional_tests.py @@ -1,9 +1,10 @@ from selenium import webdriver +from selenium.webdriver.common.keys import Keys import unittest +import time class NewVisitorTest(unittest.TestCase): - def setUp(self): self.browser = webdriver.Firefox() self.browser.implicitly_wait(3) @@ -11,6 +12,11 @@ class NewVisitorTest(unittest.TestCase): def tearDown(self): self.browser.quit() + def check_for_row_in_list_table(self, row_text): + table = self.browser.find_element_by_id('id_list_table') + rows = table.find_elements_by_tag_name('tr') + self.assertIn(row_text, [row.text for row in rows]) + def test_can_start_a_list_and_retrieve_it_later(self): # Edith has heard about a cool new online to-do app. She goes # to check out its homepage @@ -18,22 +24,47 @@ class NewVisitorTest(unittest.TestCase): # She notices the page title and header mention to-do lists self.assertIn('To-Do', self.browser.title) - self.fail('Finish the test!') + header_text = self.browser.find_element_by_tag_name('h1').text + self.assertIn('To-Do', header_text) # She is invited to enter a to-do item straight away + inputbox = self.browser.find_element_by_id('id_new_item') + self.assertEqual( + inputbox.get_attribute('placeholder'), + 'Enter a to-do item' + ) + # She types "Buy peacock feathers" into a text box (Edith's hobby # is tying fly-fishing lures) + inputbox.send_keys('Buy peacock feathers') + # When she hits enter, the page updates, and now the page lists # "1: Buy peacock feathers" as an item in a to-do list + inputbox.send_keys(Keys.ENTER) + time.sleep(1) + self.check_for_row_in_list_table('1: Buy peacock feathers') + # There is still a text box inviting her to add another item. She # enters "Use peacock feathers to make a fly" - # (Edith is very methodical) + # (Edith is very methodical) + inputbox = self.browser.find_element_by_id('id_new_item') + inputbox.send_keys('Use peacock feathers to make a fly') + inputbox.send_keys(Keys.ENTER) + time.sleep(1) + # The page updates again, and now shows both items on her list + self.check_for_row_in_list_table('1: Buy peacock feathers') + self.check_for_row_in_list_table('2: Use peacock feathers to make a fly') + # Edith wonders whether the site will remember her list. Then she sees # that the site has generated a unique URL for her -- there is some # explanatory text to that effect. + self.fail('Finish the test!') + # She visits that URL - her to-do list is still there. + # Satisfied, she goes back to sleep - if __name__ == '__main__': - unittest.main(warnings='ignore') + +if __name__ == '__main__': + unittest.main(warnings='ignore') diff --git a/lists/models.py b/lists/models.py index 71a836239075aa6e6e4ecb700e9c42c95c022d91..e9919df3ff3755e782739f7dd3e9103352f74b9a 100644 --- a/lists/models.py +++ b/lists/models.py @@ -1,3 +1,5 @@ from django.db import models -# Create your models here. + +class Item(models.Model): + text = models.TextField(default='') diff --git a/lists/templates/home.html b/lists/templates/home.html new file mode 100644 index 0000000000000000000000000000000000000000..972a691c8e492429736b1b05a37ce3a44ebac36e --- /dev/null +++ b/lists/templates/home.html @@ -0,0 +1,18 @@ + + + To-Do lists + + +

Your To-Do list

+
+ + {% csrf_token %} +
+ + + {% for item in items %} + + {% endfor %} +
{{ forloop.counter }}: {{ item.text }}
+ + \ No newline at end of file diff --git a/lists/tests.py b/lists/tests.py index d71cf7ff86d85b5b5cfbe68532b3095bfb60b9e7..fbdcc9be73d5b8c9fc693e2cc59d0645411c56c4 100644 --- a/lists/tests.py +++ b/lists/tests.py @@ -1,17 +1,61 @@ -from django.urls import resolve from django.test import TestCase -from lists.views import home_page -from django.http import HttpRequest +from lists.models import Item class HomePageTest(TestCase): - def test_root_url_resolves_to_home_page_view(self): - found = resolve('/') - self.assertEqual(found.func, home_page) + def test_uses_home_template(self): + response = self.client.get('/') + self.assertTemplateUsed(response, 'home.html') + + def test_can_save_a_POST_request(self): + self.client.post('/', data={'item_text': 'A new list item'}) + + self.assertEqual(Item.objects.count(), 1) + new_item = Item.objects.first() + self.assertEqual(new_item.text, 'A new list item') + + def test_redirects_after_POST(self): + response = self.client.post('/', data={'item_text': 'A new list item'}) + self.assertEqual(response.status_code, 302) + self.assertEqual(response['location'], '/') def test_home_page_returns_correct_html(self): - request = HttpRequest() - response = home_page(request) - self.assertTrue(response.content.startswith(b'')) - self.assertIn(b'To-Do lists', response.content) - self.assertTrue(response.content.endswith(b'')) + response = self.client.get('/') + html = response.content.decode('utf8') + self.assertTrue(html.startswith('')) + self.assertIn('To-Do lists', html) + self.assertTrue(html.strip().endswith('')) + self.assertTemplateUsed(response, 'home.html') + + def test_only_saves_items_when_necessary(self): + self.client.get('/') + self.assertEqual(Item.objects.count(), 0) + + def test_displays_all_list_items(self): + Item.objects.create(text='itemey 1') + Item.objects.create(text='itemey 2') + + response = self.client.get('/') + + self.assertIn('itemey 1', response.content.decode()) + self.assertIn('itemey 2', response.content.decode()) + + +class ItemModelTest(TestCase): + + def test_saving_and_retrieving_items(self): + first_item = Item() + first_item.text = 'The first (ever) list item' + first_item.save() + + second_item = Item() + second_item.text = 'Item the second' + second_item.save() + + saved_items = Item.objects.all() + self.assertEqual(saved_items.count(), 2) + + first_saved_item = saved_items[0] + second_saved_item = saved_items[1] + self.assertEqual(first_saved_item.text, 'The first (ever) list item') + self.assertEqual(second_saved_item.text, 'Item the second') diff --git a/lists/views.py b/lists/views.py index df3f630f8a37c46c6a5a856a28926a8a2c14249f..ef488d3b7356949193adc32f005ff3a2759bc50e 100644 --- a/lists/views.py +++ b/lists/views.py @@ -1,7 +1,11 @@ -from django.shortcuts import render -from django.http import HttpResponse +from django.shortcuts import redirect, render +from lists.models import Item -# Create your views here. def home_page(request): - return HttpResponse('To-Do lists') + if request.method == 'POST': + Item.objects.create(text=request.POST['item_text']) + return redirect('/') + + items = Item.objects.all() + return render(request, 'home.html', {'items': items}) diff --git a/superlists/settings.py b/superlists/settings.py index be79dc779ba4e7c3755ac32376cecf1f5530b2e8..a443f8d88e8f5c15918be4e15e845004d36b2f4e 100644 --- a/superlists/settings.py +++ b/superlists/settings.py @@ -37,6 +37,7 @@ INSTALLED_APPS = [ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'lists', ] MIDDLEWARE = [ @@ -54,7 +55,7 @@ ROOT_URLCONF = 'superlists.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], + 'DIRS': [os.path.join(BASE_DIR, 'templates')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ diff --git a/superlists/urls.py b/superlists/urls.py index 17be17b80af10b08f3f6f697ff9b5596e92493cc..b7683ae8982769a8d10cd5dbc7fb172c819b957e 100644 --- a/superlists/urls.py +++ b/superlists/urls.py @@ -13,10 +13,9 @@ Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ -from django.contrib import admin -from django.urls import path +from django.conf.urls import url +from lists import views urlpatterns = [ - path('$', 'lists.views.home') -# path('admin/', admin.site.urls), + url(r'^$', views.home_page, name='home'), ]