From 59797388aca02d73725a04922840d61a7d639221 Mon Sep 17 00:00:00 2001 From: gzbender Date: Tue, 19 Feb 2019 21:51:26 +0500 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=BE=D0=BA=D1=80=D1=8B=D1=82=D1=8C=20?= =?UTF-8?q?=D1=82=D0=B5=D1=81=D1=82=D0=B0=D0=BC=D0=B8=20-=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=BF=D0=BE=D0=BB=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BA=D1=83?= =?UTF-8?q?=D1=80=D1=81=D0=BE=D0=B2.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/auth/__init__.py | 1 + apps/auth/migrations/0001_initial.py | 25 ++++++++++++++++ apps/auth/models.py | 27 ++++++++++++++++-- apps/course/tests/test_views.py | 41 ++++++++++++++++----------- project/settings.py | 2 +- project/tests/__init__.py | 21 ++++++++++---- project/tests/factories.py | 1 + requirements.txt | 15 ++++++++-- web/src/components/CourseRedactor.vue | 3 +- 9 files changed, 105 insertions(+), 31 deletions(-) create mode 100644 apps/auth/migrations/0001_initial.py diff --git a/apps/auth/__init__.py b/apps/auth/__init__.py index e69de29b..193dde43 100644 --- a/apps/auth/__init__.py +++ b/apps/auth/__init__.py @@ -0,0 +1 @@ +default_app_config = 'apps.auth.apps.AuthConfig' diff --git a/apps/auth/migrations/0001_initial.py b/apps/auth/migrations/0001_initial.py new file mode 100644 index 00000000..04bbcee2 --- /dev/null +++ b/apps/auth/migrations/0001_initial.py @@ -0,0 +1,25 @@ +# Generated by Django 2.0.7 on 2019-02-19 18:31 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='TempToken', + fields=[ + ('key', models.CharField(max_length=40, primary_key=True, serialize=False, verbose_name='Key')), + ('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='auth_temp_token', to=settings.AUTH_USER_MODEL, verbose_name='User')), + ], + ), + ] diff --git a/apps/auth/models.py b/apps/auth/models.py index d95d3f20..8fbac91a 100644 --- a/apps/auth/models.py +++ b/apps/auth/models.py @@ -1,8 +1,29 @@ +import binascii +import os + from django.db import models +from django.conf import settings +from django.utils.translation import ugettext_lazy as _ -from rest_framework.authtoken.models import Token +class TempToken(models.Model): + key = models.CharField(_("Key"), max_length=40, primary_key=True) + user = models.OneToOneField( + settings.AUTH_USER_MODEL, related_name='auth_temp_token', + on_delete=models.CASCADE, verbose_name=_("User") + ) + created = models.DateTimeField(_("Created"), auto_now_add=True) -class TempToken(Token): class Meta: - app_label = 'auth' + app_label = 'lilcity_auth' + + def save(self, *args, **kwargs): + if not self.key: + self.key = self.generate_key() + return super().save(*args, **kwargs) + + def generate_key(self): + return binascii.hexlify(os.urandom(20)).decode() + + def __str__(self): + return self.key diff --git a/apps/course/tests/test_views.py b/apps/course/tests/test_views.py index c1305ad2..5d791de9 100644 --- a/apps/course/tests/test_views.py +++ b/apps/course/tests/test_views.py @@ -1,7 +1,9 @@ from datetime import timedelta from random import randint +import time from selenium import webdriver +from selenium.common.exceptions import TimeoutException from factory.faker import Faker from django.utils import timezone from django.test import TestCase @@ -10,7 +12,7 @@ from django.conf import settings from django.utils.text import slugify from unidecode import unidecode -from project.tests.factories import * +from project.tests.factories import create_admin, create_batch_unique, User, UserFactory, Course, CourseFactory from project.tests import SeleniumTestCase from project.utils.selenium_utils import SeleniumExtensions as SE @@ -51,7 +53,8 @@ class CoursesTestCase(TestCase): class CourseEditTestCase(SeleniumTestCase): @classmethod - def setUpTestData(cls): + def setUpClass(cls): + super().setUpClass() create_admin() UserFactory.create_batch(5, role=User.AUTHOR_ROLE) # create_batch_unique(CourseFactory, status=Course.STATUS_CHOICES[:3], price=[0, 1000], @@ -63,24 +66,22 @@ class CourseEditTestCase(SeleniumTestCase): super().tearDownClass() def test_course_edit(self): - print('go to google') - self.driver.get('http://www.google.com') - return - user = User.objects.filter(role=User.AUTHOR_ROLE).first() - self.client.force_login(user) + self.login(user) url = self.get_url(reverse('course_create')) print('url', url) self.driver.get(url) + print('page opened', self.driver.current_url) # visible always elements title_el = self.wait_elem_name('course-title') slug_el = self.wait_elem_name('course-slug') category_el = self.wait_elem_name('course-category') is_paid_no_el = self.wait_elem_name('course-is-paid-no') is_paid_yes_el = self.wait_elem_name('course-is-paid-yes') - is_paid_input_el = self.wait_elem_name('course-is-paid-input') + is_paid_input_el = self.wait_elem_css('[name=course-is-paid-input]:checked') age_el = self.wait_elem_name('course-age') - is_featured_el = self.wait_elem_name('course-is-featured') + # Only for ADMIN + # is_featured_el = self.wait_elem_name('course-is-featured') is_deferred_no_el = self.wait_elem_name('course-is-deferred-no') is_deferred_yes_el = self.wait_elem_name('course-is-deferred-yes') is_deferred_input_el = self.wait_elem_name('course-is-deferred-input') @@ -88,26 +89,32 @@ class CourseEditTestCase(SeleniumTestCase): lessons_btn_el = self.wait_elem_name('course-lessons-btn') content_el = self.wait_elem_name('course-content') - title = Faker('sentence', nb_words=6) + title = Faker('sentence', nb_words=6).generate({}) title_el.send_keys(title) slug = slugify(unidecode(title[:90])) print('title', title, 'slug', slug) - self.assertEqual(slug_el.text, slug) + time.sleep(1) + self.assertEqual(slug_el.get_attribute('value'), slug) print("is_paid_input_el.get_attribute('checked')", is_paid_input_el.get_attribute('checked')) - self.assertFalse(is_paid_input_el.get_attribute('checked')) - self.assertRaises(callable=lambda: self.driver.find_element_by_name('course-price')) - self.assertRaises(callable=lambda: self.driver.find_element_by_name('course-old-price')) + self.assertEqual(is_paid_input_el.get_attribute('value'), 'false') + self.assertRaises(TimeoutException, callable=lambda: self.driver.find_element_by_name('course-price')) + self.assertRaises(TimeoutException, callable=lambda: self.driver.find_element_by_name('course-old-price')) + self.assertRaises(TimeoutException, callable=lambda: self.driver.find_element_by_name('course-access-duration')) is_paid_yes_el.click() + time.sleep(0.5) + is_paid_input_el = self.wait_elem_css('[name=course-is-paid-input]:checked') + self.assertEqual(is_paid_input_el.get_attribute('value'), 'true') try: price_el = self.wait_elem_name('course-price') old_price_el = self.wait_elem_name('course-old-price') + access_duration_el = self.wait_elem_name('course-access-duration') except: - self.fail('Price and old price elements not shown') + self.fail('Price, old price and access_duration elements not shown') self.assertFalse(is_deferred_input_el.get_attribute('checked')) - self.assertRaises(callable=lambda: self.driver.find_element_by_name('course-date')) - self.assertRaises(callable=lambda: self.driver.find_element_by_name('course-time')) + self.assertRaises(TimeoutException, callable=lambda: self.driver.find_element_by_name('course-date')) + self.assertRaises(TimeoutException, callable=lambda: self.driver.find_element_by_name('course-time')) is_deferred_yes_el.click() try: date_el = self.wait_elem_name('course-date') diff --git a/project/settings.py b/project/settings.py index 9f835203..edfcdf90 100644 --- a/project/settings.py +++ b/project/settings.py @@ -59,7 +59,7 @@ INSTALLED_APPS = [ 'django_user_agents', 'imagekit', ] + [ - 'apps.auth.apps', + 'apps.auth', 'apps.user', 'apps.notification', 'apps.payment', diff --git a/project/tests/__init__.py b/project/tests/__init__.py index b2548d50..41f2bc52 100644 --- a/project/tests/__init__.py +++ b/project/tests/__init__.py @@ -1,13 +1,14 @@ -from django.test import TestCase +from django.test import LiveServerTestCase from selenium import webdriver from selenium.webdriver.common.by import By from pyvirtualdisplay import Display +from django.conf import settings from project.utils.selenium_utils import SeleniumExtensions as SE -from django.conf import settings +from apps.auth.models import TempToken -class SeleniumTestCase(TestCase): +class SeleniumTestCase(LiveServerTestCase ): @classmethod def setUpClass(cls): @@ -18,12 +19,12 @@ class SeleniumTestCase(TestCase): @classmethod def tearDownClass(cls): - cls.driver.close() + cls.driver.quit() cls.display.stop() super().tearDownClass() - def get_url(self, url): - return 'http://%s%s' % (settings.MAIN_HOST, url) + def get_url(self, url=''): + return '%s%s' % (self.live_server_url, url) def wait_elem_xpath(self, xpath, inside_el=None, wait_time=10): return SE.wait_elem(self.driver, (By.XPATH, xpath), inside_el, wait_time) @@ -40,5 +41,13 @@ class SeleniumTestCase(TestCase): def wait_elem_name(self, name, inside_el=None, wait_time=10): return SE.wait_elem(self.driver, (By.NAME, name), inside_el, wait_time) + def wait_elem_id(self, id, inside_el=None, wait_time=10): + return SE.wait_elem(self.driver, (By.ID, id), inside_el, wait_time) + def wait_elems_name(self, name, inside_el=None, wait_time=10): return SE.wait_elems(self.driver, (By.NAME, name), inside_el, wait_time) + + def login(self, user): + tt = TempToken.objects.create(user=user) + self.driver.get('%s?temp-token=%s' % (self.get_url(), tt.key)) + diff --git a/project/tests/factories.py b/project/tests/factories.py index 5c7cd5da..a56fd68b 100644 --- a/project/tests/factories.py +++ b/project/tests/factories.py @@ -75,6 +75,7 @@ class UserFactory(DjangoModelFactory): gallery = None photo = None #factory.SubFactory(ImageObjectFactory) is_active = True + auth_token = None class CategoryFactory(DjangoModelFactory): diff --git a/requirements.txt b/requirements.txt index 4dac0884..c5d8b002 100644 --- a/requirements.txt +++ b/requirements.txt @@ -38,5 +38,16 @@ drf_dynamic_fields flower==0.9.2 unidecode factory-boy==2.11.1 -pyvirtualdisplay==0.2.1 # + sudo apt-get install xvfb + sudo apt-get install chromium-chromedriver -# + sudo ln -s /usr/lib/chromium-browser/chromedriver /usr/bin/chromedriver +pyvirtualdisplay==0.2.1 +selenium + # sudo apt-get install xvfb + # sudo apt-get install libappindicator3-1 fonts-liberation + # wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb + + # sudo apt-get install unzip + # wget -N https://chromedriver.storage.googleapis.com/73.0.3683.20/chromedriver_linux64.zip + # unzip chromedriver_linux64.zip + # chmod +x chromedriver + # sudo mv -f chromedriver /usr/local/share/chromedriver + # sudo ln -s /usr/local/share/chromedriver /usr/local/bin/chromedriver + # sudo ln -s /usr/local/share/chromedriver /usr/bin/chromedriver diff --git a/web/src/components/CourseRedactor.vue b/web/src/components/CourseRedactor.vue index 4e1ec796..f73144fb 100644 --- a/web/src/components/CourseRedactor.vue +++ b/web/src/components/CourseRedactor.vue @@ -90,7 +90,7 @@
ПРОДОЛЖИТЕЛЬНОСТЬ ДОСТУПА
+ @input="$v.course.access_duration.$touch()" name="course-access-duration">
@@ -612,7 +612,6 @@ this.lessons = data.lessons.map((lessonJson) => { return api.convertLessonJson(lessonJson); }); - this.course.access_duration = this.course.access_duration || ''; }, loadCourseDraft() { //console.log('loadCourseDraft');