diff --git a/.gitignore b/.gitignore index 0144ed7..cf60f24 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ dist/ .project .pydevproject build/ +*.sqlite3 diff --git a/Makefile b/Makefile index 5ed64ba..80f11f6 100644 --- a/Makefile +++ b/Makefile @@ -3,11 +3,17 @@ setup: deps db deps: @pip install -r requirements.txt -pyvows: db - @env PYTHONPATH=$$PYTHONPATH:vows/sandbox/:. pyvows -c -l django_pyvows --profile-threshold 95 vows/ +sandbox_run: + @env PYTHONPATH=sandbox:.:$$PYTHONPATH python sandbox/manage.py runserver -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/ +sandbox_shell: + @env PYTHONPATH=sandbox:.:$$PYTHONPATH python sandbox/manage.py shell + +pyvows: + @env PYTHONPATH=sandbox:.:$$PYTHONPATH pyvows -c -l django_pyvows --profile-threshold 95 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/README.md b/README.md index 2c7bb3b..58311ac 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 more 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") + def should_return_the_correct_response(self, topic): + 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. The test client is also available in the `DjangoContext` instance as `self.client`. + +### 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 diff --git a/django_pyvows/__init__.py b/django_pyvows/__init__.py index e22d7f2..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 @@ -8,4 +7,5 @@ # 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 +from assertions import * # NOQA diff --git a/django_pyvows/assertions.py b/django_pyvows/assertions.py new file mode 100644 index 0000000..66153a0 --- /dev/null +++ b/django_pyvows/assertions.py @@ -0,0 +1,33 @@ +# -*- 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/django_pyvows/assertions/__init__.py b/django_pyvows/assertions/__init__.py deleted file mode 100644 index 27216e8..0000000 --- a/django_pyvows/assertions/__init__.py +++ /dev/null @@ -1,13 +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 urls import * -from models import * -from templates import * 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/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 cf6f96f..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 @@ -9,101 +8,35 @@ # 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 +from django_pyvows.http_helpers import HttpClientSupport +from django_pyvows.settings_helpers import SettingsOverrideSupport -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() +class DjangoContext(Vows.Context, HttpClientSupport, SettingsOverrideSupport): 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') - def setup(self): - DjangoContext.start_environment(self.get_settings()) + HttpClientSupport.__init__(self) + SettingsOverrideSupport.__init__(self) - def get_settings(self): - return os.environ.get('DJANGO_SETTINGS_MODULE', 'settings') + self.ignore('setup_environment', 'settings_module') - def url(self, path): - return Url(self, path) + DjangoContext.setup_environment(self.settings_module()) - def template(self, template_name, context): - return Template(template_name, context) + def settings_module(self): + return 'settings' - def request(self, **kw): - return HttpRequest(**kw) + @classmethod + def setup_environment(cls, settings_module): + if not settings_module: + raise ValueError('The settings_path argument is required.') - def model(self, model_class): - return Model(self, model_class) - - def get(self, path): - return http_helpers.get(self.get_url(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 - - - -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) + os.environ.update({'DJANGO_SETTINGS_MODULE': settings_module}) + django.setup() + from django.test.utils import setup_test_environment + setup_test_environment() diff --git a/django_pyvows/http_helpers.py b/django_pyvows/http_helpers.py index e37ecff..dbafb16 100644 --- a/django_pyvows/http_helpers.py +++ b/django_pyvows/http_helpers.py @@ -1,101 +1,27 @@ -#!/usr/bin/python # -*- coding: utf-8 -*- -import httplib2 +# django-pyvows extensions +# https://github.com/rafaelcaricio/django-pyvows -import mimetypes -import mimetools -import itertools +# Licensed under the MIT license: +# http://www.opensource.org/licenses/mit-license +# Copyright (c) 2011 Rafael Caricio rafael@caricio.com -class MultiPartForm(object): - """Accumulate the data to be used when posting a form.""" +class HttpClientSupport(object): def __init__(self): - self.form_fields = [] - self.files = [] - self.boundary = mimetools.choose_boundary() - return + self._client = None + self.ignore('get', 'post') - def get_content_type(self): - return 'multipart/form-data; boundary=%s' % self.boundary + @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 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 get(url): - h = httplib2.Http() - resp, content = h.request(url) - return 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 + 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/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/vows/sandbox/main/admin.py b/django_pyvows/settings_helpers.py similarity index 51% rename from vows/sandbox/main/admin.py rename to django_pyvows/settings_helpers.py index f7e6678..243df4b 100644 --- a/vows/sandbox/main/admin.py +++ b/django_pyvows/settings_helpers.py @@ -1,4 +1,3 @@ -#!/usr/bin/python # -*- coding: utf-8 -*- # django-pyvows extensions @@ -8,11 +7,11 @@ # http://www.opensource.org/licenses/mit-license # Copyright (c) 2011 Rafael Caricio rafael@caricio.com -from django.contrib import admin -from sandbox.main.models import StringModel +class SettingsOverrideSupport(object): + def __init__(self): + self.ignore('settings') -class StringModelAdmin(admin.ModelAdmin): - list_display = ('name', ) - -admin.site.register(StringModel, StringModelAdmin) + def settings(self, **kwargs): + from django.test.utils import override_settings + return override_settings(**kwargs) 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/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/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 diff --git a/vows/sandbox/main/__init__.py b/sandbox/__init__.py similarity index 100% rename from vows/sandbox/main/__init__.py rename to sandbox/__init__.py diff --git a/sandbox/main/__init__.py b/sandbox/main/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/sandbox/main/admin.py b/sandbox/main/admin.py new file mode 100644 index 0000000..9d1b31a --- /dev/null +++ b/sandbox/main/admin.py @@ -0,0 +1,9 @@ +from django.contrib import admin + +from models import StringModel + + +class StringModelAdmin(admin.ModelAdmin): + list_display = ('name', ) + +admin.site.register(StringModel, StringModelAdmin) diff --git a/sandbox/main/forms.py b/sandbox/main/forms.py new file mode 100644 index 0000000..3fb866d --- /dev/null +++ b/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/sandbox/main/models.py b/sandbox/main/models.py new file mode 100644 index 0000000..5d1a30c --- /dev/null +++ b/sandbox/main/models.py @@ -0,0 +1,5 @@ +from django.db import models + + +class StringModel(models.Model): + name = models.CharField(max_length=100) diff --git a/vows/sandbox/main/views.py b/sandbox/main/views.py similarity index 56% rename from vows/sandbox/main/views.py rename to sandbox/main/views.py index b956c7c..3d48ef5 100644 --- a/vows/sandbox/main/views.py +++ b/sandbox/main/views.py @@ -1,32 +1,43 @@ -#!/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 +from django.shortcuts import render + +from .forms import NameForm 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: 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) + def post_it(request): return HttpResponse(request.POST['value']) + 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/sandbox/manage.py b/sandbox/manage.py new file mode 100755 index 0000000..3b13742 --- /dev/null +++ b/sandbox/manage.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == "__main__": + 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/sandbox/sandbox/__init__.py b/sandbox/sandbox/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/sandbox/sandbox/settings.py b/sandbox/sandbox/settings.py new file mode 100644 index 0000000..bd96828 --- /dev/null +++ b/sandbox/sandbox/settings.py @@ -0,0 +1,95 @@ +""" +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/ +""" +import os + +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +SECRET_KEY = '+1e*=5h9ri-$#@xmgs*9wrm&@d#li6d9u3e&atmzo5s*6k%_#o' + +DEBUG = True + +ALLOWED_HOSTS = [] + +INSTALLED_APPS = ( + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + + '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 + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + + +# Internationalization + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) + +STATIC_URL = '/static/' + +# Custom configs +SAY_HELLO_WITHOUT_NAME = False diff --git a/sandbox/sandbox/urls.py b/sandbox/sandbox/urls.py new file mode 100644 index 0000000..9738df5 --- /dev/null +++ b/sandbox/sandbox/urls.py @@ -0,0 +1,11 @@ +from django.conf.urls import url +from main.views import home, say_hello, post_it, post_file, post_name + + +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'), + url(r'^post-name/$', post_name, name='post_name'), +] diff --git a/sandbox/sandbox/wsgi.py b/sandbox/sandbox/wsgi.py new file mode 100644 index 0000000..0e7d688 --- /dev/null +++ b/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() 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/sandbox/templates/name.html b/sandbox/templates/name.html new file mode 100644 index 0000000..bd1b89c --- /dev/null +++ b/sandbox/templates/name.html @@ -0,0 +1,5 @@ +
diff --git a/vows/__init__.py b/vows/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vows/assertions_vows.py b/vows/assertions_vows.py new file mode 100644 index 0000000..fe39c72 --- /dev/null +++ b/vows/assertions_vows.py @@ -0,0 +1,37 @@ +# -*- 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 test_config import ConfiguredVowsContext as DjangoContext + + +@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/client_vows.py b/vows/client_vows.py new file mode 100644 index 0000000..f828aac --- /dev/null +++ b/vows/client_vows.py @@ -0,0 +1,55 @@ +#!/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 test_config import ConfiguredVowsContext as DjangoContext + + +@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).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).contains('Hello, Rafael!') + + class SimplePost(DjangoContext): + def topic(self): + return self.post('/post_it/', {'value': 'posted!'}) + + def should_be_posted(self, topic): + 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).contains("the contents") + + class WhenNotFound(DjangoContext): + def topic(self): + 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 9bd596d..48b3ea2 100644 --- a/vows/context_vows.py +++ b/vows/context_vows.py @@ -9,109 +9,23 @@ # 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) + return DjangoContext.setup_environment(None) def should_be_an_error(self, topic): 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.') - - 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('/') - - 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 deleted file mode 100644 index 8f45377..0000000 --- a/vows/model_vows.py +++ /dev/null @@ -1,58 +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 django.db import models -from sandbox.main.models import StringModel - -@Vows.batch -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/sandbox/__init__.py b/vows/sandbox/__init__.py deleted file mode 100644 index 2e71dd5..0000000 --- a/vows/sandbox/__init__.py +++ /dev/null @@ -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/models.py b/vows/sandbox/main/models.py deleted file mode 100644 index f1c183e..0000000 --- a/vows/sandbox/main/models.py +++ /dev/null @@ -1,14 +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.db import models - -class StringModel(models.Model): - name = models.CharField(max_length=100) 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/manage.py b/vows/sandbox/manage.py deleted file mode 100755 index 3e4eedc..0000000 --- a/vows/sandbox/manage.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/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 - -if __name__ == "__main__": - execute_manager(settings) 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')), -) diff --git a/vows/server_vows.py b/vows/server_vows.py deleted file mode 100644 index 8b3ddbf..0000000 --- a/vows/server_vows.py +++ /dev/null @@ -1,83 +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 - -import httplib2 - -from django_pyvows.context import DjangoContext, DjangoHTTPContext -from django_pyvows.assertions import * - -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) - - diff --git a/vows/settings_vows.py b/vows/settings_vows.py index d40eb36..2228358 100644 --- a/vows/settings_vows.py +++ b/vows/settings_vows.py @@ -9,73 +9,40 @@ # 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, DjangoHTTPContext -from django_pyvows.settings_manager import settings_tracker, VowsSettings +from test_config import ConfiguredVowsContext as DjangoContext -DjangoContext.start_environment("sandbox.settings") @Vows.batch -class SettingsVows(DjangoContext): - - class WhenIUseTheSettingsTracker(DjangoContext): +class SettingsOverridingVows(DjangoContext): + class CannotSayHelloWithoutName(DjangoContext): def topic(self): - settings_tracker.install() + with self.settings(SAY_HELLO_WITHOUT_NAME=False): + return self.get('/say/') - class WhenImportFromDjangoConf(DjangoContext): + def should_be_ok(self, topic): + expect(topic.status_code).to_equal(200) - 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 should_ask_for_my_name(self, topic): + expect(topic).contains("What's your name?") + class SayHelloWithoutName(DjangoContext): def topic(self): - self.start_server(port=9000, settings={ - 'SAY_HELLO_WITHOUT_NAME': False - }) + with self.settings(SAY_HELLO_WITHOUT_NAME=True): + return self.get('/say/') + def should_be_ok(self, topic): + expect(topic.status_code).to_equal(200) + + def should_(self, topic): + expect(topic).contains("Hello, guest!") + + class UseDecoratorToChangeSettings(DjangoContext): + @override_settings(SAY_HELLO_WITHOUT_NAME=True) + def topic(self): return self.get('/say/') - def should_be_ok(self, (topic, content)): - expect(topic.status).to_equal(200) - - def should_ask_for_my_name(self, (topic, content)): - expect(content).to_equal("What's your name?") - - class SayHelloWithoutName(DjangoHTTPContext): - - def topic(self): - self.start_server(port=9001, 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!") - + def should_say_hello_to_guest(self, topic): + expect(topic).contains("Hello, guest!") diff --git a/vows/template_vows.py b/vows/template_vows.py deleted file mode 100644 index cd9470c..0000000 --- a/vows/template_vows.py +++ /dev/null @@ -1,46 +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 * - -@Vows.batch -class TemplateVows(DjangoContext): - - def get_settings(self): - return 'sandbox.settings' - - 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') - - diff --git a/vows/test_config.py b/vows/test_config.py new file mode 100644 index 0000000..c13d4ee --- /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.settings' + + def setup(self): + self.TEST_FILE_PATH = abspath(join(dirname(__file__), 'fixtures/the_file.txt')) diff --git a/vows/url_vows.py b/vows/url_vows.py deleted file mode 100644 index fdcd053..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 Vows, expect - -from django_pyvows.context import DjangoContext -from django_pyvows.assertions import * - -DjangoContext.start_environment("sandbox.settings") - -from sandbox.main.views import home - -@Vows.batch -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 932e888..0000000 --- a/vows/view_vows.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 pyvows import Vows, expect -from django_pyvows.context import DjangoContext - -DjangoContext.start_environment("sandbox.settings") - -from sandbox.main.views import home - -@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')