Покрыть тестами - заполнение курсов.

remotes/origin/feature/testing_courses_30-01-19
gzbender 7 years ago
parent 327925c7ba
commit 2eef7078eb
  1. 37
      apps/content/tests/mixins.py
  2. 17
      apps/course/tests/test_views.py
  3. 16
      project/tests/__init__.py
  4. 51
      project/utils/selenium_utils.py
  5. 7
      web/src/components/CourseRedactor.vue

@ -3,6 +3,7 @@ import time
from factory.faker import Faker from factory.faker import Faker
from selenium.common.exceptions import TimeoutException from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
from django.utils.html import strip_tags
class TestContentMixin: class TestContentMixin:
@ -22,9 +23,10 @@ class TestContentMixin:
for i, item in enumerate(object.content.all().order_by('position', '-created_at',)): for i, item in enumerate(object.content.all().order_by('position', '-created_at',)):
item_data = self.content_data[i].get('data') item_data = self.content_data[i].get('data')
for key, value in item_data.items(): for key, value in item_data.items():
print('real obj field value:', getattr(item, key, None)) if key == 'txt':
print('expected value:', value) self.assertEqual(strip_tags(getattr(item, key)), value)
self.assertEqual(getattr(item, key), value) else:
self.assertEqual(getattr(item, key), value)
print('OK') print('OK')
def check_content(self, inside_el=None): def check_content(self, inside_el=None):
@ -34,7 +36,7 @@ class TestContentMixin:
open_el = self.wait_elem_name('block-add-open', block_add_el) open_el = self.wait_elem_name('block-add-open', block_add_el)
self.assertRaises(TimeoutException, lambda: self.wait_elem_name('block-add-close', block_add_el)) self.assertRaises(TimeoutException, lambda: self.wait_elem_name('block-add-close', block_add_el))
open_el.click() open_el.click()
time.sleep(1) time.sleep(0.5)
close_el = self.wait_elem_name('block-add-close', block_add_el) close_el = self.wait_elem_name('block-add-close', block_add_el)
block_text_el = self.wait_elem_name('block-add-block-text', block_add_el) block_text_el = self.wait_elem_name('block-add-block-text', block_add_el)
@ -42,21 +44,29 @@ class TestContentMixin:
time.sleep(0.5) time.sleep(0.5)
self.check_block_text(inside_el) self.check_block_text(inside_el)
open_el.click()
time.sleep(0.5)
block_image_el = self.wait_elem_name('block-add-block-image', block_add_el) block_image_el = self.wait_elem_name('block-add-block-image', block_add_el)
block_image_el.click() block_image_el.click()
time.sleep(0.5) time.sleep(0.5)
self.check_block_image(inside_el) self.check_block_image(inside_el)
open_el.click()
time.sleep(0.5)
block_image_text_el = self.wait_elem_name('block-add-block-image-text', block_add_el) block_image_text_el = self.wait_elem_name('block-add-block-image-text', block_add_el)
block_image_text_el.click() block_image_text_el.click()
time.sleep(0.5) time.sleep(0.5)
self.check_block_image_text(inside_el) self.check_block_image_text(inside_el)
open_el.click()
time.sleep(0.5)
block_images_el = self.wait_elem_name('block-add-block-images', block_add_el) block_images_el = self.wait_elem_name('block-add-block-images', block_add_el)
block_images_el.click() block_images_el.click()
time.sleep(0.5) time.sleep(0.5)
self.check_block_images(inside_el) self.check_block_images(inside_el)
open_el.click()
time.sleep(0.5)
block_video_el = self.wait_elem_name('block-add-block-video', block_add_el) block_video_el = self.wait_elem_name('block-add-block-video', block_add_el)
block_video_el.click() block_video_el.click()
time.sleep(0.5) time.sleep(0.5)
@ -68,8 +78,7 @@ class TestContentMixin:
block_obj = {'type': 'text', 'data': {}} block_obj = {'type': 'text', 'data': {}}
block_el = self.wait_elem_name('block-text', inside_el) block_el = self.wait_elem_name('block-text', inside_el)
title_el = self.wait_elem_name('block-text-title', block_el) title_el = self.wait_elem_name('block-text-title', block_el)
text_el = self.wait_elem_name('block-text-text-wrap', block_el) text_el = self.wait_elem_name('block-text-text-wrap', block_el).find_element(
text_el.find_element(
By.XPATH, './/div[contains(@class, "redactor-layer")][@contenteditable]') By.XPATH, './/div[contains(@class, "redactor-layer")][@contenteditable]')
self.content_data.append(block_obj) self.content_data.append(block_obj)
@ -77,11 +86,9 @@ class TestContentMixin:
title_el.send_keys(title) title_el.send_keys(title)
block_obj['data']['title'] = title block_obj['data']['title'] = title
text = Faker('sentence', nb_words=50).generate({}) text = Faker('sentence', nb_words=50).generate({})
self.driver.execute_script("arguments[0].click();", text_el) text_el.click()
self.driver.execute_script("arguments[0].innerHtml = arguments[1];", text_el, text) text_el.send_keys(text)
self.driver.execute_script("arguments[0].dispatchEvent(new Event('change'));", text_el) block_obj['data']['txt'] = text
# text_el.send_keys(text)
block_obj['data']['text'] = '<p>%s</p>' % text
self.check_auto_saving() self.check_auto_saving()
def check_block_image(self, inside_el=None): def check_block_image(self, inside_el=None):
@ -114,11 +121,9 @@ class TestContentMixin:
title_el.send_keys(title) title_el.send_keys(title)
block_obj['data']['title'] = title block_obj['data']['title'] = title
text = Faker('sentence', nb_words=50).generate({}) text = Faker('sentence', nb_words=50).generate({})
self.driver.execute_script("arguments[0].click();", text_el) text_el.click()
self.driver.execute_script("arguments[0].innerHtml = arguments[1];", text_el, text) text_el.send_keys(text)
self.driver.execute_script("arguments[0].dispatchEvent(new Event('change'));", text_el) block_obj['data']['txt'] = text
# text_el.send_keys(text)
block_obj['data']['text'] = '<p>%s</p>' % text
# TODO: check image upload # TODO: check image upload
self.check_auto_saving() self.check_auto_saving()

@ -1,8 +1,8 @@
from datetime import timedelta from datetime import timedelta
from random import randint from random import randint
import time import time
import re
from selenium import webdriver
from selenium.common.exceptions import TimeoutException from selenium.common.exceptions import TimeoutException
from factory.faker import Faker from factory.faker import Faker
from django.utils import timezone from django.utils import timezone
@ -70,9 +70,11 @@ class CourseEditTestCase(TestContentMixin, SeleniumTestCase):
super().tearDownClass() super().tearDownClass()
def check_auto_saving(self): def check_auto_saving(self):
# TODO time.sleep(0.5)
time.sleep(5) request = self.wait_for_request(f'/api/v1/courses/{self.object_id}/')
obj = self.model.objects.get(id=self.object_id) obj = self.model.objects.get(id=self.object_id)
self.assertEqual(request[2].get('status'), 200)
self.assertNotEqual(request[2].get('response'), None)
for key, value in self.object_data.items(): for key, value in self.object_data.items():
if key != 'content': if key != 'content':
self.assertEqual(getattr(obj, key), value) self.assertEqual(getattr(obj, key), value)
@ -85,6 +87,8 @@ class CourseEditTestCase(TestContentMixin, SeleniumTestCase):
print('url is', url) print('url is', url)
self.driver.get(url) self.driver.get(url)
print('page opened', self.driver.current_url) print('page opened', self.driver.current_url)
self.add_requests_log()
course_redactor = self.wait_elem_name('course-redactor') course_redactor = self.wait_elem_name('course-redactor')
# visible always elements # visible always elements
title_el = self.wait_elem_name('course-title') title_el = self.wait_elem_name('course-title')
@ -106,11 +110,11 @@ class CourseEditTestCase(TestContentMixin, SeleniumTestCase):
title = Faker('sentence', nb_words=6).generate({}) title = Faker('sentence', nb_words=6).generate({})
title_el.send_keys(title) title_el.send_keys(title)
slug = slugify(unidecode(title[:90])) slug = slugify(unidecode(title[:90]))
slug = re.sub(r'[^-\w]+$', '', slug)
slug = re.sub(r'[^-\w]', '-', slug)
self.object_data['title'] = title self.object_data['title'] = title
self.object_data['slug'] = slug self.object_data['slug'] = slug
# print('title:', title) time.sleep(1)
# print('slug:', slug)
time.sleep(2)
self.assertEqual(slug_el.get_attribute('value'), slug) self.assertEqual(slug_el.get_attribute('value'), slug)
# check save # check save
@ -151,4 +155,3 @@ class CourseEditTestCase(TestContentMixin, SeleniumTestCase):
# lesson_edit_el = self.wait_elem_name('course-lesson-edit') # lesson_edit_el = self.wait_elem_name('course-lesson-edit')
# stream_el = self.wait_elem_name('course-stream') # stream_el = self.wait_elem_name('course-stream')

@ -3,19 +3,21 @@ from selenium import webdriver
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
from pyvirtualdisplay import Display from pyvirtualdisplay import Display
from django.conf import settings from django.conf import settings
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from project.utils.selenium_utils import SeleniumExtensions as SE from project.utils.selenium_utils import SeleniumExtensions as SE
from apps.auth.models import TempToken from apps.auth.models import TempToken
class SeleniumTestCase(LiveServerTestCase ): class SeleniumTestCase(LiveServerTestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
super().setUpClass() super().setUpClass()
cls.display = Display(visible=0, size=(1280, 1024)) cls.display = Display(visible=0, size=(1280, 1024))
cls.display.start() cls.display.start()
cls.driver = webdriver.Chrome() cls.driver = webdriver.Firefox()
@classmethod @classmethod
def tearDownClass(cls): def tearDownClass(cls):
@ -48,6 +50,16 @@ class SeleniumTestCase(LiveServerTestCase ):
return SE.wait_elems(self.driver, (By.NAME, name), inside_el, wait_time) return SE.wait_elems(self.driver, (By.NAME, name), inside_el, wait_time)
def login(self, user): def login(self, user):
TempToken.objects.all().delete()
tt = TempToken.objects.create(user=user) tt = TempToken.objects.create(user=user)
self.driver.get('%s?temp-token=%s' % (self.get_url(), tt.key)) self.driver.get('%s?temp-token=%s' % (self.get_url(), tt.key))
def add_requests_log(self):
SE.add_requests_log(self.driver)
def get_requests(self):
return SE.get_requests(self.driver)
def wait_for_request(self, path, wait_time=10):
return SE.wait_for_request(self.driver, path, wait_time)

@ -1,7 +1,5 @@
# -*- coding: utf-8 -*- # -*- 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.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support import expected_conditions as EC
@ -44,3 +42,52 @@ class SeleniumExtensions(object):
return WebDriverWait(driver, wait_time).until( return WebDriverWait(driver, wait_time).until(
EC.presence_of_all_elements_located(locator) EC.presence_of_all_elements_located(locator)
) )
@classmethod
def wait_for_js_load(cls, driver, wait_time=10):
WebDriverWait(driver, wait_time).until(
lambda driver: driver.execute_script('return document.readyState') == 'complete')
@classmethod
def add_requests_log(cls, driver):
js = '''
(function() {
var open = XMLHttpRequest.prototype.open;
var send = XMLHttpRequest.prototype.send;
window._requestsLog = [];
window._findRequest = function(pathOrXHR){
for(var i=window._requestsLog.length - 1; i >= 0; i--){
var request = window._requestsLog[i];
if(typeof(pathOrXHR) == 'string' && request[1].indexOf(pathOrXHR) > -1
|| request[2] === pathOrXHR){
return request;
}
}
}
XMLHttpRequest.prototype.open = function(method, url){
window._requestsLog.push([method, url, this]);
return open.apply(this, arguments);
}
XMLHttpRequest.prototype.send = function(body){
var r = window._findRequest(this);
if(r){
r.push(body);
}
return send.apply(this, arguments);
}
})();
'''
return driver.execute_script(js)
@classmethod
def get_requests(cls, driver):
return driver.execute_script('return window._requestsLog;')
@classmethod
def wait_for_request(cls, driver, path, wait_time=10):
WebDriverWait(driver, wait_time).until(
lambda driver: driver.execute_script('''
var r = window._findRequest(arguments[0]);
return !!r && r[2].readyState == 4;
''', path) is True)
return driver.execute_script('return window._findRequest(arguments[0]);', path)

@ -487,10 +487,13 @@
onCoursePriceChange(event) { onCoursePriceChange(event) {
this.course.price = event.target.value; this.course.price = event.target.value;
}, },
getSlug(text) {
return slugify(text || '').toLowerCase().replace(/[^-\w]+$/, '').replace(/[^-\w]/g, '-');
},
onCourseNameInput() { onCourseNameInput() {
this.$v.course.title.$touch(); this.$v.course.title.$touch();
if (!this.slugChanged && !this.$v.course.status) { if (!this.slugChanged && !this.$v.course.status) {
this.course.slug = (slugify(this.course.title) || '').toLowerCase(); this.course.slug = this.getSlug(this.course.title);
} }
}, },
removeLesson(lessonIndex) { removeLesson(lessonIndex) {
@ -767,7 +770,7 @@
this.courseSaving = true; this.courseSaving = true;
this.changeSavingStatus(); this.changeSavingStatus();
const courseObject = this.course; const courseObject = this.course;
courseObject.slug = courseObject.slug && slugify(courseObject.slug); courseObject.slug = this.getSlug(courseObject.slug);
api.saveCourse(courseObject, this.accessToken) api.saveCourse(courseObject, this.accessToken)
.then((response) => { .then((response) => {
this.courseSaving = false; this.courseSaving = false;

Loading…
Cancel
Save