Explorar el Código

Added ability to manage Catalogue.Option from the dashboard

master
dfirst hace 7 años
padre
commit
f42e5d1d30

+ 17
- 0
src/oscar/apps/dashboard/catalogue/app.py Ver fichero

62
     attribute_option_group_delete_view = get_class('dashboard.catalogue.views',
62
     attribute_option_group_delete_view = get_class('dashboard.catalogue.views',
63
                                                    'AttributeOptionGroupDeleteView')
63
                                                    'AttributeOptionGroupDeleteView')
64
 
64
 
65
+    option_list_view = get_class('dashboard.catalogue.views', 'OptionListView')
66
+    option_create_view = get_class('dashboard.catalogue.views', 'OptionCreateView')
67
+    option_update_view = get_class('dashboard.catalogue.views', 'OptionUpdateView')
68
+    option_delete_view = get_class('dashboard.catalogue.views', 'OptionDeleteView')
69
+
65
     def get_urls(self):
70
     def get_urls(self):
66
         urls = [
71
         urls = [
67
             url(r'^products/(?P<pk>\d+)/$',
72
             url(r'^products/(?P<pk>\d+)/$',
129
             url(r'^attribute-option-group/(?P<pk>\w+)/delete/$',
134
             url(r'^attribute-option-group/(?P<pk>\w+)/delete/$',
130
                 self.attribute_option_group_delete_view.as_view(),
135
                 self.attribute_option_group_delete_view.as_view(),
131
                 name='catalogue-attribute-option-group-delete'),
136
                 name='catalogue-attribute-option-group-delete'),
137
+            url(r'^option/$',
138
+                self.option_list_view.as_view(),
139
+                name='catalogue-option-list'),
140
+            url(r'^option/create/$',
141
+                self.option_create_view.as_view(),
142
+                name='catalogue-option-create'),
143
+            url(r'^option/(?P<pk>\w+)/update/$',
144
+                self.option_update_view.as_view(),
145
+                name='catalogue-option-update'),
146
+            url(r'^option/(?P<pk>\w+)/delete/$',
147
+                self.option_delete_view.as_view(),
148
+                name='catalogue-option-delete'),
132
         ]
149
         ]
133
         return self.post_process_urls(urls)
150
         return self.post_process_urls(urls)
134
 
151
 

+ 8
- 0
src/oscar/apps/dashboard/catalogue/forms.py Ver fichero

17
 ProductRecommendation = get_model('catalogue', 'ProductRecommendation')
17
 ProductRecommendation = get_model('catalogue', 'ProductRecommendation')
18
 AttributeOptionGroup = get_model('catalogue', 'AttributeOptionGroup')
18
 AttributeOptionGroup = get_model('catalogue', 'AttributeOptionGroup')
19
 AttributeOption = get_model('catalogue', 'AttributeOption')
19
 AttributeOption = get_model('catalogue', 'AttributeOption')
20
+Option = get_model('catalogue', 'Option')
20
 ProductSelect = get_class('dashboard.catalogue.widgets', 'ProductSelect')
21
 ProductSelect = get_class('dashboard.catalogue.widgets', 'ProductSelect')
21
 RelatedFieldWidgetWrapper = get_class('dashboard.widgets',
22
 RelatedFieldWidgetWrapper = get_class('dashboard.widgets',
22
                                       'RelatedFieldWidgetWrapper')
23
                                       'RelatedFieldWidgetWrapper')
376
     class Meta:
377
     class Meta:
377
         model = AttributeOption
378
         model = AttributeOption
378
         fields = ['option']
379
         fields = ['option']
380
+
381
+
382
+class OptionForm(forms.ModelForm):
383
+
384
+    class Meta:
385
+        model = Option
386
+        fields = ['name', 'type']

+ 21
- 0
src/oscar/apps/dashboard/catalogue/tables.py Ver fichero

10
 Product = get_model('catalogue', 'Product')
10
 Product = get_model('catalogue', 'Product')
11
 Category = get_model('catalogue', 'Category')
11
 Category = get_model('catalogue', 'Category')
12
 AttributeOptionGroup = get_model('catalogue', 'AttributeOptionGroup')
12
 AttributeOptionGroup = get_model('catalogue', 'AttributeOptionGroup')
13
+Option = get_model('catalogue', 'Option')
13
 
14
 
14
 
15
 
15
 class ProductTable(DashboardTable):
16
 class ProductTable(DashboardTable):
95
         fields = ('name',)
96
         fields = ('name',)
96
         sequence = ('name', 'option_summary', 'actions')
97
         sequence = ('name', 'option_summary', 'actions')
97
         per_page = settings.OSCAR_DASHBOARD_ITEMS_PER_PAGE
98
         per_page = settings.OSCAR_DASHBOARD_ITEMS_PER_PAGE
99
+
100
+
101
+class OptionTable(DashboardTable):
102
+    name = TemplateColumn(
103
+        verbose_name=_('Name'),
104
+        template_name='dashboard/catalogue/option_row_name.html',
105
+        order_by='name')
106
+    actions = TemplateColumn(
107
+        verbose_name=_('Actions'),
108
+        template_name='dashboard/catalogue/option_row_actions.html',
109
+        orderable=False)
110
+
111
+    icon = "reorder"
112
+    caption = ungettext_lazy("%s Option", "%s Options")
113
+
114
+    class Meta(DashboardTable.Meta):
115
+        model = Option
116
+        fields = ('name', 'type')
117
+        sequence = ('name', 'type', 'actions')
118
+        per_page = settings.OSCAR_DASHBOARD_ITEMS_PER_PAGE

+ 98
- 4
src/oscar/apps/dashboard/catalogue/views.py Ver fichero

12
 from oscar.core.loading import get_classes, get_model
12
 from oscar.core.loading import get_classes, get_model
13
 from oscar.views.generic import ObjectLookupView
13
 from oscar.views.generic import ObjectLookupView
14
 
14
 
15
+
15
 (ProductForm,
16
 (ProductForm,
16
  ProductClassSelectForm,
17
  ProductClassSelectForm,
17
  ProductSearchForm,
18
  ProductSearchForm,
18
  ProductClassForm,
19
  ProductClassForm,
19
  CategoryForm,
20
  CategoryForm,
20
  StockAlertSearchForm,
21
  StockAlertSearchForm,
21
- AttributeOptionGroupForm) \
22
+ AttributeOptionGroupForm,
23
+ OptionForm) \
22
     = get_classes('dashboard.catalogue.forms',
24
     = get_classes('dashboard.catalogue.forms',
23
                   ('ProductForm',
25
                   ('ProductForm',
24
                    'ProductClassSelectForm',
26
                    'ProductClassSelectForm',
26
                    'ProductClassForm',
28
                    'ProductClassForm',
27
                    'CategoryForm',
29
                    'CategoryForm',
28
                    'StockAlertSearchForm',
30
                    'StockAlertSearchForm',
29
-                   'AttributeOptionGroupForm'))
31
+                   'AttributeOptionGroupForm',
32
+                   'OptionForm'))
30
 (StockRecordFormSet,
33
 (StockRecordFormSet,
31
  ProductCategoryFormSet,
34
  ProductCategoryFormSet,
32
  ProductImageFormSet,
35
  ProductImageFormSet,
40
                    'ProductRecommendationFormSet',
43
                    'ProductRecommendationFormSet',
41
                    'ProductAttributesFormSet',
44
                    'ProductAttributesFormSet',
42
                    'AttributeOptionFormSet'))
45
                    'AttributeOptionFormSet'))
43
-ProductTable, CategoryTable, AttributeOptionGroupTable \
46
+ProductTable, CategoryTable, AttributeOptionGroupTable, OptionTable \
44
     = get_classes('dashboard.catalogue.tables',
47
     = get_classes('dashboard.catalogue.tables',
45
                   ('ProductTable', 'CategoryTable',
48
                   ('ProductTable', 'CategoryTable',
46
-                   'AttributeOptionGroupTable'))
49
+                   'AttributeOptionGroupTable', 'OptionTable'))
47
 (PopUpWindowCreateMixin,
50
 (PopUpWindowCreateMixin,
48
  PopUpWindowUpdateMixin,
51
  PopUpWindowUpdateMixin,
49
  PopUpWindowDeleteMixin) \
52
  PopUpWindowDeleteMixin) \
60
 StockAlert = get_model('partner', 'StockAlert')
63
 StockAlert = get_model('partner', 'StockAlert')
61
 Partner = get_model('partner', 'Partner')
64
 Partner = get_model('partner', 'Partner')
62
 AttributeOptionGroup = get_model('catalogue', 'AttributeOptionGroup')
65
 AttributeOptionGroup = get_model('catalogue', 'AttributeOptionGroup')
66
+Option = get_model('catalogue', 'Option')
63
 
67
 
64
 
68
 
65
 def filter_products(queryset, user):
69
 def filter_products(queryset, user):
915
             messages.info(self.request, _("Attribute Option Group deleted successfully"))
919
             messages.info(self.request, _("Attribute Option Group deleted successfully"))
916
         url = reverse("dashboard:catalogue-attribute-option-group-list")
920
         url = reverse("dashboard:catalogue-attribute-option-group-list")
917
         return self.get_url_with_querystring(url)
921
         return self.get_url_with_querystring(url)
922
+
923
+
924
+class OptionListView(SingleTableView):
925
+
926
+    template_name = 'dashboard/catalogue/option_list.html'
927
+    model = Option
928
+    table_class = OptionTable
929
+    context_table_name = 'options'
930
+
931
+
932
+class OptionCreateUpdateView(generic.UpdateView):
933
+
934
+    template_name = 'dashboard/catalogue/option_form.html'
935
+    model = Option
936
+    form_class = OptionForm
937
+
938
+    def forms_invalid(self, *args, **kwargs):
939
+        messages.error(
940
+            self.request,
941
+            _("Your submitted data was not valid - please correct the errors below")
942
+        )
943
+        return super().form_invalid(*args, **kwargs)
944
+
945
+    def get_context_data(self, **kwargs):
946
+        ctx = super().get_context_data(**kwargs)
947
+        ctx["title"] = self.get_title()
948
+        return ctx
949
+
950
+
951
+class OptionCreateView(OptionCreateUpdateView):
952
+
953
+    def get_object(self):
954
+        return None
955
+
956
+    def get_title(self):
957
+        return _("Add a new Option")
958
+
959
+    def get_success_url(self):
960
+        messages.info(self.request, _("Option created successfully"))
961
+        url = reverse("dashboard:catalogue-option-list")
962
+        return url
963
+
964
+
965
+class OptionUpdateView(OptionCreateUpdateView):
966
+
967
+    def get_object(self):
968
+        attribute_option_group = get_object_or_404(Option, pk=self.kwargs['pk'])
969
+        return attribute_option_group
970
+
971
+    def get_title(self):
972
+        return _("Update Option '%s'") % self.object.name
973
+
974
+    def get_success_url(self):
975
+        messages.info(self.request, _("Option updated successfully"))
976
+        url = reverse("dashboard:catalogue-option-list")
977
+        return url
978
+
979
+
980
+class OptionDeleteView(generic.DeleteView):
981
+
982
+    template_name = 'dashboard/catalogue/option_delete.html'
983
+    model = Option
984
+
985
+    def get_context_data(self, **kwargs):
986
+        ctx = super().get_context_data(**kwargs)
987
+
988
+        ctx['title'] = _("Delete Option '%s'") % self.object.name
989
+
990
+        products = self.object.product_set.count()
991
+        product_classes = self.object.productclass_set.count()
992
+        if any([products, product_classes]):
993
+            ctx['disallow'] = True
994
+            ctx['title'] = _("Unable to delete '%s'") % self.object.name
995
+            if products:
996
+                messages.error(
997
+                    self.request,
998
+                    _("%i products are still assigned to this option") % products
999
+                )
1000
+            if product_classes:
1001
+                messages.error(
1002
+                    self.request,
1003
+                    _("%i product classes are still assigned to this option") % product_classes
1004
+                )
1005
+
1006
+        return ctx
1007
+
1008
+    def get_success_url(self):
1009
+        messages.info(self.request, _("Option deleted successfully"))
1010
+        url = reverse("dashboard:catalogue-option-list")
1011
+        return url

+ 4
- 0
src/oscar/defaults.py Ver fichero

124
                 'label': _('Low stock alerts'),
124
                 'label': _('Low stock alerts'),
125
                 'url_name': 'dashboard:stock-alert-list',
125
                 'url_name': 'dashboard:stock-alert-list',
126
             },
126
             },
127
+            {
128
+                'label': _('Options'),
129
+                'url_name': 'dashboard:catalogue-option-list',
130
+            },
127
         ]
131
         ]
128
     },
132
     },
129
     {
133
     {

+ 51
- 0
src/oscar/templates/oscar/dashboard/catalogue/option_delete.html Ver fichero

1
+{% extends 'dashboard/layout.html' %}
2
+{% load i18n %}
3
+
4
+{% block title %}
5
+    {{ title }} | {% trans "Create Option" %} | {{ block.super }}
6
+{% endblock %}
7
+
8
+{% block body_class %}{{ block.super }} create-page{% endblock %}
9
+
10
+{% block breadcrumbs %}
11
+    <ul class="breadcrumb">
12
+        <li>
13
+            <a href="{% url 'dashboard:index' %}">{% trans "Dashboard" %}</a>
14
+        </li>
15
+        <li>
16
+            <a href="{% url 'dashboard:catalogue-option-list' %}">{% trans "Options" %}</a>
17
+        </li>
18
+        <li class="active">{% trans "Delete Option?" %}</li>
19
+    </ul>
20
+{% endblock %}
21
+
22
+{% block headertext %}
23
+    {{ title }}
24
+{% endblock %}
25
+
26
+{% block dashboard_content %}
27
+    {% if disallow %}
28
+        <p>{% trans "Please make sure option is not assigned to Products or Product Classes before deletion." %}</p>
29
+    {% else %}
30
+        <div class="table-header">
31
+            <h2>{% trans "Delete Option" %}</h2>
32
+        </div>
33
+        <form action="." method="post" class="well">
34
+            {% csrf_token %}
35
+            {% for key, value in http_get_params.items %}
36
+                <input type="hidden" name="{{ key }}" value="{{ value }}" />
37
+            {% endfor %}
38
+            <p>
39
+                {% blocktrans with name=object.name %}Delete Option <strong>{{ name }}</strong> - are you sure?{% endblocktrans %}
40
+            </p>
41
+            <div class="form-actions">
42
+                <a href="{% url 'dashboard:catalogue-option-list' %}">
43
+                    {% trans "Cancel" %}
44
+                </a>
45
+                <button type="submit" class="btn btn-danger" data-loading-text="{% trans 'Deleting...' %}">
46
+                    {% trans "Delete" %}
47
+                </button>
48
+            </div>
49
+        </form>
50
+    {% endif %}
51
+{% endblock %}

+ 77
- 0
src/oscar/templates/oscar/dashboard/catalogue/option_form.html Ver fichero

1
+{% extends 'dashboard/layout.html' %}
2
+{% load i18n %}
3
+
4
+{% block title %}
5
+    {{ title }} | {{ block.super }}
6
+{% endblock %}
7
+
8
+{% block body_class %}{{ block.super }} create-page{% endblock %}
9
+
10
+
11
+{% block breadcrumbs %}
12
+    <ul class="breadcrumb">
13
+        <li>
14
+            <a href="{% url 'dashboard:index' %}">{% trans "Dashboard" %}</a>
15
+        </li>
16
+        <li>
17
+            <a href="{% url 'dashboard:catalogue-option-list' %}">{% trans "Options" %}</a>
18
+        </li>
19
+        <li class="active">{{ title }}</li>
20
+    </ul>
21
+{% endblock %}
22
+
23
+{% block headertext %}{{ title }}{% endblock %}
24
+
25
+{% block dashboard_content %}
26
+    <form class="form-stacked wysiwyg fixed-actions" method="post" data-behaviour="affix-nav-errors">
27
+        {% csrf_token %}
28
+
29
+        {% block option_details %}
30
+            <div id="option_details">
31
+                <div class="table-header">
32
+                    <h3>{% trans "Option details" %}</h3>
33
+                </div>
34
+                <div class="well option-details">
35
+
36
+                    {% comment %}
37
+                        If the Option form has field_errors, partials/form_fields.html
38
+                        will render an error message.
39
+                        This means that there'll be 2 error messages,
40
+                        one from the partial and one from the view. Perhaps there should be
41
+                        an option allowing hiding of the error message in the template.
42
+                        For now we copy paste what we need from the template.
43
+                    {% endcomment %}
44
+
45
+                    {% if form.non_field_errors %}
46
+                        {% for error in form.non_field_errors %}
47
+                            <div class="alert alert-error control-group error">
48
+                                <span class="error-block"><i class="icon-exclamation-sign"></i> {{ error }}</span>
49
+                            </div>
50
+                        {% endfor %}
51
+                    {% endif %}
52
+
53
+                    {% for field in form %}
54
+                        {% include 'dashboard/partials/form_field.html' with field=field %}
55
+                    {% endfor %}
56
+                </div>
57
+            </div>
58
+        {% endblock %}
59
+
60
+        {% block fixed_actions_group %}
61
+            <div class="fixed-actions-group">
62
+                <div class="form-actions">
63
+                    <div class="pull-right">
64
+                        <a href="#" onclick="window.history.go(-1);return false">
65
+                            {% trans "Cancel" %}
66
+                        </a>
67
+                        {% trans "or" %}
68
+                        <button class="btn btn-large btn-primary" type="submit">
69
+                            {% trans "Save" %}
70
+                        </button>
71
+                    </div>
72
+                </div>
73
+            </div>
74
+        {% endblock fixed_actions_group %}
75
+    </form>
76
+
77
+{% endblock dashboard_content %}

+ 36
- 0
src/oscar/templates/oscar/dashboard/catalogue/option_list.html Ver fichero

1
+{% extends 'dashboard/layout.html' %}
2
+{% load i18n %}
3
+{% load render_table from django_tables2 %}
4
+
5
+{% block title %}
6
+    {% trans "Options" %} | {{ block.super }}
7
+{% endblock %}
8
+
9
+{% block breadcrumbs %}
10
+    <ul class="breadcrumb">
11
+        <li>
12
+            <a href="{% url 'dashboard:index' %}">{% trans "Dashboard" %}</a>
13
+        </li>
14
+        <li class="active">{% trans "Options" %}</li>
15
+    </ul>
16
+{% endblock %}
17
+
18
+{% block header %}
19
+    <div class="page-header">
20
+        <a href="{% url 'dashboard:catalogue-option-create' %}" class="btn btn-primary btn-lg pull-right"><i class="icon-plus"></i> {% trans "Create new Option" %}</a>
21
+        <h1>{% trans "Option" %}</h1>
22
+    </div>
23
+{% endblock header %}
24
+
25
+{% block dashboard_content %}
26
+    {% if options %}
27
+        {% block product_list %}
28
+            <form action="." method="post">
29
+                {% csrf_token %}
30
+                {% render_table options %}
31
+            </form>
32
+        {% endblock product_list %}
33
+    {% else %}
34
+        <p>{% trans "No Option found." %}</p>
35
+    {% endif %}
36
+{% endblock dashboard_content %}

+ 18
- 0
src/oscar/templates/oscar/dashboard/catalogue/option_row_actions.html Ver fichero

1
+{% load django_tables2 %}
2
+{% load i18n %}
3
+<div class="btn-toolbar">
4
+    <div class="btn-group">
5
+        <button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="true">
6
+            {% trans "Actions" %}
7
+            <span class="caret"></span>
8
+        </button>
9
+        <ul class="dropdown-menu pull-right">
10
+            <li>
11
+                <a href="{% url 'dashboard:catalogue-option-update' pk=record.id %}{% querystring %}">{% trans "Edit Option" %}</a>
12
+            </li>
13
+            <li>
14
+                <a href="{% url 'dashboard:catalogue-option-delete' pk=record.id %}{% querystring %}">{% trans "Delete" %}</a>
15
+            </li>
16
+        </ul>
17
+    </div>
18
+</div>

+ 2
- 0
src/oscar/templates/oscar/dashboard/catalogue/option_row_name.html Ver fichero

1
+{% load django_tables2 %}
2
+<a href="{% url 'dashboard:catalogue-option-update' pk=record.pk %}{% querystring %}">{{ value }}</a>

+ 271
- 0
tests/functional/dashboard/test_catalogue_options.py Ver fichero

1
+from http import client as http_client
2
+
3
+from django.conf import settings
4
+from django.contrib.messages import ERROR, INFO
5
+from django.urls import reverse
6
+from django.utils.text import slugify
7
+from django.utils.translation import gettext
8
+
9
+from oscar.core.loading import get_class, get_model
10
+from oscar.test.factories import (
11
+    OptionFactory, ProductClassFactory, create_product)
12
+from oscar.test.testcases import WebTestCase
13
+
14
+
15
+Option = get_model('catalogue', 'Option')
16
+OptionForm = get_class('dashboard.catalogue.forms', 'OptionForm')
17
+
18
+
19
+class OptionCreateMixin(object):
20
+
21
+    def _set_up_display_create_form_vars(self):
22
+        self.url_name = 'dashboard:catalogue-option-create'
23
+        self.title = gettext("Add a new Option")
24
+
25
+    def _test_display_create_form_response(self):
26
+        response = self.response
27
+
28
+        self.assertEqual(response.status_code, http_client.OK)
29
+        self.assertTemplateUsed(response, 'dashboard/catalogue/option_form.html')
30
+        self.assertInContext(response, 'form')
31
+        self.assertIsInstance(response.context['form'], OptionForm)
32
+        self.assertTrue(response.context['form'].instance._state.adding)
33
+        self.assertInContext(response, 'title')
34
+        self.assertEqual(response.context['title'], self.title)
35
+
36
+    def _set_up_create_vars(self):
37
+        self.url_name = 'dashboard:catalogue-option-create'
38
+        self.option_name = 'Test Option'
39
+
40
+    def _set_up_create_success_vars(self):
41
+        self.success_url_name = 'dashboard:catalogue-option-list'
42
+        self.success_message = gettext("Option created successfully")
43
+
44
+    def _test_creation_of_objects(self):
45
+        # Test the creation of the option
46
+        self.assertEqual(1, Option.objects.all().count())
47
+        option = Option.objects.first()
48
+        self.assertEqual(option.name, self.option_name)
49
+
50
+
51
+class OptionUpdateMixin(object):
52
+
53
+    def _set_up_display_update_form_vars(self):
54
+        url_name = 'dashboard:catalogue-option-update'
55
+        self.url = reverse(url_name, kwargs={'pk': self.option.pk})
56
+        self.title = gettext("Update Option '%s'") % self.option.name
57
+
58
+    def _test_display_update_form_response(self):
59
+        response = self.response
60
+
61
+        self.assertEqual(response.status_code, http_client.OK)
62
+        self.assertTemplateUsed(response, 'dashboard/catalogue/option_form.html')
63
+        self.assertInContext(response, 'form')
64
+        self.assertIsInstance(response.context['form'], OptionForm)
65
+        self.assertEqual(response.context['form'].instance, self.option)
66
+        self.assertInContext(response, 'title')
67
+        self.assertEqual(response.context['title'], self.title)
68
+
69
+    def _set_up_update_vars(self):
70
+        url_name = 'dashboard:catalogue-option-update'
71
+        self.url = reverse(url_name, kwargs={'pk': self.option.pk})
72
+        self.option_name = 'Test Option'
73
+
74
+    def _set_up_update_success_vars(self):
75
+        self.success_url_name = 'dashboard:catalogue-option-list'
76
+        self.success_message = gettext("Option updated successfully")
77
+
78
+    def _test_update_of_objects(self):
79
+        # Test the update of the option
80
+        option = Option.objects.first()
81
+        self.assertEqual(option.name, self.option_name)
82
+
83
+
84
+class OptionDeleteMixin(object):
85
+
86
+    def _set_up_display_delete_form_vars(self):
87
+        url_name = 'dashboard:catalogue-option-delete'
88
+        self.url = reverse(url_name, kwargs={'pk': self.option.pk})
89
+
90
+    def _set_up_display_delete_form_allowed_vars(self):
91
+        self.title = gettext("Delete Option '%s'") % self.option.name
92
+
93
+    def _set_up_display_delete_form_disallowed_objects(self):
94
+        product_class = ProductClassFactory()
95
+        product = create_product(product_class=product_class)
96
+        product_class.options.add(self.option)
97
+        product.product_options.add(self.option)
98
+
99
+    def _set_up_display_delete_form_disallowed_vars(self):
100
+        self.title = gettext("Unable to delete '%s'") % self.option.name
101
+        self.error_product_message = gettext("1 products are still assigned to this option")
102
+        self.error_class_message = gettext("1 product classes are still assigned to this option")
103
+
104
+    def _test_display_delete_form_response(self):
105
+        response = self.response
106
+
107
+        self.assertEqual(response.status_code, http_client.OK)
108
+        self.assertTemplateUsed(response, 'dashboard/catalogue/option_delete.html')
109
+        self.assertInContext(response, 'title')
110
+        self.assertEqual(response.context['title'], self.title)
111
+
112
+    def _test_display_delete_disallowed_response(self):
113
+        response = self.response
114
+
115
+        self.assertInContext(response, 'disallow')
116
+        self.assertTrue(response.context['disallow'])
117
+        messages = list(response.context['messages'])
118
+        self.assertEqual(len(messages), 2)
119
+        self.assertEqual(messages[0].level, ERROR)
120
+        self.assertEqual(messages[1].level, ERROR)
121
+        self.assertEqual(messages[0].message, self.error_product_message)
122
+        self.assertEqual(messages[1].message, self.error_class_message)
123
+
124
+    def _set_up_delete_vars(self):
125
+        url_name = 'dashboard:catalogue-option-delete'
126
+        self.url = reverse(url_name, kwargs={'pk': self.option.pk})
127
+
128
+    def _set_up_delete_success_vars(self):
129
+        self.success_url_name = 'dashboard:catalogue-option-list'
130
+        self.success_message = gettext("Option deleted successfully")
131
+
132
+    def _test_deletion_of_objects(self):
133
+        # Test the deletion of the option
134
+        option_exists = Option.objects.exists()
135
+        self.assertFalse(option_exists)
136
+
137
+
138
+class TestResponseOptionMixin(object):
139
+
140
+    def _test_success_response(self):
141
+        response = self.response
142
+
143
+        self.assertEqual(response.status_code, http_client.FOUND)
144
+        self.assertRedirectsTo(response, self.success_url_name)
145
+        messages = list(response.follow().context['messages'])
146
+        self.assertEqual(len(messages), 1)
147
+        self.assertEqual(messages[0].level, INFO)
148
+        self.assertEqual(messages[0].message, self.success_message)
149
+
150
+
151
+class TestOptionCreateView(OptionCreateMixin,
152
+                           TestResponseOptionMixin,
153
+                           WebTestCase):
154
+    is_staff = True
155
+
156
+    def test_display_create_form(self):
157
+        self._set_up_display_create_form_vars()
158
+
159
+        self.response = self.get(reverse(self.url_name))
160
+
161
+        # Test the response
162
+        self._test_display_create_form_response()
163
+
164
+    def test_create_option(self):
165
+        self._set_up_create_vars()
166
+        self._set_up_create_success_vars()
167
+
168
+        form = self.get(reverse(self.url_name)).form
169
+        form['name'] = self.option_name
170
+        self.response = form.submit()
171
+
172
+        # Test the creation of the option
173
+        self._test_creation_of_objects()
174
+
175
+        # Test the response
176
+        self._test_success_response()
177
+
178
+
179
+class TestOptionUpdateView(OptionUpdateMixin,
180
+                           TestResponseOptionMixin,
181
+                           WebTestCase):
182
+    is_staff = True
183
+
184
+    def setUp(self):
185
+        super().setUp()
186
+
187
+        self.option = OptionFactory()
188
+
189
+    def test_display_update_form(self):
190
+        self._set_up_display_update_form_vars()
191
+
192
+        self.response = self.get(self.url)
193
+
194
+        # Test the response
195
+        self._test_display_update_form_response()
196
+
197
+    def test_update_option(self):
198
+        self._set_up_update_vars()
199
+        self._set_up_update_success_vars()
200
+
201
+        form = self.get(self.url).form
202
+        form['name'] = self.option_name
203
+        self.response = form.submit()
204
+
205
+        # Test the update of the attribute option
206
+        self._test_update_of_objects()
207
+
208
+        # Test the response
209
+        self._test_success_response()
210
+
211
+
212
+class TestOptionListView(WebTestCase):
213
+    is_staff = True
214
+
215
+    def test_display_pagination_navigation(self):
216
+        url_name = 'dashboard:catalogue-option-list'
217
+        per_page = settings.OSCAR_DASHBOARD_ITEMS_PER_PAGE
218
+        option_name = 'Test Option #%d'
219
+
220
+        for i in range(0, int(1.5 * per_page)):
221
+            name = option_name % i
222
+            OptionFactory(name=name, code=slugify(name))
223
+
224
+        page = self.get(reverse(url_name))
225
+
226
+        # Test the pagination
227
+        self.assertContains(page, 'Page 1 of 2')
228
+
229
+
230
+class TestOptionDeleteView(OptionDeleteMixin,
231
+                           TestResponseOptionMixin,
232
+                           WebTestCase):
233
+    is_staff = True
234
+
235
+    def setUp(self):
236
+        super().setUp()
237
+
238
+        self.option = OptionFactory()
239
+
240
+    def test_display_delete_form(self):
241
+        self._set_up_display_delete_form_vars()
242
+        self._set_up_display_delete_form_allowed_vars()
243
+
244
+        self.response = self.get(self.url)
245
+
246
+        # Test the response
247
+        self._test_display_delete_form_response()
248
+
249
+    def test_display_disallowed_delete_via_regular_window(self):
250
+        self._set_up_display_delete_form_vars()
251
+        self._set_up_display_delete_form_disallowed_vars()
252
+        self._set_up_display_delete_form_disallowed_objects()
253
+
254
+        self.response = self.get(self.url)
255
+
256
+        # Test the response
257
+        self._test_display_delete_form_response()
258
+        self._test_display_delete_disallowed_response()
259
+
260
+    def test_delete_option(self):
261
+        self._set_up_delete_vars()
262
+        self._set_up_delete_success_vars()
263
+
264
+        form = self.get(self.url).form
265
+        self.response = form.submit()
266
+
267
+        # Test the deletion of the option
268
+        self._test_deletion_of_objects()
269
+
270
+        # Test the response
271
+        self._test_success_response()

Loading…
Cancelar
Guardar