From fdaa90e1b89328d440c6e7f31a8a66174b6a0cd4 Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Sat, 18 Jul 2015 21:42:46 +0200 Subject: [PATCH 01/20] Sandbox shell executable single command --- Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5ed64ba..68472d1 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,10 @@ setup: deps db deps: @pip install -r requirements.txt -pyvows: db +sandbox_shell: + @env PYTHONPATH=$$PYTHONPATH:vows/sandbox/:. python vows/sandbox/manage.py shell + +pyvows: @env PYTHONPATH=$$PYTHONPATH:vows/sandbox/:. pyvows -c -l django_pyvows --profile-threshold 95 vows/ ci_test: From 451bb5eec240107f9394f2d3d29eb780bc05aeeb Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Sat, 18 Jul 2015 21:55:44 +0200 Subject: [PATCH 02/20] Update Django version to 1.8.3 --- requirements.txt | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index 57a7db9..20c2e0f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,5 @@ -django==1.3.1 -pyvows +Django==1.8.3 +pyVows==2.0.6 coverage==3.5.1 -httplib2 -lxml -cssselect -cherrypy +lxml==3.4.4 +cssselect==0.9.1 From 7381d866402abfcee9a8b90660f9d96d5d46e7e4 Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Sat, 18 Jul 2015 21:55:59 +0200 Subject: [PATCH 03/20] Update gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 0144ed7..cf60f24 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ dist/ .project .pydevproject build/ +*.sqlite3 From 163c036f847be97bcb9e92b22476c66977138ca0 Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Sat, 18 Jul 2015 21:56:40 +0200 Subject: [PATCH 04/20] Update sandbox project to Django 1.8.3 template --- vows/sandbox/__init__.py | 9 --- vows/sandbox/main/admin.py | 3 +- vows/sandbox/main/models.py | 1 + vows/sandbox/main/urls.py | 0 vows/sandbox/main/views.py | 3 + vows/sandbox/manage.py | 18 ++--- vows/sandbox/sandbox/__init__.py | 0 vows/sandbox/sandbox/settings.py | 109 +++++++++++++++++++++++++++++++ vows/sandbox/sandbox/urls.py | 25 +++++++ vows/sandbox/sandbox/wsgi.py | 16 +++++ 10 files changed, 163 insertions(+), 21 deletions(-) create mode 100644 vows/sandbox/main/urls.py create mode 100644 vows/sandbox/sandbox/__init__.py create mode 100644 vows/sandbox/sandbox/settings.py create mode 100644 vows/sandbox/sandbox/urls.py create mode 100644 vows/sandbox/sandbox/wsgi.py diff --git a/vows/sandbox/__init__.py b/vows/sandbox/__init__.py index 2e71dd5..e69de29 100644 --- a/vows/sandbox/__init__.py +++ b/vows/sandbox/__init__.py @@ -1,9 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# django-pyvows extensions -# https://github.com/rafaelcaricio/django-pyvows - -# Licensed under the MIT license: -# http://www.opensource.org/licenses/mit-license -# Copyright (c) 2011 Rafael Caricio rafael@caricio.com diff --git a/vows/sandbox/main/admin.py b/vows/sandbox/main/admin.py index f7e6678..e09a757 100644 --- a/vows/sandbox/main/admin.py +++ b/vows/sandbox/main/admin.py @@ -10,7 +10,8 @@ from django.contrib import admin -from sandbox.main.models import StringModel +from models import StringModel + class StringModelAdmin(admin.ModelAdmin): list_display = ('name', ) diff --git a/vows/sandbox/main/models.py b/vows/sandbox/main/models.py index f1c183e..8c7dbca 100644 --- a/vows/sandbox/main/models.py +++ b/vows/sandbox/main/models.py @@ -10,5 +10,6 @@ from django.db import models + class StringModel(models.Model): name = models.CharField(max_length=100) diff --git a/vows/sandbox/main/urls.py b/vows/sandbox/main/urls.py new file mode 100644 index 0000000..e69de29 diff --git a/vows/sandbox/main/views.py b/vows/sandbox/main/views.py index b956c7c..cefbb26 100644 --- a/vows/sandbox/main/views.py +++ b/vows/sandbox/main/views.py @@ -15,6 +15,7 @@ from django.http import HttpResponse def home(request): return HttpResponse('hello world') + def say_hello(request): SAY_HELLO_WITHOUT_NAME = getattr(settings, "SAY_HELLO_WITHOUT_NAME", False) if 'name' in request.GET: @@ -25,8 +26,10 @@ def say_hello(request): return HttpResponse("What's your name?") return HttpResponse("Hello, %s!" % name) + def post_it(request): return HttpResponse(request.POST['value']) + def post_file(request): return HttpResponse(request.FILES['the_file'].read().strip()) diff --git a/vows/sandbox/manage.py b/vows/sandbox/manage.py index 3e4eedc..3b13742 100755 --- a/vows/sandbox/manage.py +++ b/vows/sandbox/manage.py @@ -1,14 +1,10 @@ #!/usr/bin/env python -from django.core.management import execute_manager -import imp -try: - imp.find_module('settings') # Assumed to be in the same directory. -except ImportError: - import sys - sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n" % __file__) - sys.exit(1) - -import settings +import os +import sys if __name__ == "__main__": - execute_manager(settings) + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "sandbox.settings") + + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv) diff --git a/vows/sandbox/sandbox/__init__.py b/vows/sandbox/sandbox/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vows/sandbox/sandbox/settings.py b/vows/sandbox/sandbox/settings.py new file mode 100644 index 0000000..2ddacc8 --- /dev/null +++ b/vows/sandbox/sandbox/settings.py @@ -0,0 +1,109 @@ +""" +Django settings for sandbox project. + +Generated by 'django-admin startproject' using Django 1.8.3. + +For more information on this file, see +https://docs.djangoproject.com/en/1.8/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.8/ref/settings/ +""" + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +import os + +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = '+1e*=5h9ri-$#@xmgs*9wrm&@d#li6d9u3e&atmzo5s*6k%_#o' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = ( + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + + 'sandbox.main', +) + +MIDDLEWARE_CLASSES = ( + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'django.middleware.security.SecurityMiddleware', +) + +ROOT_URLCONF = 'sandbox.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [ + os.path.join(BASE_DIR, "templates"), + ], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'sandbox.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/1.8/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + + +# Internationalization +# https://docs.djangoproject.com/en/1.8/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.8/howto/static-files/ + +STATIC_URL = '/static/' + +# Custom config to be used in tests +SAY_HELLO_WITHOUT_NAME = False diff --git a/vows/sandbox/sandbox/urls.py b/vows/sandbox/sandbox/urls.py new file mode 100644 index 0000000..6a48525 --- /dev/null +++ b/vows/sandbox/sandbox/urls.py @@ -0,0 +1,25 @@ +"""sandbox URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/1.8/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') +Including another URLconf + 1. Add an import: from blog import urls as blog_urls + 2. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls)) +""" +from django.conf.urls import url +from main.views import home, say_hello, post_it, post_file + + +urlpatterns = [ + url(r'^$', home, name='home'), + url(r'^say/$', say_hello, name='say_hello'), + url(r'^post_it/$', post_it, name='post_it'), + url(r'^post_file/$', post_file, name='post_file'), +] diff --git a/vows/sandbox/sandbox/wsgi.py b/vows/sandbox/sandbox/wsgi.py new file mode 100644 index 0000000..0e7d688 --- /dev/null +++ b/vows/sandbox/sandbox/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for sandbox project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "sandbox.settings") + +application = get_wsgi_application() From 0f85836faf6809c9c648c9dae0f2951ffb99fba5 Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Sat, 18 Jul 2015 22:32:16 +0200 Subject: [PATCH 05/20] Fix sandbox project --- vows/__init__.py | 0 vows/sandbox/sandbox/settings.py | 2 +- vows/sandbox/settings.py | 153 ------------------------------- vows/sandbox/urls.py | 30 ------ 4 files changed, 1 insertion(+), 184 deletions(-) create mode 100644 vows/__init__.py delete mode 100644 vows/sandbox/settings.py delete mode 100644 vows/sandbox/urls.py diff --git a/vows/__init__.py b/vows/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vows/sandbox/sandbox/settings.py b/vows/sandbox/sandbox/settings.py index 2ddacc8..2364fa7 100644 --- a/vows/sandbox/sandbox/settings.py +++ b/vows/sandbox/sandbox/settings.py @@ -52,7 +52,7 @@ MIDDLEWARE_CLASSES = ( 'django.middleware.security.SecurityMiddleware', ) -ROOT_URLCONF = 'sandbox.urls' +ROOT_URLCONF = 'sandbox.sandbox.urls' TEMPLATES = [ { diff --git a/vows/sandbox/settings.py b/vows/sandbox/settings.py deleted file mode 100644 index 07fc918..0000000 --- a/vows/sandbox/settings.py +++ /dev/null @@ -1,153 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# django-pyvows extensions -# https://github.com/rafaelcaricio/django-pyvows - -# Licensed under the MIT license: -# http://www.opensource.org/licenses/mit-license -# Copyright (c) 2011 Rafael Caricio rafael@caricio.com - -# Django settings for sandbox project. - -import os.path - -DEBUG = True -TEMPLATE_DEBUG = DEBUG - -PROJECT_ROOT = os.path.realpath(os.path.dirname(__file__)) - -ADMINS = ( - # ('Your Name', 'your_email@example.com'), -) - -MANAGERS = ADMINS - -DATABASES = { - 'default': { - 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. - 'PORT': '', # Set to empty string for default. Not used with sqlite3. - 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. - 'NAME': 'db.db', # Or path to database file if using sqlite3. - 'USER': 'root', # Not used with sqlite3. - 'PASSWORD': '', # Not used with sqlite3. - } -} - -# Local time zone for this installation. Choices can be found here: -# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name -# although not all choices may be available on all operating systems. -# On Unix systems, a value of None will cause Django to use the same -# timezone as the operating system. -# If running in a Windows environment this must be set to the same as your -# system time zone. -TIME_ZONE = 'America/Chicago' - -# Language code for this installation. All choices can be found here: -# http://www.i18nguy.com/unicode/language-identifiers.html -LANGUAGE_CODE = 'en-us' - -SITE_ID = 1 - -# If you set this to False, Django will make some optimizations so as not -# to load the internationalization machinery. -USE_I18N = True - -# If you set this to False, Django will not format dates, numbers and -# calendars according to the current locale -USE_L10N = True - -# Absolute filesystem path to the directory that will hold user-uploaded files. -# Example: "/home/media/media.lawrence.com/media/" -MEDIA_ROOT = '' - -# URL that handles the media served from MEDIA_ROOT. Make sure to use a -# trailing slash. -# Examples: "http://media.lawrence.com/media/", "http://example.com/media/" -MEDIA_URL = '' - -# Absolute path to the directory static files should be collected to. -# Don't put anything in this directory yourself; store your static files -# in apps' "static/" subdirectories and in STATICFILES_DIRS. -# Example: "/home/media/media.lawrence.com/static/" -STATIC_ROOT = '' - -# URL prefix for static files. -# Example: "http://media.lawrence.com/static/" -STATIC_URL = '/static/' - -# URL prefix for admin static files -- CSS, JavaScript and images. -# Make sure to use a trailing slash. -# Examples: "http://foo.com/static/admin/", "/static/admin/". -ADMIN_MEDIA_PREFIX = '/static/admin/' - -# Additional locations of static files -STATICFILES_DIRS = ( - # Put strings here, like "/home/html/static" or "C:/www/django/static". - # Always use forward slashes, even on Windows. - # Don't forget to use absolute paths, not relative paths. -) - -# List of finder classes that know how to find static files in -# various locations. -STATICFILES_FINDERS = ( - 'django.contrib.staticfiles.finders.FileSystemFinder', - 'django.contrib.staticfiles.finders.AppDirectoriesFinder', -) - -# Make this unique, and don't share it with anybody. -SECRET_KEY = 'l7#du3ceob9^i^6^jambl@io7nxc^%&))b$uxlxa#-3lake5+w' - -# List of callables that know how to import templates from various sources. -TEMPLATE_LOADERS = ( - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', -) - -MIDDLEWARE_CLASSES = ( - 'django.middleware.common.CommonMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', -) - -ROOT_URLCONF = 'sandbox.urls' - -TEMPLATE_DIRS = ( - # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". - # Always use forward slashes, even on Windows. - # Don't forget to use absolute paths, not relative paths. - os.path.join(PROJECT_ROOT, "templates"), -) - -INSTALLED_APPS = ( - 'sandbox.main', - # Uncomment the next line to enable the admin: - # 'django.contrib.admin', - # Uncomment the next line to enable admin documentation: - # 'django.contrib.admindocs', -) - -SAY_HELLO_WITHOUT_NAME = False - -# A sample logging configuration. The only tangible logging -# performed by this configuration is to send an email to -# the site admins on every HTTP 500 error. -# See http://docs.djangoproject.com/en/dev/topics/logging for -# more details on how to customize your logging configuration. -LOGGING = { - 'version': 1, - 'disable_existing_loggers': False, - 'handlers': { - 'mail_admins': { - 'level': 'ERROR', - 'class': 'django.utils.log.AdminEmailHandler' - } - }, - 'loggers': { - 'django.request': { - 'handlers': ['mail_admins'], - 'level': 'ERROR', - 'propagate': True, - }, - } -} diff --git a/vows/sandbox/urls.py b/vows/sandbox/urls.py deleted file mode 100644 index 1cf3032..0000000 --- a/vows/sandbox/urls.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# django-pyvows extensions -# https://github.com/rafaelcaricio/django-pyvows - -# Licensed under the MIT license: -# http://www.opensource.org/licenses/mit-license -# Copyright (c) 2011 Rafael Caricio rafael@caricio.com - -from django.conf.urls.defaults import patterns, include, url - -# Uncomment the next two lines to enable the admin: -from django.contrib import admin -admin.autodiscover() - -urlpatterns = patterns('', - url(r'^$', 'sandbox.main.views.home', name='home'), - url(r'^say/$', 'sandbox.main.views.say_hello', name='say_hello'), - url(r'^post_it/$', 'sandbox.main.views.post_it', name='post_it'), - url(r'^post_file/$', 'sandbox.main.views.post_file', name='post_file'), - - # url(r'^sandbox/', include('sandbox.foo.urls')), - - # Uncomment the admin/doc line below to enable admin documentation: - # url(r'^admin/doc/', include('django.contrib.admindocs.urls')), - - # Uncomment the next line to enable the admin: - #url(r'^admin/', include('admin.site.urls')), -) From 895365e0e4676c818137cb3cbd0fd0d57b0c347c Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Sat, 18 Jul 2015 22:33:33 +0200 Subject: [PATCH 06/20] Remove CherryPy dependency to simplify --- django_pyvows/context.py | 78 +++++----------------- django_pyvows/http_helpers.py | 107 +++++------------------------- django_pyvows/server.py | 59 ---------------- django_pyvows/settings_manager.py | 44 ------------ vows/context_vows.py | 86 ++---------------------- vows/model_vows.py | 12 ++-- vows/server_vows.py | 11 ++- vows/settings_vows.py | 56 +++------------- vows/template_vows.py | 8 +-- vows/url_vows.py | 8 +-- vows/view_vows.py | 3 +- 11 files changed, 66 insertions(+), 406 deletions(-) delete mode 100644 django_pyvows/server.py delete mode 100644 django_pyvows/settings_manager.py diff --git a/django_pyvows/context.py b/django_pyvows/context.py index cf6f96f..7638425 100644 --- a/django_pyvows/context.py +++ b/django_pyvows/context.py @@ -9,38 +9,40 @@ # Copyright (c) 2011 Rafael Caricio rafael@caricio.com import os -import re +import django from pyvows import Vows from django.http import HttpRequest from django_pyvows import http_helpers from django_pyvows.assertions import Url, Model, Template -from django_pyvows.server import DjangoServer -from django_pyvows.settings_manager import settings_tracker -DEFAULT_PORT = 3331 -DEFAULT_HOST = '127.0.0.1' class DjangoContext(Vows.Context): @classmethod def start_environment(cls, settings_path): if not settings_path: - raise RuntimeError('The settings_path argument is required.') - os.environ['DJANGO_SETTINGS_MODULE'] = settings_path - settings_tracker.install() + raise ValueError('The settings_path argument is required.') + + os.environ.update({'DJANGO_SETTINGS_MODULE': settings_path}) + django.setup() + + from django.test.utils import setup_test_environment + setup_test_environment() def __init__(self, parent): super(DjangoContext, self).__init__(parent) self.ignore('get_settings', 'template', 'request', 'model', 'url', 'find_in_parent', - 'start_environment', 'port', 'host', 'get_url', 'get', 'post') + 'start_environment', 'settings', 'modify_settings', 'get_url', 'get', 'post') - def setup(self): - DjangoContext.start_environment(self.get_settings()) + def settings(self, **kwargs): + from django.test.utils import override_settings + return override_settings(**kwargs) - def get_settings(self): - return os.environ.get('DJANGO_SETTINGS_MODULE', 'settings') + def modify_settings(self, **kwargs): + from django.test.utils import modify_settings + return modify_settings(**kwargs) def url(self, path): return Url(self, path) @@ -55,55 +57,11 @@ class DjangoContext(Vows.Context): return Model(self, model_class) def get(self, path): - return http_helpers.get(self.get_url(path)) + return http_helpers.get(path) def post(self, path, params): - return http_helpers.post(self.get_url(path), params) - - def find_in_parent(self, attr_name): - ctx = self.parent - while ctx: - if hasattr(ctx, attr_name): - return getattr(ctx, attr_name) - ctx = ctx.parent - raise ValueError('Host could not be found in the context or any of its parents') - - def get_url(self, path): - try: - return self.find_in_parent('get_url')(path) - except ValueError: - return path - + return http_helpers.post(path, params) class DjangoHTTPContext(DjangoContext): - - def start_server(self, host=None, port=None, settings={}, threads=1): - if not port: port = DEFAULT_PORT - if not host: host = DEFAULT_HOST - - self.address = (host, port) - self.server = DjangoServer(host, port) - self.server.start(settings,threads) - - def __init__(self, parent): - super(DjangoHTTPContext, self).__init__(parent) - self.ignore('start_server', 'settings') - - @property - def host(self): - if hasattr(self, 'address'): - return self.address[0] - return self.find_in_parent('host') - - @property - def port(self): - if hasattr(self, 'address'): - return self.address[1] - return self.find_in_parent('port') - - def get_url(self, path): - if re.match('^https?:\/\/', path): - return path - return 'http://%s:%d%s' % (self.host, self.port, path) - + pass diff --git a/django_pyvows/http_helpers.py b/django_pyvows/http_helpers.py index e37ecff..d724d4e 100644 --- a/django_pyvows/http_helpers.py +++ b/django_pyvows/http_helpers.py @@ -1,101 +1,28 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -import httplib2 -import mimetypes -import mimetools -import itertools +class RetrocompatibleResponse(object): + def __init__(self, response): + self.response = response -class MultiPartForm(object): - """Accumulate the data to be used when posting a form.""" + @property + def status(self): + return self.response.status_code - def __init__(self): - self.form_fields = [] - self.files = [] - self.boundary = mimetools.choose_boundary() - return - - def get_content_type(self): - return 'multipart/form-data; boundary=%s' % self.boundary - - def add_field(self, name, value): - """Add a simple field to the form data.""" - self.form_fields.append((name, value)) - return - - def add_file(self, fieldname, filename, fileHandle, mimetype=None): - """Add a file to be uploaded.""" - body = fileHandle.read() - if mimetype is None: - mimetype = mimetypes.guess_type(filename)[0] or 'application/octet-stream' - self.files.append((fieldname, filename, mimetype, body)) - return - - def __str__(self): - """Return a string representing the form data, including attached files.""" - # Build a list of lists, each containing "lines" of the - # request. Each part is separated by a boundary string. - # Once the list is built, return a string where each - # line is separated by '\r\n'. - parts = [] - part_boundary = '--' + self.boundary - - # Add the form fields - parts.extend( - [ part_boundary, - 'Content-Disposition: form-data; name="%s"' % name, - '', - value, - ] - for name, value in self.form_fields - ) - - # Add the files to upload - parts.extend( - [ part_boundary, - 'Content-Disposition: file; name="%s"; filename="%s"' % \ - (field_name, filename), - 'Content-Type: %s' % content_type, - '', - body, - ] - for field_name, filename, content_type, body in self.files - ) - - # Flatten the list and add closing boundary marker, - # then return CR+LF separated data - flattened = list(itertools.chain(*parts)) - flattened.append('--' + self.boundary + '--') - flattened.append('') - return '\r\n'.join(flattened) + def __iter__(self): + yield self + yield self.response.content def get(url): - h = httplib2.Http() - resp, content = h.request(url) - return resp, content + from django.test.client import Client + client = Client() + resp = client.get(url) + return RetrocompatibleResponse(resp), resp.content + def post(url, data): - h = httplib2.Http() - form = MultiPartForm() - - for field, value in data.iteritems(): - if hasattr(value, "read"): - form.add_file(field, value.name, value) - else: - form.add_field(field, str(value)) - - body = str(form) - headers = { - 'Content-type': form.get_content_type(), - 'Content-length': str(len(body)) - } - - try: - response = h.request(url, 'POST', headers=headers, body=body) - except httplib2.HttpLib2Error, err: - response = err - - return response - + from django.test.client import Client + client = Client() + return RetrocompatibleResponse(client.post(url, data)) diff --git a/django_pyvows/server.py b/django_pyvows/server.py deleted file mode 100644 index 53996d9..0000000 --- a/django_pyvows/server.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# django-pyvows extensions -# https://github.com/rafaelcaricio/django-pyvows - -# Licensed under the MIT license: -# http://www.opensource.org/licenses/mit-license -# Copyright (c) 2011 Rafael Caricio rafael@caricio.com - -from threading import Thread, current_thread, local -from time import sleep - -from cherrypy import wsgiserver - -from django.core.handlers.wsgi import WSGIHandler - - -def run_app(host, port, thread_count): - server = wsgiserver.CherryPyWSGIServer( - (host, port), - WSGIHandler(), - server_name='tornado-pyvows', - numthreads = thread_count - ) - - my_thread = current_thread() - my_thread.server = server - - try: - server.start() - except KeyboardInterrupt: - server.stop() - -class DjangoServer(object): - - def __init__(self, host, port): - self.host = host - self.port = port - - def start(self, settings, thread_count=1): - self.thr = Thread(target= run_app, args=(self.host, self.port, thread_count)) - self.thr.daemon = True - self.thr.settings = {} - for k, v in settings.iteritems(): - self.thr.settings[k] = v - - self.thr.start() - - while not len(self.thr.server.requests._threads): - sleep(0.1) - - for _thread in self.thr.server.requests._threads: - _thread.settings = {} - for k, v in settings.iteritems(): - _thread.settings[k] = v - - - diff --git a/django_pyvows/settings_manager.py b/django_pyvows/settings_manager.py deleted file mode 100644 index e9122bb..0000000 --- a/django_pyvows/settings_manager.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# django-pyvows extensions -# https://github.com/rafaelcaricio/django-pyvows - -# Licensed under the MIT license: -# http://www.opensource.org/licenses/mit-license -# Copyright (c) 2011 Rafael Caricio rafael@caricio.com - -from threading import current_thread - -class SettingsTracker(object): - - def install(self): - actual_import = __builtins__['__import__'] - if actual_import != self._import: - self.real_import = actual_import - __builtins__['__import__'] = self._import - - def _import(self, name, globals=None, locals=None, fromlist=[], level=-1): - result = apply(self.real_import, (name, globals, locals, fromlist, level)) - fromlist = (fromlist or []) - if name == 'django.conf' and 'settings' in fromlist: - if type(result.settings) != VowsSettings: - result.settings = VowsSettings(result.settings) - elif name == 'django' and 'conf' in fromlist: - if type(result.conf.settings) != VowsSettings: - result.conf.settings = VowsSettings(result.conf.settings) - return result - -class VowsSettings(object): - - def __init__(self, original_settings): - self.original_settings = original_settings - - def __getattr__(self, attr_name): - thread = current_thread() - if hasattr(thread, 'settings'): - if attr_name in thread.settings: - return thread.settings[attr_name] - return getattr(self.original_settings, attr_name) - -settings_tracker = SettingsTracker() diff --git a/vows/context_vows.py b/vows/context_vows.py index 9bd596d..68f915a 100644 --- a/vows/context_vows.py +++ b/vows/context_vows.py @@ -9,12 +9,15 @@ # Copyright (c) 2011 Rafael Caricio rafael@caricio.com from pyvows import Vows, expect +from pyvows.decorators import capture_error + +from django_pyvows.context import DjangoContext -from django_pyvows.context import DjangoContext, DjangoHTTPContext @Vows.batch class ContextTest(Vows.Context): + @capture_error def topic(self): return DjangoContext.start_environment(None) @@ -22,7 +25,7 @@ class ContextTest(Vows.Context): expect(topic).to_be_an_error() def should_be_runtime_error(self, topic): - expect(topic).to_be_an_error_like(RuntimeError) + expect(topic).to_be_an_error_like(ValueError) def should_have_nice_error_message(self, topic): expect(topic).to_have_an_error_message_of('The settings_path argument is required.') @@ -36,82 +39,3 @@ class ContextTest(Vows.Context): def should_return_the_same_path(self, topic): expect(topic).to_equal('/') - - class TheHost(DjangoHTTPContext): - - def topic(self): - return self.host - - def should_return_an_error(self, topic): - expect(topic).to_be_an_error_like(ValueError) - - class ThePort(DjangoHTTPContext): - - def topic(self): - return self.port - - def should_return_an_error(self, topic): - expect(topic).to_be_an_error_like(ValueError) - - class WithinAServer(DjangoHTTPContext): - - def setup(self): - self.start_server(port=8085) - - def should_default_to_one_thread(self,topic): - expect(self.server.thr.server._get_numthreads()).to_equal(1) - - class WithinDjangoHTTPContextTheGetUrlMethod(DjangoHTTPContext): - - def topic(self): - return self.get_url('http://127.0.0.1:8085/complete_url/') - - def when_passed_a_complete_url_should_return_the_url_without_modification(self, topic): - expect(topic).to_equal('http://127.0.0.1:8085/complete_url/') - - class InADjangoHTTPContext(DjangoHTTPContext): - - def topic(self): - return self.get_url('/') - - def the_get_url_should_return_a_well_formed_url(self, topic): - expect(topic).to_equal('http://127.0.0.1:8085/') - - class ANoDjangoContext(Vows.Context): - - class TheHost(DjangoHTTPContext): - - def topic(self): - return self.host - - def should_be_equal_to_the_host_in_out_context(self, topic): - expect(topic).to_equal('127.0.0.1') - - class ThePort(DjangoHTTPContext): - - def topic(self): - return self.port - - def should_be_equal_to_the_port_in_out_context(self, topic): - expect(topic).to_equal(8085) - - class AnDjangoContext(DjangoContext): - - def topic(self): - return self.get_url('/') - - def the_get_url_method_should_return_a_well_formed_url(self, topic): - expect(topic).to_equal('http://127.0.0.1:8085/') - - class WithinAMultiThreadedServer(DjangoHTTPContext): - - def setup(self): - self.start_server(threads=5) - - def topic(self): - return self.server - - def should_allow_user_to_specify_number_of_threads(self,topic): - expect(topic.thr.server._get_numthreads()).to_equal(5) - - diff --git a/vows/model_vows.py b/vows/model_vows.py index 8f45377..335dceb 100644 --- a/vows/model_vows.py +++ b/vows/model_vows.py @@ -8,15 +8,15 @@ # http://www.opensource.org/licenses/mit-license # Copyright (c) 2011 Rafael Caricio rafael@caricio.com -from pyvows import Vows, expect +from pyvows import expect from django_pyvows.context import DjangoContext -DjangoContext.start_environment("sandbox.settings") +DjangoContext.start_environment("sandbox.sandbox.settings") + +from django.db import models # NOQA +from sandbox.main.models import StringModel # NOQA -from django.db import models -from sandbox.main.models import StringModel -@Vows.batch class ModelVows(DjangoContext): class MainModel(DjangoContext): @@ -54,5 +54,3 @@ class ModelVows(DjangoContext): def should_have_a_name_field_as_charfield_and_max_length_100(self, topic): expect(topic).to_have_field('name', models.CharField, max_length=100) - - diff --git a/vows/server_vows.py b/vows/server_vows.py index 8b3ddbf..8c998e0 100644 --- a/vows/server_vows.py +++ b/vows/server_vows.py @@ -10,15 +10,14 @@ from os.path import abspath, join, dirname -import httplib2 - from django_pyvows.context import DjangoContext, DjangoHTTPContext -from django_pyvows.assertions import * +from django_pyvows.assertions import * # NOQA TEST_FILE_PATH = abspath(join(dirname(__file__), 'fixtures/the_file.txt')) DjangoContext.start_environment("sandbox.settings") + @Vows.batch class HttpContextVows(DjangoHTTPContext): @@ -67,7 +66,7 @@ class HttpContextVows(DjangoHTTPContext): class PostFile(DjangoContext): def topic(self): - return self.post('/post_file/', {'the_file': open(TEST_FILE_PATH) }) + return self.post('/post_file/', {'the_file': open(TEST_FILE_PATH)}) def should_be_posted_to_the_server(self, (topic, content)): expect(content).to_equal("the contents") @@ -75,9 +74,7 @@ class HttpContextVows(DjangoHTTPContext): class PostToNotFound(DjangoContext): def topic(self): - return self.post('/post_/', {'the_file': open(TEST_FILE_PATH) }) + return self.post('/post_/', {'the_file': open(TEST_FILE_PATH)}) def should_be_404(self, (topic, content)): expect(topic.status).to_equal(404) - - diff --git a/vows/settings_vows.py b/vows/settings_vows.py index d40eb36..cf851ff 100644 --- a/vows/settings_vows.py +++ b/vows/settings_vows.py @@ -10,54 +10,19 @@ from pyvows import Vows, expect -from django_pyvows.context import DjangoContext, DjangoHTTPContext -from django_pyvows.settings_manager import settings_tracker, VowsSettings +from django_pyvows.context import DjangoContext + +DjangoContext.start_environment("sandbox.sandbox.settings") -DjangoContext.start_environment("sandbox.settings") @Vows.batch class SettingsVows(DjangoContext): - class WhenIUseTheSettingsTracker(DjangoContext): + class CannotSayHelloWithoutName(DjangoContext): def topic(self): - settings_tracker.install() - - class WhenImportFromDjangoConf(DjangoContext): - - def topic(self): - from django.conf import settings - return settings - - def should_be_the_vows_settings(self, topic): - expect(topic).to_be_instance_of(VowsSettings) - - class WhenIImportOnlyConfAndThenUseSettings(DjangoContext): - - def topic(self): - from django import conf - return conf.settings - - def should_be_the_vows_settings(self, topic): - expect(topic).to_be_instance_of(VowsSettings) - - class WhenIImportTheCompletePathAndThenUseSettings(DjangoContext): - - def topic(self): - import django.conf - return django.conf.settings - - def should_be_the_vows_settings(self, topic): - expect(topic).to_be_instance_of(VowsSettings) - - class CannotSayHelloWithoutName(DjangoHTTPContext): - - def topic(self): - self.start_server(port=9000, settings={ - 'SAY_HELLO_WITHOUT_NAME': False - }) - - return self.get('/say/') + with self.settings(SAY_HELLO_WITHOUT_NAME=False): + return self.get('/say/') def should_be_ok(self, (topic, content)): expect(topic.status).to_equal(200) @@ -65,17 +30,14 @@ class SettingsVows(DjangoContext): def should_ask_for_my_name(self, (topic, content)): expect(content).to_equal("What's your name?") - class SayHelloWithoutName(DjangoHTTPContext): + class SayHelloWithoutName(DjangoContext): def topic(self): - self.start_server(port=9001, settings={ - 'SAY_HELLO_WITHOUT_NAME': True - }) - return self.get('/say/') + with self.settings(SAY_HELLO_WITHOUT_NAME=True): + return self.get('/say/') def should_be_ok(self, (topic, content)): expect(topic.status).to_equal(200) def should_(self, (topic, content)): expect(content).to_equal("Hello, guess!") - diff --git a/vows/template_vows.py b/vows/template_vows.py index cd9470c..961854c 100644 --- a/vows/template_vows.py +++ b/vows/template_vows.py @@ -11,14 +11,12 @@ from pyvows import Vows, expect from django_pyvows.context import DjangoContext -from django_pyvows.assertions import * +from django_pyvows.assertions import * # NOQA + @Vows.batch class TemplateVows(DjangoContext): - def get_settings(self): - return 'sandbox.settings' - class IndexTemplate(DjangoContext): def topic(self): @@ -42,5 +40,3 @@ class TemplateVows(DjangoContext): def should_have_paragraph_with_text(self, topic): expect(topic).to_be_like('some text') - - diff --git a/vows/url_vows.py b/vows/url_vows.py index fdcd053..50134a5 100644 --- a/vows/url_vows.py +++ b/vows/url_vows.py @@ -8,16 +8,16 @@ # http://www.opensource.org/licenses/mit-license # Copyright (c) 2011 Rafael Caricio rafael@caricio.com -from pyvows import Vows, expect +from pyvows import expect from django_pyvows.context import DjangoContext -from django_pyvows.assertions import * +from django_pyvows.assertions import * # NOQA DjangoContext.start_environment("sandbox.settings") -from sandbox.main.views import home +from sandbox.main.views import home # NOQA + -@Vows.batch class UrlVows(DjangoContext): class Home(DjangoContext): diff --git a/vows/view_vows.py b/vows/view_vows.py index 932e888..757fe6d 100644 --- a/vows/view_vows.py +++ b/vows/view_vows.py @@ -13,7 +13,8 @@ from django_pyvows.context import DjangoContext DjangoContext.start_environment("sandbox.settings") -from sandbox.main.views import home +from sandbox.main.views import home # NOQA + @Vows.batch class ViewVows(DjangoContext): From 27492fd8c6eb58b4554a68822fe4e63029329d95 Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Sat, 18 Jul 2015 22:36:24 +0200 Subject: [PATCH 07/20] Remove useless code --- django_pyvows/context.py | 4 +- vows/context_vows.py | 10 ----- vows/server_vows.py | 80 ---------------------------------------- 3 files changed, 2 insertions(+), 92 deletions(-) delete mode 100644 vows/server_vows.py diff --git a/django_pyvows/context.py b/django_pyvows/context.py index 7638425..3cc1502 100644 --- a/django_pyvows/context.py +++ b/django_pyvows/context.py @@ -33,8 +33,8 @@ class DjangoContext(Vows.Context): def __init__(self, parent): super(DjangoContext, self).__init__(parent) - self.ignore('get_settings', 'template', 'request', 'model', 'url', 'find_in_parent', - 'start_environment', 'settings', 'modify_settings', 'get_url', 'get', 'post') + self.ignore('template', 'request', 'model', 'url', 'find_in_parent', + 'start_environment', 'settings', 'modify_settings', 'get', 'post') def settings(self, **kwargs): from django.test.utils import override_settings diff --git a/vows/context_vows.py b/vows/context_vows.py index 68f915a..aa32940 100644 --- a/vows/context_vows.py +++ b/vows/context_vows.py @@ -29,13 +29,3 @@ class ContextTest(Vows.Context): def should_have_nice_error_message(self, topic): expect(topic).to_have_an_error_message_of('The settings_path argument is required.') - - class WithinANoDjangoContext(Vows.Context): - - class TheGetUrlMethod(DjangoContext): - - def topic(self): - return self.get_url('/') - - def should_return_the_same_path(self, topic): - expect(topic).to_equal('/') diff --git a/vows/server_vows.py b/vows/server_vows.py deleted file mode 100644 index 8c998e0..0000000 --- a/vows/server_vows.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# django-pyvows extensions -# https://github.com/rafaelcaricio/django-pyvows - -# Licensed under the MIT license: -# http://www.opensource.org/licenses/mit-license -# Copyright (c) 2011 Rafael Caricio rafael@caricio.com - -from os.path import abspath, join, dirname - -from django_pyvows.context import DjangoContext, DjangoHTTPContext -from django_pyvows.assertions import * # NOQA - -TEST_FILE_PATH = abspath(join(dirname(__file__), 'fixtures/the_file.txt')) - -DjangoContext.start_environment("sandbox.settings") - - -@Vows.batch -class HttpContextVows(DjangoHTTPContext): - - def topic(self): - self.start_server() - return self.get("/") - - def the_return_code_should_be_200(self, (topic, content)): - expect(topic.status).to_equal(200) - - def should_return_the_success_response(self, (topic, content)): - expect(content).to_equal("hello world") - - class Methods(DjangoContext): - - def topic(self): - return self.get('/?name=rafael') - - def should_be_possible_to_pass_get_parameters(self, (topic, content)): - expect(topic.status).to_equal(200) - - class AskMyName(DjangoContext): - - def topic(self): - return self.get('/say/') - - def should_ask_for_my_name(self, (topic, content)): - expect(content).to_equal('What\'s your name?') - - class SayHelloToMe(DjangoContext): - - def topic(self): - return self.get('/say/?name=Rafael') - - def should_say_hello_to_me(self, (topic, content)): - expect(content).to_equal('Hello, Rafael!') - - class PostIt(DjangoContext): - - def topic(self): - return self.post('/post_it/', {'value': 'posted!'}) - - def should_be_posted(self, (topic, content)): - expect(content).to_equal('posted!') - - class PostFile(DjangoContext): - - def topic(self): - return self.post('/post_file/', {'the_file': open(TEST_FILE_PATH)}) - - def should_be_posted_to_the_server(self, (topic, content)): - expect(content).to_equal("the contents") - - class PostToNotFound(DjangoContext): - - def topic(self): - return self.post('/post_/', {'the_file': open(TEST_FILE_PATH)}) - - def should_be_404(self, (topic, content)): - expect(topic.status).to_equal(404) From 8adb51c7565b91644c1d8f68e788121f96afe3cf Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Sat, 18 Jul 2015 22:39:47 +0200 Subject: [PATCH 08/20] Remove useless assertions --- django_pyvows/assertions/__init__.py | 5 ++- django_pyvows/assertions/urls.py | 54 ---------------------------- django_pyvows/context.py | 9 +---- vows/url_vows.py | 32 ----------------- vows/view_vows.py | 31 ---------------- 5 files changed, 3 insertions(+), 128 deletions(-) delete mode 100644 django_pyvows/assertions/urls.py delete mode 100644 vows/url_vows.py delete mode 100644 vows/view_vows.py diff --git a/django_pyvows/assertions/__init__.py b/django_pyvows/assertions/__init__.py index 27216e8..5e3a4a6 100644 --- a/django_pyvows/assertions/__init__.py +++ b/django_pyvows/assertions/__init__.py @@ -8,6 +8,5 @@ # http://www.opensource.org/licenses/mit-license # Copyright (c) 2011 Rafael Caricio rafael@caricio.com -from urls import * -from models import * -from templates import * +from models import * # NOQA +from templates import * # NOQA diff --git a/django_pyvows/assertions/urls.py b/django_pyvows/assertions/urls.py deleted file mode 100644 index 2542a85..0000000 --- a/django_pyvows/assertions/urls.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# django-pyvows extensions -# https://github.com/rafaelcaricio/django-pyvows - -# Licensed under the MIT license: -# http://www.opensource.org/licenses/mit-license -# Copyright (c) 2011 Rafael Caricio rafael@caricio.com - -from pyvows import Vows, expect -from django.http import HttpResponse - - -class Url(object): - def __init__(self, context, path): - self.context = context - self.path = path - -@Vows.assertion -def to_be_mapped(topic): - verify_url_is_mapped_to_method(topic) - -@Vows.assertion -def to_match_view(topic, view): - verify_url_is_mapped_to_method(topic, view, True) - -@Vows.assertion -def to_be_http_response(topic): - expect(topic).to_be_instance_of(HttpResponse) - -def verify_url_is_mapped_to_method(topic, method=None, assert_method_as_well=False): - assert isinstance(topic, Url), "Only django_pyvows.Url items can be verified for mapping" - - from django.conf import settings - project_urls = settings.ROOT_URLCONF - - urlpatterns = __import__(project_urls).urls.urlpatterns - - found = False - matches_method = False - for urlpattern in urlpatterns: - regex = urlpattern.regex - pattern = regex.pattern - actual_method = urlpattern.callback - - if topic.path == pattern: - found = True - if method == actual_method: - matches_method = True - - assert found, "Expected url(%s) to be mapped but it wasn't" - if assert_method_as_well: - assert matches_method, "Expected url(%s) to match method(%s), but it didn't" diff --git a/django_pyvows/context.py b/django_pyvows/context.py index 3cc1502..e0ea4a9 100644 --- a/django_pyvows/context.py +++ b/django_pyvows/context.py @@ -12,10 +12,9 @@ import os import django from pyvows import Vows -from django.http import HttpRequest from django_pyvows import http_helpers -from django_pyvows.assertions import Url, Model, Template +from django_pyvows.assertions import Model, Template class DjangoContext(Vows.Context): @@ -44,15 +43,9 @@ class DjangoContext(Vows.Context): from django.test.utils import modify_settings return modify_settings(**kwargs) - def url(self, path): - return Url(self, path) - def template(self, template_name, context): return Template(template_name, context) - def request(self, **kw): - return HttpRequest(**kw) - def model(self, model_class): return Model(self, model_class) diff --git a/vows/url_vows.py b/vows/url_vows.py deleted file mode 100644 index 50134a5..0000000 --- a/vows/url_vows.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# django-pyvows extensions -# https://github.com/rafaelcaricio/django-pyvows - -# Licensed under the MIT license: -# http://www.opensource.org/licenses/mit-license -# Copyright (c) 2011 Rafael Caricio rafael@caricio.com - -from pyvows import expect - -from django_pyvows.context import DjangoContext -from django_pyvows.assertions import * # NOQA - -DjangoContext.start_environment("sandbox.settings") - -from sandbox.main.views import home # NOQA - - -class UrlVows(DjangoContext): - - class Home(DjangoContext): - - def topic(self): - return self.url('^$') - - def should_have_home_url_mapped(self, topic): - expect(topic).to_be_mapped() - - def should_have_url_mapped_to_home_view(self, topic): - expect(topic).to_match_view(home) diff --git a/vows/view_vows.py b/vows/view_vows.py deleted file mode 100644 index 757fe6d..0000000 --- a/vows/view_vows.py +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# django-pyvows extensions -# https://github.com/rafaelcaricio/django-pyvows - -# Licensed under the MIT license: -# http://www.opensource.org/licenses/mit-license -# Copyright (c) 2011 Rafael Caricio rafael@caricio.com - -from pyvows import Vows, expect -from django_pyvows.context import DjangoContext - -DjangoContext.start_environment("sandbox.settings") - -from sandbox.main.views import home # NOQA - - -@Vows.batch -class ViewVows(DjangoContext): - - class Home(DjangoContext): - - def topic(self): - return home(self.request()) - - def should_be_instance_of_http_response(self, topic): - expect(topic).to_be_http_response() - - def should_be_hello_world(self, topic): - expect(topic).to_have_contents_of('Hello World') From 4e450da0417e142b62131a98df9d5fcfad61e76d Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Sat, 18 Jul 2015 22:47:08 +0200 Subject: [PATCH 09/20] Remove old assertions --- django_pyvows/assertions/__init__.py | 12 --- django_pyvows/assertions/models.py | 119 -------------------------- django_pyvows/assertions/templates.py | 57 ------------ vows/model_vows.py | 56 ------------ vows/template_vows.py | 42 --------- 5 files changed, 286 deletions(-) delete mode 100644 django_pyvows/assertions/__init__.py delete mode 100644 django_pyvows/assertions/models.py delete mode 100644 django_pyvows/assertions/templates.py delete mode 100644 vows/model_vows.py delete mode 100644 vows/template_vows.py diff --git a/django_pyvows/assertions/__init__.py b/django_pyvows/assertions/__init__.py deleted file mode 100644 index 5e3a4a6..0000000 --- a/django_pyvows/assertions/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# django-pyvows extensions -# https://github.com/rafaelcaricio/django-pyvows - -# Licensed under the MIT license: -# http://www.opensource.org/licenses/mit-license -# Copyright (c) 2011 Rafael Caricio rafael@caricio.com - -from models import * # NOQA -from templates import * # NOQA diff --git a/django_pyvows/assertions/models.py b/django_pyvows/assertions/models.py deleted file mode 100644 index c97f53d..0000000 --- a/django_pyvows/assertions/models.py +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# django-pyvows extensions -# https://github.com/rafaelcaricio/django-pyvows - -# Licensed under the MIT license: -# http://www.opensource.org/licenses/mit-license -# Copyright (c) 2011 Rafael Caricio rafael@caricio.com - -from pyvows import Vows - -class Model(object): - def __init__(self, context, model): - from django.db import models - - self.context = context - if isinstance(model, models.Model): - model = model.__class__ - self.model = model - - def __call__(self, *args, **kwargs): - return self.model(*args, **kwargs) - - @property - def admin(self): - from django.contrib import admin - if self.model not in admin.site._registry: - raise admin.sites.NotRegistered('The model %s is not registered' % self.model.__name__) - return admin.site._registry[self.model] - -@Vows.assertion -def to_be_in_admin(topic): - from django.contrib import admin - try: - assert topic.admin - except admin.sites.NotRegistered: - assert False, "The model %s isn't registered in admin." % topic.model.__name__ - -@Vows.assertion -def to_have_field(topic, field_name, field_class=None, **kwargs): - from django.db import models - - if isinstance(topic, Model): - topic = topic.model - - if isinstance(topic, models.Model): - topic = topic.__class__ - - if not field_class: - field_class = models.Field - - field = topic._meta.get_field(field_name) - assert isinstance(field, field_class), "The '%s.%s' is not an instance of '%s'" % (topic.__name__, field_name, field_class.__name__) - if kwargs: - for attr, value in kwargs.items(): - field_value = getattr(field, attr) - assert field_value == value, "The field option '%s' should be equal to '%s', but it's equal to '%s'" % (attr, value, field_value) - -@Vows.assertion -def to_be_cruddable(topic, defaults={}): - import django.db.models.fields as fields - instance = __create_or_update_instance(topic, None, defaults) - - assert instance, "An instance could not be created for model %s" % topic.model.__name__ - - retrieved = topic.model.objects.get(id=instance.id) - assert retrieved.id == instance.id, "An instance could not be retrieved for model %s with id %d" % (topic.model.__name__, instance.id) - - for key, value in defaults.iteritems(): - assert value == getattr(retrieved, key), "The default specified value of '%s' should have been set in the '%s' property of the instance but it was not" % (value, key) - - updated = __create_or_update_instance(topic, retrieved, defaults) - - for field, value in topic.model._meta._field_cache: - if field.__class__ == fields.AutoField: - continue - - if field.name in defaults: - continue - - assert getattr(updated, field.name) != getattr(instance, field.name), "The instance should have been updated but the field %s is the same in both the original instance and the updated one (%s)." % (field.name, getattr(updated, field.name)) - - instance.delete() - object_count = topic.model.objects.count() - assert object_count == 0, "Object should have been deleted, but it wasn't (count: %d)" % object_count - -def __create_or_update_instance(topic, instance, defaults): - import django.db.models.fields as fields - arguments = {} - for field, value in topic.model._meta._field_cache: - if field.__class__ == fields.AutoField: - continue - - if field.name in defaults: - arguments[field.name] = defaults[field.name] - continue - - if field.__class__ == fields.CharField: - __add_char_value_for(field, instance, arguments) - - if instance: - for key, value in arguments.iteritems(): - setattr(instance, key, value) - - instance.save() - return instance - - return topic.model.objects.create(**arguments) - -def __add_char_value_for(field, instance, arguments): - value = "monty python" - if instance: - value = getattr(instance, field.name) + '2' - if field.max_length: - if instance: - value = value[:len(value) - 2] + '2' - value = (value * field.max_length)[:field.max_length] - arguments[field.name] = value diff --git a/django_pyvows/assertions/templates.py b/django_pyvows/assertions/templates.py deleted file mode 100644 index 6106236..0000000 --- a/django_pyvows/assertions/templates.py +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# django-pyvows extensions -# https://github.com/rafaelcaricio/django-pyvows - -# Licensed under the MIT license: -# http://www.opensource.org/licenses/mit-license -# Copyright (c) 2011 Rafael Caricio rafael@caricio.com - -from lxml.etree import fromstring -from lxml.cssselect import CSSSelector - -from pyvows import Vows, expect -from django.template.loader import render_to_string - - -class Template(object): - def __init__(self, template_name, context): - self.template_name = template_name - self.context = context - self.doc = None - - def load(self): - if self.doc is None: - self.doc = fromstring(render_to_string(self.template_name, self.context)) - return self.doc - - def select_element(self, selector): - sel = CSSSelector(selector) - return sel(self.load()) - - def _to_contain(self, selector): - return len(self.select_element(selector)) > 0 - - def get_text(self, selector): - return "".join((c.text for c in self.select_element(selector))) - - def __unicode__(self): - return self.template_name - -@Vows.assertion -def to_have_contents_of(topic, expected): - expect(topic.content).to_be_like(expected) - -@Vows.assertion -def to_contain(topic, selector): - assert isinstance(topic, Template), "Only django_pyvows.Template items can be verified for mapping" - assert topic._to_contain(selector), "Expected template(%s) to have an element(%s), but it don't have" % \ - (unicode(topic), selector) - -@Vows.assertion -def not_to_contain(topic, selector): - assert isinstance(topic, Template), "Only django_pyvows.Template items can be verified for mapping" - assert not topic._to_contain(selector), "Expected template(%s) to not have an element(%s), but it have" % \ - (unicode(topic), selector) - diff --git a/vows/model_vows.py b/vows/model_vows.py deleted file mode 100644 index 335dceb..0000000 --- a/vows/model_vows.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# django-pyvows extensions -# https://github.com/rafaelcaricio/django-pyvows - -# Licensed under the MIT license: -# http://www.opensource.org/licenses/mit-license -# Copyright (c) 2011 Rafael Caricio rafael@caricio.com - -from pyvows import expect -from django_pyvows.context import DjangoContext - -DjangoContext.start_environment("sandbox.sandbox.settings") - -from django.db import models # NOQA -from sandbox.main.models import StringModel # NOQA - - -class ModelVows(DjangoContext): - - class MainModel(DjangoContext): - - def topic(self): - return self.model(StringModel) - - def should_be_cruddable_when_model_only_has_a_string(self, topic): - expect(topic).to_be_cruddable() - - def should_be_cruddable_when_string_passed(self, topic): - expect(topic).to_be_cruddable({ - 'name': 'something' - }) - - def should_be_possible_to_use_the_assertion_in_model_instance(self, topic): - expect(topic).to_have_field('name') - - def should_have_a_method_to_call(self, topic): - expect(hasattr(topic, '__call__')).to_be_true() - - class WhenICreateANewInstance(DjangoContext): - - def topic(self, model): - return model() - - def should_be_an_instance_of_django_model_class(self, topic): - expect(isinstance(topic, models.Model)).to_be_true() - - def should_have_a_name_field(self, topic): - expect(topic).to_have_field('name') - - def should_have_a_name_field_as_charfield(self, topic): - expect(topic).to_have_field('name', models.CharField) - - def should_have_a_name_field_as_charfield_and_max_length_100(self, topic): - expect(topic).to_have_field('name', models.CharField, max_length=100) diff --git a/vows/template_vows.py b/vows/template_vows.py deleted file mode 100644 index 961854c..0000000 --- a/vows/template_vows.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# django-pyvows extensions -# https://github.com/rafaelcaricio/django-pyvows - -# Licensed under the MIT license: -# http://www.opensource.org/licenses/mit-license -# Copyright (c) 2011 Rafael Caricio rafael@caricio.com - -from pyvows import Vows, expect - -from django_pyvows.context import DjangoContext -from django_pyvows.assertions import * # NOQA - - -@Vows.batch -class TemplateVows(DjangoContext): - - class IndexTemplate(DjangoContext): - - def topic(self): - return self.template('index.html', { - 'some': 'thing' - }) - - def should_have_container_div(self, topic): - expect(topic).to_contain('div.container') - - def should_not_have_a_hello_div(self, topic): - expect(topic).Not.to_contain('div.hello') - - def should_be_index_file(self, topic): - expect(unicode(topic)).to_equal('index.html') - - class Paragraph(DjangoContext): - - def topic(self, template): - return template.get_text('p.my-text') - - def should_have_paragraph_with_text(self, topic): - expect(topic).to_be_like('some text') From c3d05f9714bca44ada9d75512cf9b3cc2a72192c Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Sat, 18 Jul 2015 22:50:03 +0200 Subject: [PATCH 10/20] Fix tests --- django_pyvows/__init__.py | 2 +- django_pyvows/context.py | 11 ----------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/django_pyvows/__init__.py b/django_pyvows/__init__.py index e22d7f2..d3c1c5f 100644 --- a/django_pyvows/__init__.py +++ b/django_pyvows/__init__.py @@ -8,4 +8,4 @@ # http://www.opensource.org/licenses/mit-license # Copyright (c) 2011 Rafael Caricio rafael@caricio.com -from context import DjangoContext, DjangoHTTPContext +from context import DjangoContext # NOQA diff --git a/django_pyvows/context.py b/django_pyvows/context.py index e0ea4a9..d0e028b 100644 --- a/django_pyvows/context.py +++ b/django_pyvows/context.py @@ -14,7 +14,6 @@ import django from pyvows import Vows from django_pyvows import http_helpers -from django_pyvows.assertions import Model, Template class DjangoContext(Vows.Context): @@ -43,18 +42,8 @@ class DjangoContext(Vows.Context): from django.test.utils import modify_settings return modify_settings(**kwargs) - def template(self, template_name, context): - return Template(template_name, context) - - def model(self, model_class): - return Model(self, model_class) - def get(self, path): return http_helpers.get(path) def post(self, path, params): return http_helpers.post(path, params) - - -class DjangoHTTPContext(DjangoContext): - pass From 242bb9df199fb3fcbf193544ea4f29c1b49f5be1 Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Sat, 18 Jul 2015 23:03:45 +0200 Subject: [PATCH 11/20] Does not need to be compatible --- django_pyvows/context.py | 11 +++++------ django_pyvows/http_helpers.py | 22 ++++------------------ vows/settings_vows.py | 18 +++++++++--------- 3 files changed, 18 insertions(+), 33 deletions(-) diff --git a/django_pyvows/context.py b/django_pyvows/context.py index d0e028b..9c3873a 100644 --- a/django_pyvows/context.py +++ b/django_pyvows/context.py @@ -31,8 +31,7 @@ class DjangoContext(Vows.Context): def __init__(self, parent): super(DjangoContext, self).__init__(parent) - self.ignore('template', 'request', 'model', 'url', 'find_in_parent', - 'start_environment', 'settings', 'modify_settings', 'get', 'post') + self.ignore('start_environment', 'settings', 'modify_settings', 'get', 'post') def settings(self, **kwargs): from django.test.utils import override_settings @@ -42,8 +41,8 @@ class DjangoContext(Vows.Context): from django.test.utils import modify_settings return modify_settings(**kwargs) - def get(self, path): - return http_helpers.get(path) + def get(self, *args, **kwargs): + return http_helpers.get(*args, **kwargs) - def post(self, path, params): - return http_helpers.post(path, params) + def post(self, *args, **kwargs): + return http_helpers.post(*args, **kwargs) diff --git a/django_pyvows/http_helpers.py b/django_pyvows/http_helpers.py index d724d4e..0826136 100644 --- a/django_pyvows/http_helpers.py +++ b/django_pyvows/http_helpers.py @@ -2,27 +2,13 @@ # -*- coding: utf-8 -*- -class RetrocompatibleResponse(object): - def __init__(self, response): - self.response = response - - @property - def status(self): - return self.response.status_code - - def __iter__(self): - yield self - yield self.response.content - - -def get(url): +def get(*args, **kwargs): from django.test.client import Client client = Client() - resp = client.get(url) - return RetrocompatibleResponse(resp), resp.content + return client.get(*args, **kwargs) -def post(url, data): +def post(*args, **kwargs): from django.test.client import Client client = Client() - return RetrocompatibleResponse(client.post(url, data)) + return client.post(*args, **kwargs) diff --git a/vows/settings_vows.py b/vows/settings_vows.py index cf851ff..3656c9b 100644 --- a/vows/settings_vows.py +++ b/vows/settings_vows.py @@ -16,7 +16,7 @@ DjangoContext.start_environment("sandbox.sandbox.settings") @Vows.batch -class SettingsVows(DjangoContext): +class SettingsOverridingVows(DjangoContext): class CannotSayHelloWithoutName(DjangoContext): @@ -24,11 +24,11 @@ class SettingsVows(DjangoContext): with self.settings(SAY_HELLO_WITHOUT_NAME=False): return self.get('/say/') - def should_be_ok(self, (topic, content)): - expect(topic.status).to_equal(200) + def should_be_ok(self, topic): + expect(topic.status_code).to_equal(200) - def should_ask_for_my_name(self, (topic, content)): - expect(content).to_equal("What's your name?") + def should_ask_for_my_name(self, topic): + expect(topic.content).to_equal("What's your name?") class SayHelloWithoutName(DjangoContext): @@ -36,8 +36,8 @@ class SettingsVows(DjangoContext): with self.settings(SAY_HELLO_WITHOUT_NAME=True): return self.get('/say/') - def should_be_ok(self, (topic, content)): - expect(topic.status).to_equal(200) + def should_be_ok(self, topic): + expect(topic.status_code).to_equal(200) - def should_(self, (topic, content)): - expect(content).to_equal("Hello, guess!") + def should_(self, topic): + expect(topic.content).to_equal("Hello, guess!") From 7dcbf31c5a773d97f967841d43f468653253db3c Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Sat, 18 Jul 2015 23:25:24 +0200 Subject: [PATCH 12/20] Guarantee override settings works --- django_pyvows/context.py | 6 +--- vows/client_vows.py | 61 ++++++++++++++++++++++++++++++++++++++ vows/sandbox/main/views.py | 2 +- vows/settings_vows.py | 13 ++++++-- 4 files changed, 73 insertions(+), 9 deletions(-) create mode 100644 vows/client_vows.py diff --git a/django_pyvows/context.py b/django_pyvows/context.py index 9c3873a..e377e5c 100644 --- a/django_pyvows/context.py +++ b/django_pyvows/context.py @@ -31,16 +31,12 @@ class DjangoContext(Vows.Context): def __init__(self, parent): super(DjangoContext, self).__init__(parent) - self.ignore('start_environment', 'settings', 'modify_settings', 'get', 'post') + self.ignore('start_environment', 'settings', 'get', 'post') def settings(self, **kwargs): from django.test.utils import override_settings return override_settings(**kwargs) - def modify_settings(self, **kwargs): - from django.test.utils import modify_settings - return modify_settings(**kwargs) - def get(self, *args, **kwargs): return http_helpers.get(*args, **kwargs) diff --git a/vows/client_vows.py b/vows/client_vows.py new file mode 100644 index 0000000..1c9d0b3 --- /dev/null +++ b/vows/client_vows.py @@ -0,0 +1,61 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# django-pyvows extensions +# https://github.com/rafaelcaricio/django-pyvows + +# Licensed under the MIT license: +# http://www.opensource.org/licenses/mit-license +# Copyright (c) 2011 Rafael Caricio rafael@caricio.com +from os.path import abspath, join, dirname + +from pyvows import Vows, expect + +from django_pyvows.context import DjangoContext + + +TEST_FILE_PATH = abspath(join(dirname(__file__), 'fixtures/the_file.txt')) + +DjangoContext.start_environment("sandbox.sandbox.settings") + + +@Vows.batch +class DjangoHTTPTestClientCompatibilityVows(DjangoContext): + + class SimpleGet(DjangoContext): + def topic(self): + return self.get('/say/') + + def should_be_ok(self, topic): + expect(topic.status_code).to_equal(200) + + def should_ask_for_my_name(self, topic): + expect(topic.content).to_equal('What\'s your name?') + + class SimpleGetWithParams(DjangoContext): + def topic(self): + return self.get('/say/?name=Rafael') + + def should_say_hello_to_me(self, topic): + expect(topic.content).to_equal('Hello, Rafael!') + + class SimplePost(DjangoContext): + def topic(self): + return self.post('/post_it/', {'value': 'posted!'}) + + def should_be_posted(self, topic): + expect(topic.content).to_equal('posted!') + + class PostFile(DjangoContext): + def topic(self): + return self.post('/post_file/', {'the_file': open(TEST_FILE_PATH)}) + + def should_be_posted_to_the_server(self, topic): + expect(topic.content).to_equal("the contents") + + class WhenNotFound(DjangoContext): + def topic(self): + return self.post('/post_/', {'the_file': open(TEST_FILE_PATH)}) + + def should_be_404(self, topic): + expect(topic.status_code).to_equal(404) diff --git a/vows/sandbox/main/views.py b/vows/sandbox/main/views.py index cefbb26..e334a90 100644 --- a/vows/sandbox/main/views.py +++ b/vows/sandbox/main/views.py @@ -21,7 +21,7 @@ def say_hello(request): if 'name' in request.GET: name = request.GET['name'] elif SAY_HELLO_WITHOUT_NAME: - name = 'guess' + name = 'guest' else: return HttpResponse("What's your name?") return HttpResponse("Hello, %s!" % name) diff --git a/vows/settings_vows.py b/vows/settings_vows.py index 3656c9b..b1f9f4c 100644 --- a/vows/settings_vows.py +++ b/vows/settings_vows.py @@ -9,6 +9,7 @@ # Copyright (c) 2011 Rafael Caricio rafael@caricio.com from pyvows import Vows, expect +from django.test.utils import override_settings from django_pyvows.context import DjangoContext @@ -19,7 +20,6 @@ DjangoContext.start_environment("sandbox.sandbox.settings") class SettingsOverridingVows(DjangoContext): class CannotSayHelloWithoutName(DjangoContext): - def topic(self): with self.settings(SAY_HELLO_WITHOUT_NAME=False): return self.get('/say/') @@ -31,7 +31,6 @@ class SettingsOverridingVows(DjangoContext): expect(topic.content).to_equal("What's your name?") class SayHelloWithoutName(DjangoContext): - def topic(self): with self.settings(SAY_HELLO_WITHOUT_NAME=True): return self.get('/say/') @@ -40,4 +39,12 @@ class SettingsOverridingVows(DjangoContext): expect(topic.status_code).to_equal(200) def should_(self, topic): - expect(topic.content).to_equal("Hello, guess!") + expect(topic.content).to_equal("Hello, guest!") + + class UseDecoratorToChangeSettings(DjangoContext): + @override_settings(SAY_HELLO_WITHOUT_NAME=True) + def topic(self): + return self.get('/say/') + + def should_say_hello_to_guest(self, topic): + expect(topic.content).to_equal("Hello, guest!") From 89e44803de7cf6d632ccc6ecba698bd3588ef6b6 Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Sun, 19 Jul 2015 00:59:42 +0200 Subject: [PATCH 13/20] Assertions from django.test.SimpleTestCase --- Makefile | 5 +++- django_pyvows/__init__.py | 1 + django_pyvows/assertions.py | 34 +++++++++++++++++++++++++++ vows/assertions_vows.py | 40 ++++++++++++++++++++++++++++++++ vows/sandbox/main/forms.py | 5 ++++ vows/sandbox/main/views.py | 18 ++++++++++++++ vows/sandbox/sandbox/urls.py | 3 ++- vows/sandbox/templates/name.html | 5 ++++ 8 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 django_pyvows/assertions.py create mode 100644 vows/assertions_vows.py create mode 100644 vows/sandbox/main/forms.py create mode 100644 vows/sandbox/templates/name.html diff --git a/Makefile b/Makefile index 68472d1..8041e69 100644 --- a/Makefile +++ b/Makefile @@ -3,8 +3,11 @@ setup: deps db deps: @pip install -r requirements.txt +sandbox_run: + @env PYTHONPATH=$$PYTHONPATH:vows:. python vows/sandbox/manage.py runserver + sandbox_shell: - @env PYTHONPATH=$$PYTHONPATH:vows/sandbox/:. python vows/sandbox/manage.py shell + @env PYTHONPATH=$$PYTHONPATH:vows:. python vows/sandbox/manage.py shell pyvows: @env PYTHONPATH=$$PYTHONPATH:vows/sandbox/:. pyvows -c -l django_pyvows --profile-threshold 95 vows/ diff --git a/django_pyvows/__init__.py b/django_pyvows/__init__.py index d3c1c5f..43d9932 100644 --- a/django_pyvows/__init__.py +++ b/django_pyvows/__init__.py @@ -9,3 +9,4 @@ # Copyright (c) 2011 Rafael Caricio rafael@caricio.com from context import DjangoContext # NOQA +from assertions import * # NOQA diff --git a/django_pyvows/assertions.py b/django_pyvows/assertions.py new file mode 100644 index 0000000..e918f0a --- /dev/null +++ b/django_pyvows/assertions.py @@ -0,0 +1,34 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# django-pyvows extensions +# https://github.com/rafaelcaricio/django-pyvows + +# Licensed under the MIT license: +# http://www.opensource.org/licenses/mit-license +# Copyright (c) 2011 Rafael Caricio rafael@caricio.com + +from django.test import SimpleTestCase +from pyvows import Vows + + +class _Dummy(SimpleTestCase): + def nop(): + pass + +dummy_testcase = _Dummy('nop') + + +@Vows.assertion +def redirects_to(*args, **kwargs): + return dummy_testcase.assertRedirects(*args, **kwargs) + + +@Vows.assertion +def contains(*args, **kwargs): + return dummy_testcase.assertContains(*args, **kwargs) + + +@Vows.assertion +def with_form_error(*args, **kwargs): + return dummy_testcase.assertFormError(*args, **kwargs) diff --git a/vows/assertions_vows.py b/vows/assertions_vows.py new file mode 100644 index 0000000..067274c --- /dev/null +++ b/vows/assertions_vows.py @@ -0,0 +1,40 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# django-pyvows extensions +# https://github.com/rafaelcaricio/django-pyvows + +# Licensed under the MIT license: +# http://www.opensource.org/licenses/mit-license +# Copyright (c) 2011 Rafael Caricio rafael@caricio.com + +from pyvows import Vows, expect + +from django_pyvows.context import DjangoContext + +DjangoContext.start_environment("sandbox.sandbox.settings") + + +@Vows.batch +class DjangoAssertionsVows(DjangoContext): + + class RedirectsTo(DjangoContext): + def topic(self): + return self.get("/say") + + def should_redirect(self, topic): + expect(topic).redirects_to('/say/', status_code=301) + + class Contains(DjangoContext): + def topic(self): + return self.get('/say/') + + def should_work(self, topic): + expect(topic).contains('name') + + class WithFormError(DjangoContext): + def topic(self): + return self.post('/post-name/', {'another_field': 'filled'}) + + def should_work(self, topic): + expect(topic).with_form_error('form', 'your_name', 'This field is required.') diff --git a/vows/sandbox/main/forms.py b/vows/sandbox/main/forms.py new file mode 100644 index 0000000..3fb866d --- /dev/null +++ b/vows/sandbox/main/forms.py @@ -0,0 +1,5 @@ +from django import forms + + +class NameForm(forms.Form): + your_name = forms.CharField(label='Your name', max_length=100, required=True) diff --git a/vows/sandbox/main/views.py b/vows/sandbox/main/views.py index e334a90..39e10b8 100644 --- a/vows/sandbox/main/views.py +++ b/vows/sandbox/main/views.py @@ -10,6 +10,10 @@ from django.conf import settings from django.http import HttpResponse +from django.http import HttpResponseRedirect +from django.shortcuts import render + +from .forms import NameForm def home(request): @@ -33,3 +37,17 @@ def post_it(request): def post_file(request): return HttpResponse(request.FILES['the_file'].read().strip()) + + +def post_name(request): + if request.method == 'POST': + form = NameForm(request.POST) + + if form.is_valid(): + return HttpResponseRedirect( + '/say/?name={}'.format(form.cleaned_data['your_name'])) + + else: + form = NameForm() + + return render(request, 'name.html', {'form': form}) diff --git a/vows/sandbox/sandbox/urls.py b/vows/sandbox/sandbox/urls.py index 6a48525..2ecfaf2 100644 --- a/vows/sandbox/sandbox/urls.py +++ b/vows/sandbox/sandbox/urls.py @@ -14,7 +14,7 @@ Including another URLconf 2. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls)) """ from django.conf.urls import url -from main.views import home, say_hello, post_it, post_file +from main.views import home, say_hello, post_it, post_file, post_name urlpatterns = [ @@ -22,4 +22,5 @@ urlpatterns = [ url(r'^say/$', say_hello, name='say_hello'), url(r'^post_it/$', post_it, name='post_it'), url(r'^post_file/$', post_file, name='post_file'), + url(r'^post-name/$', post_name, name='post_name'), ] diff --git a/vows/sandbox/templates/name.html b/vows/sandbox/templates/name.html new file mode 100644 index 0000000..bd1b89c --- /dev/null +++ b/vows/sandbox/templates/name.html @@ -0,0 +1,5 @@ +
+ {% csrf_token %} + {{ form }} + +
From c86a0917cbbadc295a73f6c03b8e625ec31ef6e5 Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Sun, 19 Jul 2015 11:17:55 +0200 Subject: [PATCH 14/20] Isolate responsabilities --- django_pyvows/context.py | 33 +++++++++++++------------------ django_pyvows/http_helpers.py | 23 +++++++++++++-------- django_pyvows/settings_helpers.py | 9 +++++++++ 3 files changed, 38 insertions(+), 27 deletions(-) create mode 100644 django_pyvows/settings_helpers.py diff --git a/django_pyvows/context.py b/django_pyvows/context.py index e377e5c..a772092 100644 --- a/django_pyvows/context.py +++ b/django_pyvows/context.py @@ -13,32 +13,27 @@ import os import django from pyvows import Vows -from django_pyvows import http_helpers +from django_pyvows.http_helpers import HttpClientSupport +from django_pyvows.settings_helpers import SettingsOverrideSupport -class DjangoContext(Vows.Context): +class DjangoContext(Vows.Context, HttpClientSupport, SettingsOverrideSupport): + def __init__(self, parent): + super(DjangoContext, self).__init__(parent) + HttpClientSupport.__init__(self) + SettingsOverrideSupport.__init__(self) + self.ignore('start_environment', 'settings_module') + + def settings_module(self): + return 'settings' @classmethod - def start_environment(cls, settings_path): - if not settings_path: + def start_environment(cls, settings_module): + if not settings_module: raise ValueError('The settings_path argument is required.') - os.environ.update({'DJANGO_SETTINGS_MODULE': settings_path}) + os.environ.update({'DJANGO_SETTINGS_MODULE': settings_module}) django.setup() from django.test.utils import setup_test_environment setup_test_environment() - - def __init__(self, parent): - super(DjangoContext, self).__init__(parent) - self.ignore('start_environment', 'settings', 'get', 'post') - - def settings(self, **kwargs): - from django.test.utils import override_settings - return override_settings(**kwargs) - - def get(self, *args, **kwargs): - return http_helpers.get(*args, **kwargs) - - def post(self, *args, **kwargs): - return http_helpers.post(*args, **kwargs) diff --git a/django_pyvows/http_helpers.py b/django_pyvows/http_helpers.py index 0826136..bd38466 100644 --- a/django_pyvows/http_helpers.py +++ b/django_pyvows/http_helpers.py @@ -2,13 +2,20 @@ # -*- coding: utf-8 -*- -def get(*args, **kwargs): - from django.test.client import Client - client = Client() - return client.get(*args, **kwargs) +class HttpClientSupport(object): + def __init__(self): + self._client = None + self.ignore('get', 'post') + @property + def client(self): + if self._client is None: + from django.test.client import Client # Needs to be lazy loaded due settings config + self._client = Client() + return self._client -def post(*args, **kwargs): - from django.test.client import Client - client = Client() - return client.post(*args, **kwargs) + def get(self, *args, **kwargs): + return self.client.get(*args, **kwargs) + + def post(self, *args, **kwargs): + return self.client.post(*args, **kwargs) diff --git a/django_pyvows/settings_helpers.py b/django_pyvows/settings_helpers.py new file mode 100644 index 0000000..0b12389 --- /dev/null +++ b/django_pyvows/settings_helpers.py @@ -0,0 +1,9 @@ + + +class SettingsOverrideSupport(object): + def __init__(self): + self.ignore('settings') + + def settings(self, **kwargs): + from django.test.utils import override_settings + return override_settings(**kwargs) From 960182861826e590ecb0942d4ee53ee56a5e1821 Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Sun, 19 Jul 2015 11:42:54 +0200 Subject: [PATCH 15/20] Support to unique test configuration --- django_pyvows/__init__.py | 1 - django_pyvows/assertions.py | 1 - django_pyvows/context.py | 9 ++++++--- django_pyvows/http_helpers.py | 8 +++++++- django_pyvows/settings_helpers.py | 8 ++++++++ django_pyvows/version.py | 1 - vows/assertions_vows.py | 5 +---- vows/client_vows.py | 12 +++--------- vows/context_vows.py | 2 +- vows/settings_vows.py | 4 +--- vows/test_config.py | 20 ++++++++++++++++++++ 11 files changed, 47 insertions(+), 24 deletions(-) create mode 100644 vows/test_config.py diff --git a/django_pyvows/__init__.py b/django_pyvows/__init__.py index 43d9932..c55ac5c 100644 --- a/django_pyvows/__init__.py +++ b/django_pyvows/__init__.py @@ -1,4 +1,3 @@ -#!/usr/bin/python # -*- coding: utf-8 -*- # django-pyvows extensions diff --git a/django_pyvows/assertions.py b/django_pyvows/assertions.py index e918f0a..66153a0 100644 --- a/django_pyvows/assertions.py +++ b/django_pyvows/assertions.py @@ -1,4 +1,3 @@ -#!/usr/bin/python # -*- coding: utf-8 -*- # django-pyvows extensions diff --git a/django_pyvows/context.py b/django_pyvows/context.py index a772092..8ec90b9 100644 --- a/django_pyvows/context.py +++ b/django_pyvows/context.py @@ -1,4 +1,3 @@ -#!/usr/bin/python # -*- coding: utf-8 -*- # django-pyvows extensions @@ -20,15 +19,19 @@ from django_pyvows.settings_helpers import SettingsOverrideSupport class DjangoContext(Vows.Context, HttpClientSupport, SettingsOverrideSupport): def __init__(self, parent): super(DjangoContext, self).__init__(parent) + HttpClientSupport.__init__(self) SettingsOverrideSupport.__init__(self) - self.ignore('start_environment', 'settings_module') + + self.ignore('setup_environment', 'settings_module') + + DjangoContext.setup_environment(self.settings_module()) def settings_module(self): return 'settings' @classmethod - def start_environment(cls, settings_module): + def setup_environment(cls, settings_module): if not settings_module: raise ValueError('The settings_path argument is required.') diff --git a/django_pyvows/http_helpers.py b/django_pyvows/http_helpers.py index bd38466..dbafb16 100644 --- a/django_pyvows/http_helpers.py +++ b/django_pyvows/http_helpers.py @@ -1,6 +1,12 @@ -#!/usr/bin/python # -*- coding: utf-8 -*- +# django-pyvows extensions +# https://github.com/rafaelcaricio/django-pyvows + +# Licensed under the MIT license: +# http://www.opensource.org/licenses/mit-license +# Copyright (c) 2011 Rafael Caricio rafael@caricio.com + class HttpClientSupport(object): def __init__(self): diff --git a/django_pyvows/settings_helpers.py b/django_pyvows/settings_helpers.py index 0b12389..243df4b 100644 --- a/django_pyvows/settings_helpers.py +++ b/django_pyvows/settings_helpers.py @@ -1,3 +1,11 @@ +# -*- coding: utf-8 -*- + +# django-pyvows extensions +# https://github.com/rafaelcaricio/django-pyvows + +# Licensed under the MIT license: +# http://www.opensource.org/licenses/mit-license +# Copyright (c) 2011 Rafael Caricio rafael@caricio.com class SettingsOverrideSupport(object): diff --git a/django_pyvows/version.py b/django_pyvows/version.py index 87442a9..3f7e054 100644 --- a/django_pyvows/version.py +++ b/django_pyvows/version.py @@ -1,4 +1,3 @@ -#!/usr/bin/python # -*- coding: utf-8 -*- # django-pyvows extensions diff --git a/vows/assertions_vows.py b/vows/assertions_vows.py index 067274c..fe39c72 100644 --- a/vows/assertions_vows.py +++ b/vows/assertions_vows.py @@ -1,4 +1,3 @@ -#!/usr/bin/python # -*- coding: utf-8 -*- # django-pyvows extensions @@ -10,9 +9,7 @@ from pyvows import Vows, expect -from django_pyvows.context import DjangoContext - -DjangoContext.start_environment("sandbox.sandbox.settings") +from test_config import ConfiguredVowsContext as DjangoContext @Vows.batch diff --git a/vows/client_vows.py b/vows/client_vows.py index 1c9d0b3..7300a27 100644 --- a/vows/client_vows.py +++ b/vows/client_vows.py @@ -7,16 +7,10 @@ # Licensed under the MIT license: # http://www.opensource.org/licenses/mit-license # Copyright (c) 2011 Rafael Caricio rafael@caricio.com -from os.path import abspath, join, dirname from pyvows import Vows, expect -from django_pyvows.context import DjangoContext - - -TEST_FILE_PATH = abspath(join(dirname(__file__), 'fixtures/the_file.txt')) - -DjangoContext.start_environment("sandbox.sandbox.settings") +from test_config import ConfiguredVowsContext as DjangoContext @Vows.batch @@ -48,14 +42,14 @@ class DjangoHTTPTestClientCompatibilityVows(DjangoContext): class PostFile(DjangoContext): def topic(self): - return self.post('/post_file/', {'the_file': open(TEST_FILE_PATH)}) + return self.post('/post_file/', {'the_file': open(self.TEST_FILE_PATH)}) def should_be_posted_to_the_server(self, topic): expect(topic.content).to_equal("the contents") class WhenNotFound(DjangoContext): def topic(self): - return self.post('/post_/', {'the_file': open(TEST_FILE_PATH)}) + return self.post('/post_/', {'the_file': open(self.TEST_FILE_PATH)}) def should_be_404(self, topic): expect(topic.status_code).to_equal(404) diff --git a/vows/context_vows.py b/vows/context_vows.py index aa32940..48b3ea2 100644 --- a/vows/context_vows.py +++ b/vows/context_vows.py @@ -19,7 +19,7 @@ class ContextTest(Vows.Context): @capture_error def topic(self): - return DjangoContext.start_environment(None) + return DjangoContext.setup_environment(None) def should_be_an_error(self, topic): expect(topic).to_be_an_error() diff --git a/vows/settings_vows.py b/vows/settings_vows.py index b1f9f4c..827ad1f 100644 --- a/vows/settings_vows.py +++ b/vows/settings_vows.py @@ -11,9 +11,7 @@ from pyvows import Vows, expect from django.test.utils import override_settings -from django_pyvows.context import DjangoContext - -DjangoContext.start_environment("sandbox.sandbox.settings") +from test_config import ConfiguredVowsContext as DjangoContext @Vows.batch diff --git a/vows/test_config.py b/vows/test_config.py new file mode 100644 index 0000000..e557ccc --- /dev/null +++ b/vows/test_config.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- + +# django-pyvows extensions +# https://github.com/rafaelcaricio/django-pyvows + +# Licensed under the MIT license: +# http://www.opensource.org/licenses/mit-license +# Copyright (c) 2011 Rafael Caricio rafael@caricio.com + +from os.path import abspath, join, dirname + +from django_pyvows.context import DjangoContext + + +class ConfiguredVowsContext(DjangoContext): + def settings_module(self): + return 'sandbox.sandbox.settings' + + def setup(self): + self.TEST_FILE_PATH = abspath(join(dirname(__file__), 'fixtures/the_file.txt')) From 709f6db6eff405708675892d542b76fb37396ae0 Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Sun, 19 Jul 2015 11:56:34 +0200 Subject: [PATCH 16/20] Simplify sandbox code --- vows/sandbox/main/admin.py | 10 ---------- vows/sandbox/main/models.py | 10 ---------- vows/sandbox/main/tests.py | 16 ---------------- vows/sandbox/main/urls.py | 0 vows/sandbox/main/views.py | 10 ---------- vows/sandbox/sandbox/settings.py | 16 +--------------- vows/sandbox/sandbox/urls.py | 15 --------------- 7 files changed, 1 insertion(+), 76 deletions(-) delete mode 100644 vows/sandbox/main/tests.py delete mode 100644 vows/sandbox/main/urls.py diff --git a/vows/sandbox/main/admin.py b/vows/sandbox/main/admin.py index e09a757..9d1b31a 100644 --- a/vows/sandbox/main/admin.py +++ b/vows/sandbox/main/admin.py @@ -1,13 +1,3 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# django-pyvows extensions -# https://github.com/rafaelcaricio/django-pyvows - -# Licensed under the MIT license: -# http://www.opensource.org/licenses/mit-license -# Copyright (c) 2011 Rafael Caricio rafael@caricio.com - from django.contrib import admin from models import StringModel diff --git a/vows/sandbox/main/models.py b/vows/sandbox/main/models.py index 8c7dbca..5d1a30c 100644 --- a/vows/sandbox/main/models.py +++ b/vows/sandbox/main/models.py @@ -1,13 +1,3 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# django-pyvows extensions -# https://github.com/rafaelcaricio/django-pyvows - -# Licensed under the MIT license: -# http://www.opensource.org/licenses/mit-license -# Copyright (c) 2011 Rafael Caricio rafael@caricio.com - from django.db import models diff --git a/vows/sandbox/main/tests.py b/vows/sandbox/main/tests.py deleted file mode 100644 index 501deb7..0000000 --- a/vows/sandbox/main/tests.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -This file demonstrates writing tests using the unittest module. These will pass -when you run "manage.py test". - -Replace this with more appropriate tests for your application. -""" - -from django.test import TestCase - - -class SimpleTest(TestCase): - def test_basic_addition(self): - """ - Tests that 1 + 1 always equals 2. - """ - self.assertEqual(1 + 1, 2) diff --git a/vows/sandbox/main/urls.py b/vows/sandbox/main/urls.py deleted file mode 100644 index e69de29..0000000 diff --git a/vows/sandbox/main/views.py b/vows/sandbox/main/views.py index 39e10b8..3d48ef5 100644 --- a/vows/sandbox/main/views.py +++ b/vows/sandbox/main/views.py @@ -1,13 +1,3 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# django-pyvows extensions -# https://github.com/rafaelcaricio/django-pyvows - -# Licensed under the MIT license: -# http://www.opensource.org/licenses/mit-license -# Copyright (c) 2011 Rafael Caricio rafael@caricio.com - from django.conf import settings from django.http import HttpResponse from django.http import HttpResponseRedirect diff --git a/vows/sandbox/sandbox/settings.py b/vows/sandbox/sandbox/settings.py index 2364fa7..bb986a7 100644 --- a/vows/sandbox/sandbox/settings.py +++ b/vows/sandbox/sandbox/settings.py @@ -9,27 +9,17 @@ https://docs.djangoproject.com/en/1.8/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/1.8/ref/settings/ """ - -# Build paths inside the project like this: os.path.join(BASE_DIR, ...) import os BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ - -# SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = '+1e*=5h9ri-$#@xmgs*9wrm&@d#li6d9u3e&atmzo5s*6k%_#o' -# SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [] - -# Application definition - INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', @@ -74,9 +64,7 @@ TEMPLATES = [ WSGI_APPLICATION = 'sandbox.wsgi.application' - # Database -# https://docs.djangoproject.com/en/1.8/ref/settings/#databases DATABASES = { 'default': { @@ -87,7 +75,6 @@ DATABASES = { # Internationalization -# https://docs.djangoproject.com/en/1.8/topics/i18n/ LANGUAGE_CODE = 'en-us' @@ -101,9 +88,8 @@ USE_TZ = True # Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/1.8/howto/static-files/ STATIC_URL = '/static/' -# Custom config to be used in tests +# Custom configs SAY_HELLO_WITHOUT_NAME = False diff --git a/vows/sandbox/sandbox/urls.py b/vows/sandbox/sandbox/urls.py index 2ecfaf2..9738df5 100644 --- a/vows/sandbox/sandbox/urls.py +++ b/vows/sandbox/sandbox/urls.py @@ -1,18 +1,3 @@ -"""sandbox URL Configuration - -The `urlpatterns` list routes URLs to views. For more information please see: - https://docs.djangoproject.com/en/1.8/topics/http/urls/ -Examples: -Function views - 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') -Class-based views - 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') -Including another URLconf - 1. Add an import: from blog import urls as blog_urls - 2. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls)) -""" from django.conf.urls import url from main.views import home, say_hello, post_it, post_file, post_name From 9494a5ceac5461c40910df1f2571d0b7e1e47af6 Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Sun, 19 Jul 2015 12:20:37 +0200 Subject: [PATCH 17/20] Reoganize project to reflect final usage --- Makefile | 12 ++++++------ {vows/sandbox => sandbox}/__init__.py | 0 {vows/sandbox => sandbox}/main/__init__.py | 0 {vows/sandbox => sandbox}/main/admin.py | 0 {vows/sandbox => sandbox}/main/forms.py | 0 {vows/sandbox => sandbox}/main/models.py | 0 {vows/sandbox => sandbox}/main/views.py | 0 {vows/sandbox => sandbox}/manage.py | 0 {vows/sandbox => sandbox}/sandbox/__init__.py | 0 {vows/sandbox => sandbox}/sandbox/settings.py | 4 ++-- {vows/sandbox => sandbox}/sandbox/urls.py | 0 {vows/sandbox => sandbox}/sandbox/wsgi.py | 0 {vows/sandbox => sandbox}/templates/index.html | 0 {vows/sandbox => sandbox}/templates/name.html | 0 vows/test_config.py | 2 +- 15 files changed, 9 insertions(+), 9 deletions(-) rename {vows/sandbox => sandbox}/__init__.py (100%) rename {vows/sandbox => sandbox}/main/__init__.py (100%) rename {vows/sandbox => sandbox}/main/admin.py (100%) rename {vows/sandbox => sandbox}/main/forms.py (100%) rename {vows/sandbox => sandbox}/main/models.py (100%) rename {vows/sandbox => sandbox}/main/views.py (100%) rename {vows/sandbox => sandbox}/manage.py (100%) rename {vows/sandbox => sandbox}/sandbox/__init__.py (100%) rename {vows/sandbox => sandbox}/sandbox/settings.py (97%) rename {vows/sandbox => sandbox}/sandbox/urls.py (100%) rename {vows/sandbox => sandbox}/sandbox/wsgi.py (100%) rename {vows/sandbox => sandbox}/templates/index.html (100%) rename {vows/sandbox => sandbox}/templates/name.html (100%) diff --git a/Makefile b/Makefile index 8041e69..80f11f6 100644 --- a/Makefile +++ b/Makefile @@ -4,16 +4,16 @@ deps: @pip install -r requirements.txt sandbox_run: - @env PYTHONPATH=$$PYTHONPATH:vows:. python vows/sandbox/manage.py runserver + @env PYTHONPATH=sandbox:.:$$PYTHONPATH python sandbox/manage.py runserver sandbox_shell: - @env PYTHONPATH=$$PYTHONPATH:vows:. python vows/sandbox/manage.py shell + @env PYTHONPATH=sandbox:.:$$PYTHONPATH python sandbox/manage.py shell pyvows: - @env PYTHONPATH=$$PYTHONPATH:vows/sandbox/:. pyvows -c -l django_pyvows --profile-threshold 95 vows/ + @env PYTHONPATH=sandbox:.:$$PYTHONPATH pyvows -c -l django_pyvows --profile-threshold 95 vows/ -ci_test: - @env PYTHONPATH=$$PYTHONPATH:vows/sandbox/:. python vows/sandbox/manage.py syncdb && pyvows --no_color --cover --cover_package=django_pyvows --cover_threshold=100 -r django_pyvows.coverage.xml -x vows/ +ci_test: db + @env PYTHONPATH=sandbox:.:$$PYTHONPATH pyvows -c -l django_pyvows --profile-threshold 95 -r django_pyvows.coverage.xml vows/ db: - @env PYTHONPATH=$$PYTHONPATH:. python vows/sandbox/manage.py syncdb + @env PYTHONPATH=$$PYTHONPATH:. python sandbox/manage.py syncdb --noinput diff --git a/vows/sandbox/__init__.py b/sandbox/__init__.py similarity index 100% rename from vows/sandbox/__init__.py rename to sandbox/__init__.py diff --git a/vows/sandbox/main/__init__.py b/sandbox/main/__init__.py similarity index 100% rename from vows/sandbox/main/__init__.py rename to sandbox/main/__init__.py diff --git a/vows/sandbox/main/admin.py b/sandbox/main/admin.py similarity index 100% rename from vows/sandbox/main/admin.py rename to sandbox/main/admin.py diff --git a/vows/sandbox/main/forms.py b/sandbox/main/forms.py similarity index 100% rename from vows/sandbox/main/forms.py rename to sandbox/main/forms.py diff --git a/vows/sandbox/main/models.py b/sandbox/main/models.py similarity index 100% rename from vows/sandbox/main/models.py rename to sandbox/main/models.py diff --git a/vows/sandbox/main/views.py b/sandbox/main/views.py similarity index 100% rename from vows/sandbox/main/views.py rename to sandbox/main/views.py diff --git a/vows/sandbox/manage.py b/sandbox/manage.py similarity index 100% rename from vows/sandbox/manage.py rename to sandbox/manage.py diff --git a/vows/sandbox/sandbox/__init__.py b/sandbox/sandbox/__init__.py similarity index 100% rename from vows/sandbox/sandbox/__init__.py rename to sandbox/sandbox/__init__.py diff --git a/vows/sandbox/sandbox/settings.py b/sandbox/sandbox/settings.py similarity index 97% rename from vows/sandbox/sandbox/settings.py rename to sandbox/sandbox/settings.py index bb986a7..bd96828 100644 --- a/vows/sandbox/sandbox/settings.py +++ b/sandbox/sandbox/settings.py @@ -28,7 +28,7 @@ INSTALLED_APPS = ( 'django.contrib.messages', 'django.contrib.staticfiles', - 'sandbox.main', + 'main', ) MIDDLEWARE_CLASSES = ( @@ -42,7 +42,7 @@ MIDDLEWARE_CLASSES = ( 'django.middleware.security.SecurityMiddleware', ) -ROOT_URLCONF = 'sandbox.sandbox.urls' +ROOT_URLCONF = 'sandbox.urls' TEMPLATES = [ { diff --git a/vows/sandbox/sandbox/urls.py b/sandbox/sandbox/urls.py similarity index 100% rename from vows/sandbox/sandbox/urls.py rename to sandbox/sandbox/urls.py diff --git a/vows/sandbox/sandbox/wsgi.py b/sandbox/sandbox/wsgi.py similarity index 100% rename from vows/sandbox/sandbox/wsgi.py rename to sandbox/sandbox/wsgi.py diff --git a/vows/sandbox/templates/index.html b/sandbox/templates/index.html similarity index 100% rename from vows/sandbox/templates/index.html rename to sandbox/templates/index.html diff --git a/vows/sandbox/templates/name.html b/sandbox/templates/name.html similarity index 100% rename from vows/sandbox/templates/name.html rename to sandbox/templates/name.html diff --git a/vows/test_config.py b/vows/test_config.py index e557ccc..c13d4ee 100644 --- a/vows/test_config.py +++ b/vows/test_config.py @@ -14,7 +14,7 @@ from django_pyvows.context import DjangoContext class ConfiguredVowsContext(DjangoContext): def settings_module(self): - return 'sandbox.sandbox.settings' + return 'sandbox.settings' def setup(self): self.TEST_FILE_PATH = abspath(join(dirname(__file__), 'fixtures/the_file.txt')) From d6833c8584e3d104c64cc683d472d57dcf70afbc Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Sun, 19 Jul 2015 12:25:39 +0200 Subject: [PATCH 18/20] Use builtin assertions --- vows/client_vows.py | 8 ++++---- vows/settings_vows.py | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/vows/client_vows.py b/vows/client_vows.py index 7300a27..f828aac 100644 --- a/vows/client_vows.py +++ b/vows/client_vows.py @@ -24,28 +24,28 @@ class DjangoHTTPTestClientCompatibilityVows(DjangoContext): expect(topic.status_code).to_equal(200) def should_ask_for_my_name(self, topic): - expect(topic.content).to_equal('What\'s your name?') + expect(topic).contains('What\'s your name?') class SimpleGetWithParams(DjangoContext): def topic(self): return self.get('/say/?name=Rafael') def should_say_hello_to_me(self, topic): - expect(topic.content).to_equal('Hello, Rafael!') + expect(topic).contains('Hello, Rafael!') class SimplePost(DjangoContext): def topic(self): return self.post('/post_it/', {'value': 'posted!'}) def should_be_posted(self, topic): - expect(topic.content).to_equal('posted!') + expect(topic).contains('posted!') class PostFile(DjangoContext): def topic(self): return self.post('/post_file/', {'the_file': open(self.TEST_FILE_PATH)}) def should_be_posted_to_the_server(self, topic): - expect(topic.content).to_equal("the contents") + expect(topic).contains("the contents") class WhenNotFound(DjangoContext): def topic(self): diff --git a/vows/settings_vows.py b/vows/settings_vows.py index 827ad1f..2228358 100644 --- a/vows/settings_vows.py +++ b/vows/settings_vows.py @@ -26,7 +26,7 @@ class SettingsOverridingVows(DjangoContext): expect(topic.status_code).to_equal(200) def should_ask_for_my_name(self, topic): - expect(topic.content).to_equal("What's your name?") + expect(topic).contains("What's your name?") class SayHelloWithoutName(DjangoContext): def topic(self): @@ -37,7 +37,7 @@ class SettingsOverridingVows(DjangoContext): expect(topic.status_code).to_equal(200) def should_(self, topic): - expect(topic.content).to_equal("Hello, guest!") + expect(topic).contains("Hello, guest!") class UseDecoratorToChangeSettings(DjangoContext): @override_settings(SAY_HELLO_WITHOUT_NAME=True) @@ -45,4 +45,4 @@ class SettingsOverridingVows(DjangoContext): return self.get('/say/') def should_say_hello_to_guest(self, topic): - expect(topic.content).to_equal("Hello, guest!") + expect(topic).contains("Hello, guest!") From 61a25d0b9f8768b5d67fae872429919af07016ba Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Sun, 19 Jul 2015 13:26:40 +0200 Subject: [PATCH 19/20] Update README --- README.md | 62 +++++++++++++++++++++++++------------------------------ 1 file changed, 28 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 2c7bb3b..101d8d1 100644 --- a/README.md +++ b/README.md @@ -1,58 +1,52 @@ -Django-Pyvows -============= +# Django-PyVows +This project is an extension of pyVows to test [Django](https://www.djangoproject.com/) projects. It enables usage of core Django testing tools in a way that is in sync with pyVows workflow mindset. -Pyvows -------- +PyVows is a BDD (Behaviour Driven Development) inspired by Vows for node.js -pyvows is a BDD (Behaviour Driven Development) inspired by Vows for node.js +More documentation about pyVows can be found at [the project homepage](http://pyvows.org) or for mode updated usage check the project [tests itself](https://github.com/heynemann/pyvows/tree/master/tests). -More documentation about pyvows can be found at the project homepage +Django-PyVows is in sync with the latest Django developments and supports Django 1.8. -http://pyvows.org +## Usage -Django-Pyvows --------------- - -This project contains extensions to test Django projects under pyVows. - -Using Django-Pyvows -------------------- - -There is no need to modify your project to use Django-PyVows. You only have to create the vows -as you usually would, start the server and call your project urls: +There is no need to modify your project to use Django-PyVows. You only have to define where is your project settings module and start calling your project urls in your tests. ```python from pyvows import Vows, expect -from django_pyvows.context import DjangoHTTPContext +from django_pyvows.context import DjangoContext @Vows.batch -class ContextTest(DjangoHTTPContext): +class SimpleTestVows(DjangoContext): - def setup(self): - self.start_server() + def settings_module(self): + return 'yourproject.settings' def topic(self): return self.get('/mygreaturl/') def should_be_a_success(self, topic): - expect(topic.getcode()).to_equal(200) + expect(topic.status_code).to_equal(200) def should_return_the_correct_response_type(self, topic): - expect(topic.headers.type).to_equal("text/html") + expect(topic).contains("Welcome!") ``` -To work you only need to override the `get_settings` method from DjangoHTTPContext to -return the path of your settings module. The default `get_settings` returns `"settings"`. +The default `settings_module` is `settings` so you should define it accordly based on your `PYTHONPATH`. -More info: https://github.com/rafaelcaricio/django-pyvows/wiki +### HTTP Client -External links --------------- -- Asynchronous Testing with Django and PyVows - - http://www.realpython.com/blog/python/asynchronous-testing-with-django-and-pyvows/ -- Unit Testing with pyVows and Django - - http://www.realpython.com/blog/python/unit-testing-with-pyvows-and-django/ -- Integration testing with pyVows and Django - - http://www.realpython.com/blog/python/integration-testing-with-pyvows-and-django/ +We support `django.test.Client`, the methods `DjangoContext.get` and `DjangoContext.post` are actually simple wrappers around it so [the usage](https://docs.djangoproject.com/en/1.8/topics/testing/tools/#the-test-client) is the same. + +### Assertions + +The available assertions in Django-PyVows are the same as in `django.test.SimpleTestCase` they were adapted to the context of BDD and PyVows. + +- `contains`: reflects the `django.test.SimpleTestCase.assertContains`. Check usage at: [https://docs.djangoproject.com/en/1.8/topics/testing/tools/#django.test.SimpleTestCase.assertContains](https://docs.djangoproject.com/en/1.8/topics/testing/tools/#django.test.SimpleTestCase.assertContains) +- `redirects_to`: reflects the `django.test.SimpleTestCase.assertRedirects`. Check usage at: [https://docs.djangoproject.com/en/1.8/topics/testing/tools/#django.test.SimpleTestCase.assertRedirects](https://docs.djangoproject.com/en/1.8/topics/testing/tools/#django.test.SimpleTestCase.assertRedirects) +- `with_form_error`: reflects the `django.test.SimpleTestCase.assertFormError`. Check usage at: [https://docs.djangoproject.com/en/1.8/topics/testing/tools/#django.test.SimpleTestCase.assertFormError](https://docs.djangoproject.com/en/1.8/topics/testing/tools/#django.test.SimpleTestCase.assertFormError) + +### Settings Override + +TODO \ No newline at end of file From 0347dd366c35e08b3b2a3cc62ef294662bece655 Mon Sep 17 00:00:00 2001 From: Rafael Caricio Date: Sun, 19 Jul 2015 13:34:58 +0200 Subject: [PATCH 20/20] Fix README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 101d8d1..58311ac 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This project is an extension of pyVows to test [Django](https://www.djangoprojec PyVows is a BDD (Behaviour Driven Development) inspired by Vows for node.js -More documentation about pyVows can be found at [the project homepage](http://pyvows.org) or for mode updated usage check the project [tests itself](https://github.com/heynemann/pyvows/tree/master/tests). +More documentation about pyVows can be found at [the project homepage](http://pyvows.org) or for more updated usage check the project [tests itself](https://github.com/heynemann/pyvows/tree/master/tests). Django-PyVows is in sync with the latest Django developments and supports Django 1.8. @@ -29,7 +29,7 @@ class SimpleTestVows(DjangoContext): def should_be_a_success(self, topic): expect(topic.status_code).to_equal(200) - def should_return_the_correct_response_type(self, topic): + def should_return_the_correct_response(self, topic): expect(topic).contains("Welcome!") ``` @@ -37,7 +37,7 @@ The default `settings_module` is `settings` so you should define it accordly bas ### HTTP Client -We support `django.test.Client`, the methods `DjangoContext.get` and `DjangoContext.post` are actually simple wrappers around it so [the usage](https://docs.djangoproject.com/en/1.8/topics/testing/tools/#the-test-client) is the same. +We support `django.test.Client`, the methods `DjangoContext.get` and `DjangoContext.post` are actually simple wrappers around it so [the usage](https://docs.djangoproject.com/en/1.8/topics/testing/tools/#the-test-client) is the same. The test client is also available in the `DjangoContext` instance as `self.client`. ### Assertions