Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Increase test coverage for renderers.py #93

Merged
merged 2 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions drf_excel/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ def render(self, data, accepted_media_type=None, renderer_context=None):
"""
Render `data` into XLSX workbook, returning a workbook.
"""
if not self._check_validation_data(data):
return json.dumps(data)

if data is None:
return bytes()
Comment on lines 59 to 60
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code was unreachable, self._check_validation_data(data) raises an error if data is None


if not self._check_validation_data(data):
return json.dumps(data)

wb = Workbook()
self.ws = wb.active

Expand Down
13 changes: 13 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import io
from typing import Union, Callable

import pytest
from openpyxl.reader.excel import load_workbook
from openpyxl.workbook import Workbook
from openpyxl.worksheet.worksheet import Worksheet

Expand All @@ -11,3 +15,12 @@ def workbook() -> Workbook:
@pytest.fixture
def worksheet(workbook: Workbook) -> Worksheet:
return Worksheet(workbook)


@pytest.fixture
def workbook_reader() -> Callable[[Union[bytes, str]], Workbook]:
def reader_func(buffer: Union[bytes, str]) -> Workbook:
io_buffer = io.BytesIO(buffer)
return load_workbook(io_buffer, read_only=True)

return reader_func
50 changes: 50 additions & 0 deletions tests/test_renderers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from PIL import Image
from rest_framework import serializers
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response

from drf_excel.renderers import XLSXRenderer


class MySerializer(serializers.Serializer):
title = serializers.CharField()


class MyBaseView(GenericAPIView):
serializer_class = MySerializer

def retrieve(self, request, *args, **kwargs):
return Response({"title": "example"})


class TestXLSXRenderer:
renderer = XLSXRenderer()

def test_validation_error(self):
assert self.renderer.render({"detail": "invalid"}) == '{"detail": "invalid"}'

def test_none(self):
assert self.renderer.render(None) == b""

def test_with_header_attribute(self, tmp_path, workbook_reader):
image_path = tmp_path / "image.png"
with Image.new(mode="RGB", size=(100, 100), color="blue") as img:
img.save(image_path, format="png")

class MyView(MyBaseView):
header = {
"use_header": True,
"header_title": "My Header",
"tab_title": "My Tab",
"img": str(image_path),
"style": {"font": {"name": "Arial"}},
}

result = self.renderer.render({}, renderer_context={"view": MyView})
wb = workbook_reader(result)
sheet = wb.worksheets[0]
rows = list(sheet.rows)
assert len(rows) == 1
row0_col0 = rows[0][0]
assert row0_col0.value == "My Header"
assert row0_col0.font.name == "Arial"
58 changes: 49 additions & 9 deletions tests/test_viewset_mixin.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import io
import datetime as dt

import pytest
from openpyxl.reader.excel import load_workbook
from rest_framework.test import APIClient
from time_machine import TimeMachineFixture

from tests.testapp.models import ExampleModel
from tests.testapp.models import ExampleModel, AllFieldsModel, Tag

pytestmark = pytest.mark.django_db


@pytest.fixture
def api_client():
return APIClient()


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

workbook_buffer = io.BytesIO(response.content)
workbook = load_workbook(workbook_buffer, read_only=True)
wb = workbook_reader(response.content)

assert len(workbook.worksheets) == 1
sheet = workbook.worksheets[0]
assert len(wb.worksheets) == 1
sheet = wb.worksheets[0]
rows = list(sheet.rows)
assert len(rows) == 4
r0, r1, r2, r3 = rows
Expand All @@ -53,3 +53,43 @@ def test_simple_viewset_model(api_client):
assert len(r3) == 2
assert r3[0].value == "test 3"
assert r3[1].value == "Testing this out"


def test_all_fields_viewset(
api_client, time_machine: TimeMachineFixture, workbook_reader
):
time_machine.move_to(dt.datetime(2023, 9, 10, 15, 44, 37))
instance = AllFieldsModel.objects.create(title="Hello", age=36, is_active=True)
instance.tags.set(
[
Tag.objects.create(name="test"),
Tag.objects.create(name="example"),
]
)
response = api_client.get("/all-fields/")
assert response.status_code == 200

wb = workbook_reader(response.content)
sheet = wb.worksheets[0]
rows = list(sheet.rows)
assert len(rows) == 2
r0, r1 = rows

assert [col.value for col in r0] == [
"title",
"created_at",
"updated_date",
"updated_time",
"age",
"is_active",
"tags",
]
assert [col.value for col in r1] == [
"Hello",
dt.datetime(2023, 9, 10, 15, 44, 37),
dt.datetime(2023, 9, 10, 0, 0),
dt.time(15, 44, 37),
36,
True,
"test, example",
]
23 changes: 23 additions & 0 deletions tests/testapp/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,26 @@ class ExampleModel(models.Model):

def __str__(self):
return self.title


class Tag(models.Model):
name = models.CharField(max_length=100)

def __str__(self):
return self.name


class AllFieldsModel(models.Model):
title = models.CharField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
updated_date = models.DateField(auto_now=True)
updated_time = models.TimeField(auto_now=True)
age = models.IntegerField()
is_active = models.BooleanField(default=True)
tags = models.ManyToManyField(Tag, related_name="all_fields")

def __str__(self):
return self.title

def get_tag_names(self):
return [tag.name for tag in self.tags.all()]
18 changes: 17 additions & 1 deletion tests/testapp/serializers.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
from rest_framework import serializers

from .models import ExampleModel
from .models import ExampleModel, AllFieldsModel


class ExampleSerializer(serializers.ModelSerializer):
class Meta:
model = ExampleModel
fields = ("title", "description")


class AllFieldsSerializer(serializers.ModelSerializer):
tags = serializers.ListField(source="get_tag_names")

class Meta:
model = AllFieldsModel
fields = (
"title",
"created_at",
"updated_date",
"updated_time",
"age",
"is_active",
"tags",
)
11 changes: 9 additions & 2 deletions tests/testapp/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,19 @@
from drf_excel.mixins import XLSXFileMixin
from drf_excel.renderers import XLSXRenderer

from .models import ExampleModel
from .serializers import ExampleSerializer
from .models import ExampleModel, AllFieldsModel
from .serializers import ExampleSerializer, AllFieldsSerializer


class ExampleViewSet(XLSXFileMixin, ReadOnlyModelViewSet):
queryset = ExampleModel.objects.all()
serializer_class = ExampleSerializer
renderer_classes = (XLSXRenderer,)
filename = "my_export.xlsx"


class AllFieldsViewSet(XLSXFileMixin, ReadOnlyModelViewSet):
queryset = AllFieldsModel.objects.all()
serializer_class = AllFieldsSerializer
renderer_classes = (XLSXRenderer,)
filename = "al_fileds.xlsx"
3 changes: 2 additions & 1 deletion tests/urls.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from rest_framework import routers

from .testapp.views import ExampleViewSet
from .testapp.views import ExampleViewSet, AllFieldsViewSet

router = routers.SimpleRouter()
router.register(r"examples", ExampleViewSet)
router.register(r"all-fields", AllFieldsViewSet)

urlpatterns = router.urls
2 changes: 2 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ deps =

djangorestframework
openpyxl
Pillow

pytest
pytest-django
pytest-cov
django-coverage-plugin
time-machine

commands = {posargs:python -m pytest}