diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7c4d831dc72434858e2f152cf8f2edebe728cd4b..c470586fa1e4319e31ac2c98e2bcd177035b4c08 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,22 +1,39 @@ stages: - test + - functional_test - deploy - # - functional_test UnitTest: image: python:3.6 stage: test before_script: + - pip3 install --upgrade pip - pip3 install -r requirements.txt - - pip3 install requests - - pip3 install django-environ + script: - python3 manage.py makemigrations - python3 manage.py migrate - - python3 manage.py runserver 8000 & + - python3 manage.py test lists + tags: + - test + + +TestFunctional: + image: python:3.6 + stage: functional_test + before_script: + - wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - + - echo "deb http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google.list + - pip3 install -r requirements.txt + - apt-get update -qq && apt-get install -y -qq unzip + - apt-get install -y google-chrome-stable + - apt-get install -y xvfb + - wget https://chromedriver.storage.googleapis.com/2.41/chromedriver_linux64.zip + - unzip chromedriver_linux64.zip when: on_success script: - - coverage run manage.py test practice - - coverage report -m + - python3 manage.py makemigrations + - python3 manage.py migrate + - python3 manage.py test functional_test Deployment: image: ruby:2.4 @@ -33,24 +50,4 @@ Deployment: name: production url: $HEROKU_APP_HOST only: - - master - -# FunctionalTest: -# image: python:3.6 -# stage: functional_test -# before_script: -# - wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - -# - echo "deb http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google.list -# - pip3 install -r requirements.txt -# - pip3 install requests -# - pip3 install django-environ -# - apt-get update -qq && apt-get install -y -qq unzip -# - apt-get install -y google-chrome-stable -# - apt-get install -y xvfb -# - wget https://chromedriver.storage.googleapis.com/2.41/chromedriver_linux64.zip -# - unzip chromedriver_linux64.zip -# when: on_success -# script: -# - python3 functional_tests.py -# only: -# - master + - master \ No newline at end of file diff --git a/functional_test/test_list.py b/functional_test/test_list.py new file mode 100644 index 0000000000000000000000000000000000000000..e027cda65ff512990759ca58f4d8e1c8fd4dec86 --- /dev/null +++ b/functional_test/test_list.py @@ -0,0 +1,26 @@ +from selenium.webdriver.common.keys import Keys + +from .tests import FunctionalTest + +class ItemValidationTest(FunctionalTest): + + def test_item_validation(self): + self.browser.get(self.live_server_url) + self.get_item_input_box().send_keys(Keys.ENTER) + + self.wait_for(lambda: self.browser.find_elements_by_css_selector( + '#id_text:invalid' + )) + + self.get_item_input_box().send_keys('Buy milk') + self.wait_for(lambda: self.browser.find_elements_by_css_selector( + '#id_text:valid' + )) + + self.get_item_input_box().send_keys(Keys.ENTER) + self.wait_for_row_in_list_table('2: Buy milk') + + self.get_item_input_box().send_keys('Buy cheese') + self.get_item_input_box().send_keys(Keys.ENTER) + + self.wait_for_row_in_list_table('3: Buy cheese') \ No newline at end of file diff --git a/functional_test/tests.py b/functional_test/tests.py index 009e93a2da25b1ceecc969a4e41a8c498b545b00..0901d3a0aa629d679a25194b7956486065218760 100644 --- a/functional_test/tests.py +++ b/functional_test/tests.py @@ -1,70 +1,79 @@ +import sys +import time + +from django.contrib.staticfiles.testing import StaticLiveServerTestCase from selenium import webdriver from selenium.common.exceptions import WebDriverException -from selenium.webdriver.common.keys import Keys -from selenium.webdriver.common.by import By -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.support import expected_conditions from selenium.webdriver.chrome.options import Options -from django.test import LiveServerTestCase -import time -import unittest -import environ -MAX_WAIT = 5 +MAX_WAIT = 10 + + +def wait(fn): + def modified_fn(*args, **kwargs): + start_time = time.time() + while True: + try: + return fn(*args, **kwargs) + except (AssertionError, WebDriverException) as e: + if time.time() - start_time > MAX_WAIT: + raise e + time.sleep(0.5) + + return modified_fn + + +class FunctionalTest(StaticLiveServerTestCase): -class NewVisitorTest(LiveServerTestCase): + @classmethod + def setUpClass(cls): + for arg in sys.argv: + if 'liveserver' in arg: + cls.server_url = 'http://' + arg.split('=')[1] + return + super().setUpClass() + cls.server_url = cls.live_server_url def setUp(self): - self.browser = webdriver.Chrome() + chrome_options = Options() + chrome_options.add_argument('--dns-prefetch-disable') + chrome_options.add_argument('--no-sandbox') + chrome_options.add_argument('--headless') + chrome_options.add_argument('disable-gpu') + try: + self.browser = webdriver.Chrome('./chromedriver.exe', chrome_options=chrome_options) + except: + self.browser = webdriver.Chrome('./chromedriver', chrome_options=chrome_options) def tearDown(self): self.browser.quit() - def check_for_row(self, row_text): + 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): - self.browser.get(self.live_server_url) + def get_item_input_box(self): + return self.browser.find_element_by_id('id_new_item') - self.assertIn('To-Do', self.browser.title) - header_text = self.browser.find_element_by_tag_name('h1').text - self.assertIn('To-Do', header_text) - - introduction = self.browser.find_element_by_id("intro") - self.assertIn('Izzatul', introduction.text) - - comment = self.browser.find_element_by_id('comment') - self.assertEqual(comment.text, 'yey, waktunya libur') - - inputbox = self.browser.find_element_by_id('id_new_item') - self.assertEqual( - inputbox.get_attribute('placeholder'), - 'Enter a to-do item' - ) - - inputbox.send_keys('Buy peacock feathers') - - inputbox.send_keys(Keys.ENTER) - WebDriverWait(self.browser, 10).until( - expected_conditions.text_to_be_present_in_element( - (By.ID, 'id_list_table'), 'Buy peacock feathers')) - self.check_for_row('1: Buy peacock feathers') - - 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) + @wait + def wait_for(self, fn): + return fn() + @wait + def wait_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.check_for_row('1: Buy peacock feathers') - WebDriverWait(self.browser, 10).until( - expected_conditions.text_to_be_present_in_element( - (By.ID, 'id_list_table'), 'Use peacock feathers to make a fly')) - self.check_for_row('2: Use peacock feathers to make a fly') - - # self.fail('Finish the test!') + self.assertIn(row_text, [row.text for row in rows]) -if __name__ == '__main__': - unittest.main(warnings='ignore') + @wait + def wait_to_be_logged_in(self, email): + self.browser.find_element_by_link_text('Log out') + navbar = self.browser.find_element_by_css_selector('.navbar') + self.assertIn(email, navbar.text) + + @wait + def wait_to_be_logged_out(self, email): + self.browser.find_element_by_name('email') + navbar = self.browser.find_element_by_css_selector('.navbar') + self.assertNotIn(email, navbar.text) \ No newline at end of file diff --git a/practice/__init__.py b/lists/__init__.py similarity index 100% rename from practice/__init__.py rename to lists/__init__.py diff --git a/practice/admin.py b/lists/admin.py similarity index 100% rename from practice/admin.py rename to lists/admin.py diff --git a/lists/apps.py b/lists/apps.py new file mode 100644 index 0000000000000000000000000000000000000000..efe43f03b32216436c4e15becbab60ef7d342598 --- /dev/null +++ b/lists/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class ListsConfig(AppConfig): + name = 'lists' diff --git a/practice/helper.py b/lists/helper.py similarity index 100% rename from practice/helper.py rename to lists/helper.py diff --git a/practice/models.py b/lists/models.py similarity index 58% rename from practice/models.py rename to lists/models.py index 201edc2ec8bbfab70ac37d820416e704ce180af5..7ba0fddfe438a342c9e3619446dd95ebad45c1d4 100644 --- a/practice/models.py +++ b/lists/models.py @@ -1,4 +1,4 @@ from django.db import models class Item(models.Model): - text = models.TextField(default='') + text = models.TextField(default='') \ No newline at end of file diff --git a/practice/templates/homepage/home.html b/lists/templates/homepage/home.html similarity index 100% rename from practice/templates/homepage/home.html rename to lists/templates/homepage/home.html diff --git a/practice/tests.py b/lists/tests.py similarity index 100% rename from practice/tests.py rename to lists/tests.py diff --git a/practice/urls.py b/lists/urls.py similarity index 100% rename from practice/urls.py rename to lists/urls.py diff --git a/practice/views.py b/lists/views.py similarity index 100% rename from practice/views.py rename to lists/views.py diff --git a/practice/apps.py b/practice/apps.py deleted file mode 100644 index e84e85b2d41c66efff6f19574832ee62e7390b3a..0000000000000000000000000000000000000000 --- a/practice/apps.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.apps import AppConfig - - -class PracticeConfig(AppConfig): - name = 'practice' diff --git a/testproject/settings.py b/testproject/settings.py index 41a8ddbb412c8bfabab1f57161c283a1440559c7..5d73af676163bd615fe213f93fefd54a194d4cc4 100644 --- a/testproject/settings.py +++ b/testproject/settings.py @@ -11,10 +11,11 @@ https://docs.djangoproject.com/en/2.2/ref/settings/ """ import os - +import socket +import dj_database_url # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - +PRODUCTION = os.environ.get('DATABASE_URL') != None # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/ @@ -37,7 +38,7 @@ INSTALLED_APPS = [ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', - 'practice', + 'lists' ] MIDDLEWARE = [ diff --git a/testproject/urls.py b/testproject/urls.py index bf62cb52157c727f250b02997d5965d928359b1d..8e8e2127bab611510ee4be07e82593ad0d964a7f 100644 --- a/testproject/urls.py +++ b/testproject/urls.py @@ -17,9 +17,9 @@ from django.contrib import admin from django.urls import path from django.conf.urls import url, include -import practice.urls as practice +import lists.urls as lists urlpatterns = [ path('admin/', admin.site.urls), - url(r'^', include((practice, 'practice'), namespace='practice')), + url(r'^', include((lists, 'lists'), namespace='lists')), ]