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

remotes/origin/feature/testing_courses_30-01-19
gzbender 7 years ago
parent 0ebfb7fcb7
commit 327925c7ba
  1. 3
      apps/content/tests.py
  2. 0
      apps/content/tests/__init__.py
  3. 129
      apps/content/tests/mixins.py
  4. 3
      apps/course/templates/course/course_edit.html
  5. 36
      apps/course/tests/test_views.py
  6. 2
      project/utils/selenium_utils.py
  7. 6
      web/src/components/CourseRedactor.vue
  8. 16
      web/src/components/blocks/BlockAdd.vue
  9. 9
      web/src/components/blocks/BlockContent.vue
  10. 6
      web/src/components/blocks/BlockImage.vue
  11. 8
      web/src/components/blocks/BlockImageText.vue
  12. 13
      web/src/components/blocks/BlockImages.vue
  13. 6
      web/src/components/blocks/BlockText.vue
  14. 6
      web/src/components/blocks/BlockVideo.vue

@ -1,3 +0,0 @@
from django.test import TestCase
# Create your tests here.

@ -0,0 +1,129 @@
import time
from factory.faker import Faker
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
class TestContentMixin:
content_data = []
autosave = True
def check_auto_saving(self):
if not self.autosave:
return
raise NotImplementedError()
def check_content_auto_saving(self, object):
if not self.autosave:
return
print('Check content autosaving',)
self.assertEqual(object.content.all().count(), len(self.content_data))
for i, item in enumerate(object.content.all().order_by('position', '-created_at',)):
item_data = self.content_data[i].get('data')
for key, value in item_data.items():
print('real obj field value:', getattr(item, key, None))
print('expected value:', value)
self.assertEqual(getattr(item, key), value)
print('OK')
def check_content(self, inside_el=None):
print('Check content block')
self.object_data['content'] = self.content_data
block_add_el = self.wait_elem_name('block-add', inside_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))
open_el.click()
time.sleep(1)
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.click()
time.sleep(0.5)
self.check_block_text(inside_el)
block_image_el = self.wait_elem_name('block-add-block-image', block_add_el)
block_image_el.click()
time.sleep(0.5)
self.check_block_image(inside_el)
block_image_text_el = self.wait_elem_name('block-add-block-image-text', block_add_el)
block_image_text_el.click()
time.sleep(0.5)
self.check_block_image_text(inside_el)
block_images_el = self.wait_elem_name('block-add-block-images', block_add_el)
block_images_el.click()
time.sleep(0.5)
self.check_block_images(inside_el)
block_video_el = self.wait_elem_name('block-add-block-video', block_add_el)
block_video_el.click()
time.sleep(0.5)
self.check_block_video(inside_el)
def check_block_text(self, inside_el=None):
print('Check block text')
time.sleep(1)
block_obj = {'type': 'text', 'data': {}}
block_el = self.wait_elem_name('block-text', inside_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.find_element(
By.XPATH, './/div[contains(@class, "redactor-layer")][@contenteditable]')
self.content_data.append(block_obj)
title = Faker('sentence', nb_words=6).generate({})
title_el.send_keys(title)
block_obj['data']['title'] = title
text = Faker('sentence', nb_words=50).generate({})
self.driver.execute_script("arguments[0].click();", text_el)
self.driver.execute_script("arguments[0].innerHtml = arguments[1];", text_el, text)
self.driver.execute_script("arguments[0].dispatchEvent(new Event('change'));", text_el)
# text_el.send_keys(text)
block_obj['data']['text'] = '<p>%s</p>' % text
self.check_auto_saving()
def check_block_image(self, inside_el=None):
print('Check block image')
time.sleep(1)
block_obj = {'type': 'image', 'data': {}}
block_el = self.wait_elem_name('block-image', inside_el)
title_el = self.wait_elem_name('block-image-title', block_el)
image_el = self.wait_elem_name('block-image-image', block_el)
self.content_data.append(block_obj)
title = Faker('sentence', nb_words=6).generate({})
title_el.send_keys(title)
block_obj['data']['title'] = title
# TODO: check image upload
self.check_auto_saving()
def check_block_image_text(self, inside_el=None):
print('Check block image-text')
time.sleep(1)
block_obj = {'type': 'image-text', 'data': {}}
block_el = self.wait_elem_name('block-image-text', inside_el)
title_el = self.wait_elem_name('block-image-text-title', block_el)
text_el = self.wait_elem_name('block-image-text-text-wrap', block_el).find_element(
By.XPATH, './/div[contains(@class, "redactor-layer")][@contenteditable]')
image_el = self.wait_elem_name('block-image-text-image', block_el)
self.content_data.append(block_obj)
title = Faker('sentence', nb_words=6).generate({})
title_el.send_keys(title)
block_obj['data']['title'] = title
text = Faker('sentence', nb_words=50).generate({})
self.driver.execute_script("arguments[0].click();", text_el)
self.driver.execute_script("arguments[0].innerHtml = arguments[1];", text_el, text)
self.driver.execute_script("arguments[0].dispatchEvent(new Event('change'));", text_el)
# text_el.send_keys(text)
block_obj['data']['text'] = '<p>%s</p>' % text
# TODO: check image upload
self.check_auto_saving()
def check_block_images(self, inside_el=None):
print('Check block images')
def check_block_video(self, inside_el=None):
print('Check block images')

@ -17,7 +17,8 @@
{% endblock header_buttons %}
{% block content %}
<course-redactor :live="{{ live }}" author-picture="{% if request.user.photo %}{{ request.user.photo.url }}{% else %}{% static 'img/user_default.jpg' %}{% endif %}"
<course-redactor name="course-redactor" :live="{{ live }}"
author-picture="{% if request.user.photo %}{{ request.user.photo.url }}{% else %}{% static 'img/user_default.jpg' %}{% endif %}"
author-name="{{ request.user.first_name }} {{ request.user.last_name }}"
access-token="{{ request.user.auth_token }}"
{% if object and object.id %}:course-id="{{ object.id }}"{% endif %}></course-redactor>

@ -14,6 +14,7 @@ from unidecode import unidecode
from project.tests.factories import create_admin, create_batch_unique, User, UserFactory, Course, CourseFactory
from project.tests import SeleniumTestCase
from apps.content.tests.mixins import TestContentMixin
from project.utils.selenium_utils import SeleniumExtensions as SE
'''
@ -50,7 +51,10 @@ class CoursesTestCase(TestCase):
'''
class CourseEditTestCase(SeleniumTestCase):
class CourseEditTestCase(TestContentMixin, SeleniumTestCase):
model = Course
object_id = None
object_data = {}
@classmethod
def setUpClass(cls):
@ -65,13 +69,23 @@ class CourseEditTestCase(SeleniumTestCase):
print('teardown CourseEditTestCase')
super().tearDownClass()
def check_auto_saving(self):
# TODO
time.sleep(5)
obj = self.model.objects.get(id=self.object_id)
for key, value in self.object_data.items():
if key != 'content':
self.assertEqual(getattr(obj, key), value)
self.check_content_auto_saving(obj)
def test_course_edit(self):
user = User.objects.filter(role=User.AUTHOR_ROLE).first()
self.login(user)
url = self.get_url(reverse('course_create'))
print('url', url)
print('url is', url)
self.driver.get(url)
print('page opened', self.driver.current_url)
course_redactor = self.wait_elem_name('course-redactor')
# visible always elements
title_el = self.wait_elem_name('course-title')
slug_el = self.wait_elem_name('course-slug')
@ -92,17 +106,25 @@ class CourseEditTestCase(SeleniumTestCase):
title = Faker('sentence', nb_words=6).generate({})
title_el.send_keys(title)
slug = slugify(unidecode(title[:90]))
print('title', title, 'slug', slug)
time.sleep(1)
self.object_data['title'] = title
self.object_data['slug'] = slug
# print('title:', title)
# print('slug:', slug)
time.sleep(2)
self.assertEqual(slug_el.get_attribute('value'), slug)
# check save
obj = self.model.objects.get(title=title, slug=slug)
self.assertEqual(bool(obj), True)
self.object_id = obj.id
print("is_paid_input_el.get_attribute('checked')", is_paid_input_el.get_attribute('checked'))
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)
time.sleep(1)
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:
@ -112,7 +134,7 @@ class CourseEditTestCase(SeleniumTestCase):
except:
self.fail('Price, old price and access_duration elements not shown')
self.assertFalse(is_deferred_input_el.get_attribute('checked'))
self.assertEqual(is_deferred_input_el.get_attribute('checked'), 'true')
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()
@ -121,6 +143,8 @@ class CourseEditTestCase(SeleniumTestCase):
time_el = self.wait_elem_name('course-time')
except:
self.fail('Date and time elements not shown')
self.check_content(content_el)
# lessons_el = self.wait_elem_name('course-lessons')
# add_lesson_el = self.wait_elem_name('course-add-lesson')

@ -17,7 +17,7 @@ class ElementIn(object):
def __call__(self, driver):
try:
return getattr(self.element, self.find_fn)(self.locator)
return getattr(self.element, self.find_fn)(*self.locator)
except:
return False

@ -99,14 +99,16 @@
<div v-if="course.is_paid" class="info__field field">
<div class="field__label field__label_gray">СТОИМОСТЬ</div>
<div class="field__wrap field__wrap__appended" style="width: 120px;">
<input type="text" class="field__input field__input__appended" v-model.number.lazy="displayPrice">
<input type="text" class="field__input field__input__appended" v-model.number.lazy="displayPrice"
name="course-price">
<button disabled class="field__append">руб.</button>
</div>
</div>
<div v-if="course.is_paid" class="info__field field" style="margin-left: 10px;">
<div class="field__label field__label_gray">СТОИМОСТЬ БЕЗ СКИДКИ</div>
<div class="field__wrap field__wrap__appended">
<input type="text" class="field__input field__input__appended" v-model.number.lazy="displayOldPrice">
<input type="text" class="field__input field__input__appended" v-model.number.lazy="displayOldPrice"
name="course-old-price">
<button disabled class="field__append">руб.</button>
</div>
</div>

@ -1,7 +1,7 @@
<template>
<div class="kit__section">
<div class="kit__section" name="block-add">
<div v-if="!isOpen" class="kit__add add">
<button type="button" class="add__toggle" @click="isOpen = true">
<button type="button" class="add__toggle" @click="isOpen = true" name="block-add-open">
<span class="add__circle">
<svg class="icon icon-add-plus">
<use xlink:href="/static/img/sprite.svg#icon-add-plus"></use>
@ -11,7 +11,7 @@
</button>
</div>
<div v-if="isOpen" class="kit__add add open">
<button type="button" class="add__toggle" @click="isOpen = false">
<button type="button" class="add__toggle" @click="isOpen = false" name="block-add-close">
<span class="add__circle">
<svg class="icon icon-add-plus">
<use xlink:href="/static/img/sprite.svg#icon-add-plus"></use>
@ -20,27 +20,27 @@
<span class="add__title">Добавить блок</span>
</button>
<div class="add__list">
<button class="add__btn" type="button" @click="addBlockText">
<button class="add__btn" type="button" @click="addBlockText" name="block-add-block-text">
<svg class="icon icon-text">
<use xlink:href="/static/img/sprite.svg#icon-text"></use>
</svg>
</button>
<button class="add__btn" type="button" @click="addBlockImage">
<button class="add__btn" type="button" @click="addBlockImage" name="block-add-block-image">
<svg class="icon icon-image">
<use xlink:href="/static/img/sprite.svg#icon-image"></use>
</svg>
</button>
<button type="button" class="add__btn" @click="addBlockImageText">
<button type="button" class="add__btn" @click="addBlockImageText" name="block-add-block-image-text">
<svg class="icon icon-image-text">
<use xlink:href="/static/img/sprite.svg#icon-image-text"></use>
</svg>
</button>
<button type="button" class="add__btn" @click="addBlockImages">
<button type="button" class="add__btn" @click="addBlockImages" name="block-add-block-images">
<svg class="icon icon-images">
<use xlink:href="/static/img/sprite.svg#icon-images"></use>
</svg>
</button>
<button type="button" class="add__btn" @click="addBlockVideo">
<button type="button" class="add__btn" @click="addBlockVideo" name="block-add-block-video">
<svg class="icon icon-video-stroke">
<use xlink:href="/static/img/sprite.svg#icon-video-stroke"></use>
</svg>

@ -1,14 +1,15 @@
<template>
<div>
<vue-draggable :list="content" @start="drag=true" @end="drag=false" :options="{ handle: '.sortable__handle' }">
<div v-for="(block, index) in content" :key="block.id ? block.id : block.uuid" class="kit__section kit__section--block">
<div v-for="(block, index) in content" :key="block.id ? block.id : block.uuid" class="kit__section kit__section--block"
name="block-content-block">
<div class="kit__section-remove">
<button if="index != 0" type="button" @click="moveUp(index)">
<button if="index != 0" type="button" @click="moveUp(index)" name="block-content-move-up">
<svg class="icon icon-arrow-up">
<use xlink:href="/static/img/sprite.svg#icon-arrow-down"></use>
</svg>
</button>
<button if="index < (content.length - 1)" type="button" @click="moveDown(index)">
<button if="index < (content.length - 1)" type="button" @click="moveDown(index)" name="block-content-move-down">
<svg class="icon icon-arrow-down">
<use xlink:href="/static/img/sprite.svg#icon-arrow-down"></use>
</svg>
@ -18,7 +19,7 @@
<use xlink:href="/static/img/sprite.svg#icon-hamburger"></use>
</svg>
</button>
<button type="button" @click="onBlockRemoved(index)">
<button type="button" @click="onBlockRemoved(index)" name="block-content-delete">
<svg class="icon icon-delete">
<use xlink:href="/static/img/sprite.svg#icon-delete"></use>
</svg>

@ -1,8 +1,8 @@
<template>
<div>
<div name="block-image">
<div class="kit__field field">
<div class="field__wrap field__wrap--title">
<input type="text"
<input type="text" name="block-image-title"
:value="title"
class="field__input"
placeholder="Заголовок раздела"
@ -10,7 +10,7 @@
</div>
</div>
<div class="kit__row">
<lil-image :image-id="imageId" :image-url="imageUrl" v-on:update:imageUrl="onUpdateImageUrl"
<lil-image :image-id="imageId" :image-url="imageUrl" v-on:update:imageUrl="onUpdateImageUrl" name="block-image-image"
v-on:update:imageId="onUpdateImageId" :access-token="accessToken" />
</div>
</div>

@ -1,12 +1,12 @@
<template>
<div>
<div name="block-image-text">
<div class="kit__row">
<lil-image :image-id="imageId" :image-url="imageUrl" v-on:update:imageUrl="onUpdateImageUrl"
v-on:update:imageId="onUpdateImageId" :access-token="accessToken"/>
v-on:update:imageId="onUpdateImageId" :access-token="accessToken" name="block-image-text-image"/>
<div class="kit__fieldset">
<div class="kit__field field">
<div class="field__wrap field__wrap--title">
<input type="text"
<input type="text" name="block-image-text-title"
:value="title"
class="field__input"
placeholder="Заголовок раздела"
@ -14,7 +14,7 @@
</div>
</div>
<div class="kit__field field">
<div class="field__wrap">
<div class="field__wrap" name="block-image-text-text-wrap">
<vue-redactor :value="text" v-on:update:value="onTextChange" placeholder="Описание"/>
</div>
</div>

@ -1,8 +1,8 @@
<template>
<div>
<div name="block-images">
<div v-if="! noTitle" class="kit__field field">
<div class="field__wrap field__wrap--title">
<input :readonly="readOnly" type="text"
<input :readonly="readOnly" type="text" name="block-images-title"
:value="title"
class="field__input"
placeholder="Заголовок раздела"
@ -10,16 +10,17 @@
</div>
</div>
<div class="kit__gallery">
<div class="kit__preview" v-for="(image, index) in images" v-bind:class="{ 'kit__preview--loading': image.loading }">
<img :src="image.image_thumbnail_url" class="kit__pic">
<button class="kit__delete-photo" type="button" @click="onRemoveImage(index)">
<div class="kit__preview" v-for="(image, index) in images" v-bind:class="{ 'kit__preview--loading': image.loading }"
name="block-images-image-block">
<img :src="image.image_thumbnail_url" class="kit__pic" name="block-images-image">
<button class="kit__delete-photo" type="button" @click="onRemoveImage(index)" name="block-images-delete-image">
<svg class="icon icon-delete">
<use xlink:href="/static/img/sprite.svg#icon-delete"></use>
</svg>
</button>
</div>
<div class="kit__photo">
<svg class="icon icon-add-plus">
<svg class="icon icon-add-plus" name="block-images-add-image">
<use xlink:href="/static/img/sprite.svg#icon-add-plus"></use>
</svg>
<input type="file" class="kit__file" multiple @change="onImageAdded">

@ -1,8 +1,8 @@
<template>
<div>
<div name="block-text">
<div class="kit__field field">
<div class="field__wrap field__wrap--title">
<input type="text"
<input type="text" name="block-text-title"
:value="title"
class="field__input"
placeholder="Заголовок раздела"
@ -10,7 +10,7 @@
</div>
</div>
<div class="kit__field field">
<div class="field__wrap">
<div class="field__wrap" name="block-text-text-wrap">
<vue-redactor :value="text" v-on:update:value="onTextChange" placeholder="Описание"/>
</div>
</div>

@ -1,8 +1,8 @@
<template>
<div>
<div name="block-video">
<div class="kit__field field">
<div class="field__wrap field__wrap--title">
<input type="text"
<input type="text" name="block-video-title"
:value="title"
class="field__input"
placeholder="Заголовок раздела"
@ -12,7 +12,7 @@
<div class="kit__field field">
<div class="field__wrap">
<div class="field__flex">
<input type="text"
<input type="text" name="block-video-url"
:value="videoUrl"
class="field__input field__input_sm"
placeholder="Вставьте ссылку на Vimeo, YouTube, или другой сервис"

Loading…
Cancel
Save