Skip to content

Commit 339e088

Browse files
authored
Merge pull request #93 from browniebroke/test-coverage-renderers
Increase test coverage for renderers.py
2 parents dd6a7d1 + d2d88ad commit 339e088

File tree

9 files changed

+168
-16
lines changed

9 files changed

+168
-16
lines changed

drf_excel/renderers.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,12 @@ def render(self, data, accepted_media_type=None, renderer_context=None):
5656
"""
5757
Render `data` into XLSX workbook, returning a workbook.
5858
"""
59-
if not self._check_validation_data(data):
60-
return json.dumps(data)
61-
6259
if data is None:
6360
return bytes()
6461

62+
if not self._check_validation_data(data):
63+
return json.dumps(data)
64+
6565
wb = Workbook()
6666
self.ws = wb.active
6767

tests/conftest.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
import io
2+
from typing import Union, Callable
3+
14
import pytest
5+
from openpyxl.reader.excel import load_workbook
26
from openpyxl.workbook import Workbook
37
from openpyxl.worksheet.worksheet import Worksheet
48

@@ -11,3 +15,12 @@ def workbook() -> Workbook:
1115
@pytest.fixture
1216
def worksheet(workbook: Workbook) -> Worksheet:
1317
return Worksheet(workbook)
18+
19+
20+
@pytest.fixture
21+
def workbook_reader() -> Callable[[Union[bytes, str]], Workbook]:
22+
def reader_func(buffer: Union[bytes, str]) -> Workbook:
23+
io_buffer = io.BytesIO(buffer)
24+
return load_workbook(io_buffer, read_only=True)
25+
26+
return reader_func

tests/test_renderers.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from PIL import Image
2+
from rest_framework import serializers
3+
from rest_framework.generics import GenericAPIView
4+
from rest_framework.response import Response
5+
6+
from drf_excel.renderers import XLSXRenderer
7+
8+
9+
class MySerializer(serializers.Serializer):
10+
title = serializers.CharField()
11+
12+
13+
class MyBaseView(GenericAPIView):
14+
serializer_class = MySerializer
15+
16+
def retrieve(self, request, *args, **kwargs):
17+
return Response({"title": "example"})
18+
19+
20+
class TestXLSXRenderer:
21+
renderer = XLSXRenderer()
22+
23+
def test_validation_error(self):
24+
assert self.renderer.render({"detail": "invalid"}) == '{"detail": "invalid"}'
25+
26+
def test_none(self):
27+
assert self.renderer.render(None) == b""
28+
29+
def test_with_header_attribute(self, tmp_path, workbook_reader):
30+
image_path = tmp_path / "image.png"
31+
with Image.new(mode="RGB", size=(100, 100), color="blue") as img:
32+
img.save(image_path, format="png")
33+
34+
class MyView(MyBaseView):
35+
header = {
36+
"use_header": True,
37+
"header_title": "My Header",
38+
"tab_title": "My Tab",
39+
"img": str(image_path),
40+
"style": {"font": {"name": "Arial"}},
41+
}
42+
43+
result = self.renderer.render({}, renderer_context={"view": MyView})
44+
wb = workbook_reader(result)
45+
sheet = wb.worksheets[0]
46+
rows = list(sheet.rows)
47+
assert len(rows) == 1
48+
row0_col0 = rows[0][0]
49+
assert row0_col0.value == "My Header"
50+
assert row0_col0.font.name == "Arial"

tests/test_viewset_mixin.py

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
1-
import io
1+
import datetime as dt
22

33
import pytest
4-
from openpyxl.reader.excel import load_workbook
54
from rest_framework.test import APIClient
5+
from time_machine import TimeMachineFixture
66

7-
from tests.testapp.models import ExampleModel
7+
from tests.testapp.models import ExampleModel, AllFieldsModel, Tag
8+
9+
pytestmark = pytest.mark.django_db
810

911

1012
@pytest.fixture
1113
def api_client():
1214
return APIClient()
1315

1416

15-
@pytest.mark.django_db
16-
def test_simple_viewset_model(api_client):
17+
def test_simple_viewset_model(api_client, workbook_reader):
1718
ExampleModel.objects.create(title="test 1", description="This is a test")
1819
ExampleModel.objects.create(title="test 2", description="Another test")
1920
ExampleModel.objects.create(title="test 3", description="Testing this out")
@@ -29,11 +30,10 @@ def test_simple_viewset_model(api_client):
2930
response.headers["content-disposition"] == "attachment; filename=my_export.xlsx"
3031
)
3132

32-
workbook_buffer = io.BytesIO(response.content)
33-
workbook = load_workbook(workbook_buffer, read_only=True)
33+
wb = workbook_reader(response.content)
3434

35-
assert len(workbook.worksheets) == 1
36-
sheet = workbook.worksheets[0]
35+
assert len(wb.worksheets) == 1
36+
sheet = wb.worksheets[0]
3737
rows = list(sheet.rows)
3838
assert len(rows) == 4
3939
r0, r1, r2, r3 = rows
@@ -53,3 +53,43 @@ def test_simple_viewset_model(api_client):
5353
assert len(r3) == 2
5454
assert r3[0].value == "test 3"
5555
assert r3[1].value == "Testing this out"
56+
57+
58+
def test_all_fields_viewset(
59+
api_client, time_machine: TimeMachineFixture, workbook_reader
60+
):
61+
time_machine.move_to(dt.datetime(2023, 9, 10, 15, 44, 37))
62+
instance = AllFieldsModel.objects.create(title="Hello", age=36, is_active=True)
63+
instance.tags.set(
64+
[
65+
Tag.objects.create(name="test"),
66+
Tag.objects.create(name="example"),
67+
]
68+
)
69+
response = api_client.get("/all-fields/")
70+
assert response.status_code == 200
71+
72+
wb = workbook_reader(response.content)
73+
sheet = wb.worksheets[0]
74+
rows = list(sheet.rows)
75+
assert len(rows) == 2
76+
r0, r1 = rows
77+
78+
assert [col.value for col in r0] == [
79+
"title",
80+
"created_at",
81+
"updated_date",
82+
"updated_time",
83+
"age",
84+
"is_active",
85+
"tags",
86+
]
87+
assert [col.value for col in r1] == [
88+
"Hello",
89+
dt.datetime(2023, 9, 10, 15, 44, 37),
90+
dt.datetime(2023, 9, 10, 0, 0),
91+
dt.time(15, 44, 37),
92+
36,
93+
True,
94+
"test, example",
95+
]

tests/testapp/models.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,26 @@ class ExampleModel(models.Model):
77

88
def __str__(self):
99
return self.title
10+
11+
12+
class Tag(models.Model):
13+
name = models.CharField(max_length=100)
14+
15+
def __str__(self):
16+
return self.name
17+
18+
19+
class AllFieldsModel(models.Model):
20+
title = models.CharField(max_length=100)
21+
created_at = models.DateTimeField(auto_now_add=True)
22+
updated_date = models.DateField(auto_now=True)
23+
updated_time = models.TimeField(auto_now=True)
24+
age = models.IntegerField()
25+
is_active = models.BooleanField(default=True)
26+
tags = models.ManyToManyField(Tag, related_name="all_fields")
27+
28+
def __str__(self):
29+
return self.title
30+
31+
def get_tag_names(self):
32+
return [tag.name for tag in self.tags.all()]

tests/testapp/serializers.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,25 @@
11
from rest_framework import serializers
22

3-
from .models import ExampleModel
3+
from .models import ExampleModel, AllFieldsModel
44

55

66
class ExampleSerializer(serializers.ModelSerializer):
77
class Meta:
88
model = ExampleModel
99
fields = ("title", "description")
10+
11+
12+
class AllFieldsSerializer(serializers.ModelSerializer):
13+
tags = serializers.ListField(source="get_tag_names")
14+
15+
class Meta:
16+
model = AllFieldsModel
17+
fields = (
18+
"title",
19+
"created_at",
20+
"updated_date",
21+
"updated_time",
22+
"age",
23+
"is_active",
24+
"tags",
25+
)

tests/testapp/views.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,19 @@
22
from drf_excel.mixins import XLSXFileMixin
33
from drf_excel.renderers import XLSXRenderer
44

5-
from .models import ExampleModel
6-
from .serializers import ExampleSerializer
5+
from .models import ExampleModel, AllFieldsModel
6+
from .serializers import ExampleSerializer, AllFieldsSerializer
77

88

99
class ExampleViewSet(XLSXFileMixin, ReadOnlyModelViewSet):
1010
queryset = ExampleModel.objects.all()
1111
serializer_class = ExampleSerializer
1212
renderer_classes = (XLSXRenderer,)
1313
filename = "my_export.xlsx"
14+
15+
16+
class AllFieldsViewSet(XLSXFileMixin, ReadOnlyModelViewSet):
17+
queryset = AllFieldsModel.objects.all()
18+
serializer_class = AllFieldsSerializer
19+
renderer_classes = (XLSXRenderer,)
20+
filename = "al_fileds.xlsx"

tests/urls.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from rest_framework import routers
22

3-
from .testapp.views import ExampleViewSet
3+
from .testapp.views import ExampleViewSet, AllFieldsViewSet
44

55
router = routers.SimpleRouter()
66
router.register(r"examples", ExampleViewSet)
7+
router.register(r"all-fields", AllFieldsViewSet)
78

89
urlpatterns = router.urls

tox.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ deps =
1313

1414
djangorestframework
1515
openpyxl
16+
Pillow
1617

1718
pytest
1819
pytest-django
1920
pytest-cov
2021
django-coverage-plugin
22+
time-machine
2123

2224
commands = {posargs:python -m pytest}

0 commit comments

Comments
 (0)