Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
dist: xenial # for Python3.7+
language: python

env:
- MOZ_HEADLESS=1
addons:
firefox: latest

matrix:
include:
- python: 2.7
env: TOXENV=py27-django18,py27-django19,py27-django10,py27-django11
- python: 3.5
env: TOXENV=py35-django20
env: TOXENV=py35-django20-drf30
- python: 3.6
env: TOXENV=py36-django20,py36-django21,py36-django22
env: TOXENV=py36-django20-drf30,py36-django21-drf30,py36-django22-drf30
- python: 3.7
env: TOXENV=py37-django20,py37-django21,py37-django22
env: TOXENV=py37-django20-drf30,py37-django21-drf30,py37-django22-drf30

install:
- pip install codecov tox
- pip install codecov tox selenium
- wget https://github.com/mozilla/geckodriver/releases/download/v0.24.0/geckodriver-v0.24.0-linux64.tar.gz
- tar -xvzf geckodriver*
- chmod +x geckodriver && sudo mv geckodriver /usr/local/bin/

script: tox && codecov
9 changes: 7 additions & 2 deletions example/blog/urls.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
# coding: utf-8
from django.conf.urls import url
from django.conf.urls import url, include
from rest_framework import routers

from .views import BlogPostListView, BlogPostDetailView
from blog.views.views import BlogPostListView, BlogPostDetailView
from blog.views import api

router = routers.DefaultRouter()
router.register('blog-posts', api.BlogPostViewSet)

urlpatterns = [
url(r'^$', BlogPostListView.as_view(), name='posts_list'),
url(r'^(?P<pk>\d+)$', BlogPostDetailView.as_view(), name='blog_post_detail'),
url(r'^api-auth/', include('rest_framework.urls'))
]
Empty file added example/blog/views/__init__.py
Empty file.
40 changes: 40 additions & 0 deletions example/blog/views/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# coding: utf-8
from __future__ import unicode_literals

from rest_framework import routers, serializers, viewsets
from blog.models import BlogPost, Category, Tag, TextBlock

from light_draft.views import DraftAPIViewMixin


class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ['title']


class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ['title', 'colour_class']


class TextBlockSerializer(serializers.ModelSerializer):
class Meta:
model = TextBlock
fields = ['title', 'body']


class BlogPostSerializer(serializers.ModelSerializer):
tags = TagSerializer(many=True)
blocks = TextBlockSerializer(many=True)
category = CategorySerializer()

class Meta:
model = BlogPost
fields = ['pk', 'title', 'lead', 'body', 'category', 'tags', 'blocks']


class BlogPostViewSet(DraftAPIViewMixin, viewsets.ModelViewSet):
queryset = BlogPost.objects.all()
serializer_class = BlogPostSerializer
2 changes: 1 addition & 1 deletion example/blog/views.py → example/blog/views/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from django.views.generic.list import ListView
from django.views.generic.detail import DetailView

from .models import BlogPost
from blog.models import BlogPost


class BlogPostListView(ListView):
Expand Down
8 changes: 8 additions & 0 deletions example/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
'django.contrib.staticfiles',

'light_draft',
'rest_framework',

'blog',

Expand Down Expand Up @@ -109,3 +110,10 @@
'LOCATION': 'just-an-example',
}
}


REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
]
}
75 changes: 75 additions & 0 deletions example/tests/test_integrational.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import time

from django.test import LiveServerTestCase
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from selenium.webdriver.support.wait import WebDriverWait
from selenium import webdriver
from selenium.webdriver.common.keys import Keys

try:
from django.urls import reverse
except ImportError:
from django.core.urlresolvers import reverse

from tests import factories as f


class DraftTestCase(StaticLiveServerTestCase):

@classmethod
def setUpClass(cls):
super(DraftTestCase, cls).setUpClass()
cls.post = f.BlogPostFactory(tags_count=2, blocks_count=2)
cls.admin_change_url = reverse('admin:blog_blogpost_change', args=(cls.post.pk,))
cls.admin_preview_url = reverse('admin:blog_blogpost_preview', args=(cls.post.pk,))
cls.admin_user = f.UserFactory(username='admin')
cls.admin_user.set_password('admin')
cls.admin_user.save()

def setUp(self):
super(DraftTestCase, self).setUp()
self.selenium = webdriver.Firefox()
self.selenium.get('{}/admin/'.format(self.live_server_url))
self.assertEqual('{}/admin/login/?next=/admin/'.format(self.live_server_url), self.selenium.current_url)
self.selenium.find_element_by_name('username').send_keys('admin')
self.selenium.find_element_by_name('password').send_keys('admin')
self.selenium.find_element_by_xpath('//input[@type="submit"]').click()

WebDriverWait(self.selenium, 2).until(
lambda driver: driver.find_element_by_tag_name('body'))

self.assertEqual('{}/admin/'.format(self.live_server_url), self.selenium.current_url)

def tearDown(self):
self.selenium.quit()
super(DraftTestCase, self).tearDown()

def test_ok(self):
url = '{}{}'.format(self.live_server_url, self.admin_change_url)
self._get_url(url)

self.selenium.find_element_by_id('id_title').send_keys('Exiting new title')

draft_element = self.selenium.find_elements_by_css_selector('#content-main .object-tools a.previewlink')
self.assertEqual(len(draft_element), 1)

draft_element = draft_element[0]
draft_element.is_displayed()
self.assertEqual(draft_element.text, 'DRAFT PREVIEW')

draft_element.click()
self.selenium.switch_to_window(self.selenium.window_handles[1])
time.sleep(1) # need some delay after switching tabs...

expected_part_url = '{}{}?hash=blog:blogpost:{}'.format(
self.live_server_url,
self.post.get_absolute_url(),
self.post.pk
)

self.assertTrue(self.selenium.current_url.startswith(expected_part_url))

def _get_url(self, url):
"""Helper to open an URL and check that we weren't redirected somewhere."""
self.selenium.get(url)
self.assertEqual(url, self.selenium.current_url)
3 changes: 3 additions & 0 deletions example/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
from django.conf.urls import include, url
from django.contrib import admin

from blog.urls import router

admin.autodiscover()

urlpatterns = [
url(r'^', include('blog.urls')),
url(r'^admin/', admin.site.urls),
url(r'^api/v1/', include(router.urls)),
]
18 changes: 16 additions & 2 deletions light_draft/static/admin/light.draft.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
(function ($) {
$(function () {
var href = location.href.replace(/(\/change\/?$|\/$)/, '') + '/preview/', // TODO: generate an URL
button = $('<li><a href="'+href+'" class="previewlink" target="_blank">Draft preview</a></li>');
button = $('<li><a href="'+href+'" class="previewlink" target="_blank">Draft preview</a></li>'),
csrftoken = $("[name=csrfmiddlewaretoken]").val();

function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});

$('.object-tools > li > a.viewsitelink')
.closest('ul')
.find('li:first')
.before(button);

button.click(function () {
var form = $('form[method="post"][enctype="multipart/form-data"]'),
var form = $('form[method="post"]'),
m2m = form.find('select[multiple="multiple"][id$="_to"]'),
link;

Expand Down
20 changes: 16 additions & 4 deletions light_draft/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,19 @@ def get_object(self, *args, **kwargs):

return super(BaseDraftView, self).get_object(*args, **kwargs)

def get_context_data(self, *args, **kwargs):
context = super(BaseDraftView, self).get_context_data(*args, **kwargs)
context['is_draft_preview'] = True
return context

class DraftAPIViewMixin:

def get_object(self, *args, **kwargs):
if getattr(self, '__object', None):
return self.__object

if 'hash' in self.request.GET:
try:
self.__object = load_from_shapshot(
self.serializer_class.Meta.model, self.request.GET.get('hash'))
except DraftError:
raise Http404('Snapshot does not exist')
return self.__object

return super().get_object(*args, **kwargs)
40 changes: 36 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,28 +1,60 @@
appnope==0.1.0
astroid==2.2.5
atomicwrites==1.3.0
attrs==19.1.0
backports.shutil-get-terminal-size==1.0.0
blessings==1.7
bpython==0.17.1
certifi==2019.3.9
chardet==3.0.4
coverage==4.5.1
curtsies==0.3.0
decorator==4.1.2
Django==1.9
Django==2.2.3
djangorestframework==3.10.1
enum34==1.1.6
factory-boy==2.10.0
Faker==0.8.12
greenlet==0.4.15
idna==2.8
importlib-metadata==0.18
ipaddress==1.0.19
ipython==5.5.0
ipython-genutils==0.2.0
isort==4.3.21
lazy-object-proxy==1.4.1
mccabe==0.6.1
more-itertools==7.2.0
packaging==19.0
parameterized==0.6.1
pathlib2==2.3.0
pexpect==4.3.1
pickleshare==0.7.4
pluggy==0.12.0
prompt-toolkit==1.0.15
ptyprocess==0.5.2
py==1.8.0
Pygments==2.2.0
pylint==2.3.1
pyparsing==2.4.0
pytest==5.0.1
pytest-django==3.5.1
python-dateutil==2.7.2
pytz==2017.3
requests==2.21.0
scandir==1.6
selenium==3.141.0
simplegeneric==0.8.1
six==1.11.0
South==1.0
sqlparse==0.3.0
text-unidecode==1.2
tox==3.0.0
traitlets==4.3.2
typed-ast==1.4.0
typing==3.6.6
urllib3==1.24.1
virtualenv==16.6.2
wcwidth==0.1.7
coverage==4.5.1
tox==3.0.0
parameterized==0.6.1
wrapt==1.11.2
zipp==0.5.2
4 changes: 3 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tox]
envlist = py27-django{18,19,10,11},py{35,36,37}-django{20,21,22}
envlist = py27-django{18,19,10,11},py{35,36,37}-django{20,21,22}-drf{30}

[testenv]
deps =
Expand All @@ -13,6 +13,8 @@ deps =
factory-boy~=2.12.0
coverage~=4.5.0
parameterized~=0.7.0
drf30: djangorestframework~=3.10.0
selenium~=3.141.0

changedir = example
commands =
Expand Down