From e0d3caff053681a0864bf3aa5bae31e828a44f24 Mon Sep 17 00:00:00 2001 From: gzbender Date: Mon, 4 Feb 2019 20:31:46 +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 --- .../migrations/0022_auto_20180208_0647.py | 1 + apps/course/tests/test_views.py | 44 +++++++--- project/tests/factories.py | 81 +++++++++++++++++-- project/utils/selenium_utils.py | 50 ++++++++++++ 4 files changed, 160 insertions(+), 16 deletions(-) create mode 100644 project/utils/selenium_utils.py diff --git a/apps/course/migrations/0022_auto_20180208_0647.py b/apps/course/migrations/0022_auto_20180208_0647.py index c95cd3fc..54fc0991 100644 --- a/apps/course/migrations/0022_auto_20180208_0647.py +++ b/apps/course/migrations/0022_auto_20180208_0647.py @@ -8,6 +8,7 @@ class Migration(migrations.Migration): dependencies = [ ('course', '0021_auto_20180206_0632'), + ('content', '0005_auto_20180208_0520'), ] operations = [ diff --git a/apps/course/tests/test_views.py b/apps/course/tests/test_views.py index 29745bbb..29a48fb9 100644 --- a/apps/course/tests/test_views.py +++ b/apps/course/tests/test_views.py @@ -1,28 +1,50 @@ +from datetime import timedelta +from random import randint + +from selenium import webdriver +from django.utils import timezone from django.test import TestCase -from django_faker import Faker -from django.core.urlresolvers import reverse +from django.shortcuts import reverse -from apps.course.models import Course from project.tests.factories import * +from project.utils.selenium_utils import SeleniumExtensions as SE class CoursesTestCase(TestCase): @classmethod def setUpTestData(cls): - for i in range(10): - CourseFactory() + create_admin() + create_batch_unique(CourseFactory, status=Course.STATUS_CHOICES[:3], price=[0, 1000], + age=Course.AGE_CHOICES[:2], deferred_start_at=[None, timezone.now() + timedelta(days=randint(5, 15))]) def test_courses_url_accessible(self): + print('get ', reverse('courses')) resp = self.client.get(reverse('courses')) self.assertEqual(resp.status_code, 200) def test_course_url_accessible(self): - course = Course.objects.all()[:1][0] - resp = self.client.get(course.url) - self.assertEqual(resp.status_code, 200) + for course in Course.objects.filter(status=Course.PUBLISHED): + print('get ', course.url) + resp = self.client.get(course.url) + self.assertEqual(resp.status_code, 200) + @login_admin def test_course_edit_url_accessible(self): - course = Course.objects.all()[:1][0] - resp = self.client.get(course.url) - self.assertEqual(resp.status_code, 200) + for course in Course.objects.all(): + print('get ', reverse('course_edit', args=[course.id])) + resp = self.client.get(reverse('course_edit', args=[course.id])) + self.assertEqual(resp.status_code, 200) + + +class CourseEditTestCase(TestCase): + + @classmethod + def setUpTestData(cls): + cls.driver = webdriver.Chrome() + create_admin() + create_batch_unique(CourseFactory, status=Course.STATUS_CHOICES[:3], price=[0, 1000], + age=Course.AGE_CHOICES[:2], deferred_start_at=[None, timezone.now() + timedelta(days=randint(5, 15))]) + + def test_course_edit(self): + print('Course.objects.all().count()', Course.objects.all().count()) diff --git a/project/tests/factories.py b/project/tests/factories.py index 8ab316c9..5c7cd5da 100644 --- a/project/tests/factories.py +++ b/project/tests/factories.py @@ -1,13 +1,81 @@ -from factory.django import DjangoModelFactory, SubFactory +from itertools import combinations + +from factory.django import DjangoModelFactory, ImageField +import factory +import factory.fuzzy +from unidecode import unidecode +from django.utils.text import slugify + -from apps.user.models import * from apps.course.models import * +from apps.content.models import * +from apps.user.models import * + + +ADMIN_EMAIL = 'admin@mail.com' + + +def create_admin(): + admin = UserFactory(username=ADMIN_EMAIL, email=ADMIN_EMAIL, role=User.ADMIN_ROLE, + is_staff=True, is_superuser=True) + admin.set_password('admin') + admin.save() + return admin + + +def create_users(count_multiplier=1): + create_admin() + UserFactory.create_batch(10 * count_multiplier, role=User.USER_ROLE) + UserFactory.create_batch(5 * count_multiplier, role=User.AUTHOR_ROLE) + UserFactory.create_batch(5 * count_multiplier, role=User.TEACHER_ROLE) + UserFactory.create_batch(5, role=User.ADMIN_ROLE, is_staff=True) + + +def login_admin(fn): + def wrap(self, *args, **kwargs): + admin = User.objects.get(username=ADMIN_EMAIL) + self.client.force_login(admin) + try: + fn(self, *args, **kwargs) + finally: + self.client.logout() + return wrap + + +def create_batch_unique(factory_class, **kwargs): + model = factory_class._meta.model + values = [] + for k, v in kwargs.items(): + try: + f = model._meta.get_field(k) + except: + del kwargs[k] + if getattr(f, 'choices', None): + v = [c[0] for c in v] + values += ((k, val) for val in v) + for params in combinations(values, len(kwargs)): + data = dict(params) + if len(data) == len(kwargs): + factory_class(**data) + + +class ImageObjectFactory(DjangoModelFactory): + image = ImageField() + image_thumbnail = ImageField() class UserFactory(DjangoModelFactory): class Meta: model = User + first_name = factory.Faker('first_name') + last_name = factory.Faker('last_name') + email = factory.Sequence(lambda n: "test_user%d@mail.com" % n) + username = factory.LazyAttribute(lambda o: o.email) + gallery = None + photo = None #factory.SubFactory(ImageObjectFactory) + is_active = True + class CategoryFactory(DjangoModelFactory): class Meta: @@ -18,7 +86,10 @@ class CourseFactory(DjangoModelFactory): class Meta: model = Course - author = SubFactory(UserFactory) - category = SubFactory(CategoryFactory) - cover = None + author = factory.SubFactory(UserFactory, role=User.AUTHOR_ROLE) + title = factory.Faker('sentence', nb_words=6) + slug = factory.LazyAttribute(lambda o: slugify(unidecode(o.title[:90]))) + category = factory.SubFactory(CategoryFactory) + cover = None #factory.SubFactory(ImageObjectFactory) gallery = None + status = factory.Iterator([s[0] for s in Course.STATUS_CHOICES]) diff --git a/project/utils/selenium_utils.py b/project/utils/selenium_utils.py new file mode 100644 index 00000000..0b7f47cf --- /dev/null +++ b/project/utils/selenium_utils.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- + +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC + + +class elem_in(object): + default_find_fn = 'find_element_by_xpath' + def __init__(self, element, xpath, find_fn=None): + self.element = element + self.xpath = xpath + self.find_fn = self.default_find_fn if find_fn is None else find_fn + + def __call__(self, driver): + try: + return getattr(self.element, self.find_fn)(self.xpath) + except: + return False + +class elems_in(elem_in): + default_find_fn = 'find_elements_by_xpath' + + +class SeleniumExtensions(object): + + @classmethod + def wait_elem(cls, driver, xpath, inside_el=None, wait_time=10): + if inside_el: + return WebDriverWait(driver, wait_time).until(elem_in(inside_el, xpath)) + if xpath[:2] != '//': + raise Exception('XPath in wait_elem must start with //') + return WebDriverWait(driver, wait_time).until( + EC.presence_of_element_located( + (By.XPATH, xpath) + ) + ) + + @classmethod + def wait_elems(cls, driver, xpath, inside_el=None, wait_time=10): + if inside_el: + return WebDriverWait(driver, wait_time).until(elems_in(inside_el, xpath)) + if xpath[:2] != '//': + raise Exception('XPath in wait_elems must start with //') + return WebDriverWait(driver, wait_time).until( + EC.presence_of_all_elements_located( + (By.XPATH, xpath) + ) + ) \ No newline at end of file