Skip to content

Commit 2df791a

Browse files
authored
add tests, github actions (#1)
1 parent 1a6c98b commit 2df791a

21 files changed

+450
-1
lines changed

Diff for: .github/workflows/ruff.yml

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Ruff Linter
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches:
7+
- main
8+
tags-ignore:
9+
- '**'
10+
11+
jobs:
12+
build:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v4
16+
- name: Install Python
17+
uses: actions/setup-python@v5
18+
with:
19+
python-version: "3.11"
20+
- name: Install dependencies
21+
run: |
22+
python -m pip install --upgrade pip
23+
pip install -r requirements-dev.txt
24+
pip install ruff
25+
- name: Run Ruff
26+
run: ruff check --output-format=github .

Diff for: .github/workflows/test.yml

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: Tests
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches:
7+
- main
8+
tags-ignore:
9+
- '**'
10+
11+
jobs:
12+
build:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v4
16+
- name: Install Python
17+
uses: actions/setup-python@v5
18+
with:
19+
python-version: "3.11"
20+
- name: Install dependencies
21+
run: |
22+
python -m pip install --upgrade pip
23+
pip install -r requirements-dev.txt
24+
pip install codecov
25+
- name: Run Tests with coverage
26+
env:
27+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
28+
run: |
29+
pytest -s --cov=src/dalf --cov-report=xml tests/testproject
30+
codecov -f coverage.xml
31+
32+
- name: Upload coverage to Codecov
33+
uses: codecov/[email protected]
34+
with:
35+
token: ${{ secrets.CODECOV_TOKEN }}
36+
files: ./coverage.xml
37+
flags: unittests

Diff for: README.md

+7
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
[![Ruff](https://img.shields.io/endpoint?style=for-the-badge&url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
55
[![PyPI version](https://img.shields.io/pypi/v/dalf.svg?style=for-the-badge)](https://pypi.org/project/dalf/)
66
![PyPI - Downloads](https://img.shields.io/pypi/dm/dalf?style=for-the-badge)
7+
[![Codecov](https://codecov.io/gh/vigo/django-admin-list-filter/graph/badge.svg?token=6JRNSB6WN1)](https://codecov.io/gh/vigo/django-admin-list-filter)
8+
79

810
# Django Admin List Filter
911

@@ -216,6 +218,7 @@ rake -T
216218
rake build # Build package
217219
rake bump[revision] # Bump version: major,minor,patch
218220
rake clean # Remove/Delete build..
221+
rake test # Run tests
219222
rake upload:main # Upload package to main distro (release)
220223
rake upload:test # Upload package to test distro
221224
```
@@ -224,6 +227,10 @@ rake upload:test # Upload package to test distro
224227

225228
## Change Log
226229

230+
**2024-05-23**
231+
232+
- Add tests
233+
227234
**2024-05-20**
228235

229236
- Initial release.

Diff for: Rakefile

+7
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,10 @@ task :bump, [:revision] do |t, args|
3333
abort "Please provide valid revision: #{AVAILABLE_REVISIONS.join(',')}" unless AVAILABLE_REVISIONS.include?(args.revision)
3434
system "bumpversion #{args.revision}"
3535
end
36+
37+
desc "Run tests"
38+
task :test do
39+
system %{
40+
pytest -s --cov=src/dalf --cov-report=xml tests/testproject
41+
}
42+
end

Diff for: pyproject.toml

+7-1
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ version = "0.1.0"
44
authors = [
55
{ name="Uğur Özyılmazel", email="[email protected]" },
66
]
7-
description = "Django admin list filter with goodies"
7+
description = "Dead simple autocompletion for Django admin list_filter with goodies."
88
readme = "README.md"
9+
license = { file = "LICENSE" }
910
requires-python = ">=3.11"
1011
classifiers = [
1112
"Programming Language :: Python :: 3",
@@ -14,6 +15,11 @@ classifiers = [
1415
"Framework :: Django :: 5.0",
1516
"Development Status :: 3 - Alpha",
1617
]
18+
keywords = ["django", "django admin", "list filter"]
19+
20+
[project.optional-dependencies]
21+
build = ["build", "twine"]
22+
dev = ["Django", "pytest", "pytest-django", "pytest-factoryboy", "pytest-cov"]
1723

1824
[project.urls]
1925
Homepage = "https://github.com/vigo/django-admin-list-filter"

Diff for: requirements-dev.txt

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Django==5.0.6
2+
pytest==8.2.1
3+
pytest-cov==5.0.0
4+
pytest-django==4.8.0
5+
pytest-factoryboy==2.7.0

Diff for: tests/testproject/manage.py

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/env python
2+
# ruff: noqa: TRY003,EM101
3+
4+
import os
5+
import sys
6+
7+
8+
def main():
9+
"""Run administrative tasks."""
10+
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testproject.settings')
11+
try:
12+
from django.core.management import execute_from_command_line
13+
except ImportError as exc:
14+
raise ImportError(
15+
"Couldn't import Django. Are you sure it's installed and "
16+
'available on your PYTHONPATH environment variable? Did you '
17+
'forget to activate a virtual environment?'
18+
) from exc
19+
execute_from_command_line(sys.argv)
20+
21+
22+
if __name__ == '__main__':
23+
main()

Diff for: tests/testproject/pytest.ini

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[pytest]
2+
DJANGO_SETTINGS_MODULE = testproject.settings
3+
python_files = tests.py test_*.py *_tests.py
4+
pythonpath = ../../src
5+
addopts = -p no:warnings --strict-markers --no-migrations --reuse-db --capture=no

Diff for: tests/testproject/testapp/__init__.py

Whitespace-only changes.

Diff for: tests/testproject/testapp/admin.py

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from dalf.admin import (
2+
DALFChoicesField,
3+
DALFModelAdmin,
4+
DALFRelatedField,
5+
DALFRelatedFieldAjax,
6+
DALFRelatedOnlyField,
7+
)
8+
from django.contrib import admin
9+
10+
from .models import Category, Post, Tag
11+
12+
13+
@admin.register(Post)
14+
class PostAdmin(DALFModelAdmin):
15+
list_display = ('title',)
16+
list_filter = (
17+
('author', DALFRelatedField),
18+
('audience', DALFChoicesField),
19+
('category', DALFRelatedFieldAjax),
20+
('tags', DALFRelatedOnlyField),
21+
)
22+
23+
24+
@admin.register(Category)
25+
class CategoryAdmin(admin.ModelAdmin):
26+
search_fields = ('name',)
27+
ordering = ('name',)
28+
29+
30+
@admin.register(Tag)
31+
class TagAdmin(admin.ModelAdmin):
32+
search_fields = ('name',)
33+
ordering = ('name',)

Diff for: tests/testproject/testapp/apps.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from django.apps import AppConfig
2+
3+
4+
class TestappConfig(AppConfig):
5+
default_auto_field = 'django.db.models.BigAutoField'
6+
name = 'testapp'

Diff for: tests/testproject/testapp/conftest.py

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import pytest
2+
from pytest_factoryboy import register
3+
4+
from .factories import PostFactory, TagFactory
5+
6+
register(TagFactory)
7+
register(PostFactory)
8+
9+
10+
@pytest.fixture()
11+
def posts():
12+
return PostFactory.create_batch(10)

Diff for: tests/testproject/testapp/factories.py

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import factory
2+
from django.contrib.auth import get_user_model
3+
from factory import fuzzy
4+
5+
from .models import AudienceChoices, Category, Post, Tag
6+
7+
FAKE_USERNAMES = [
8+
'vigo',
9+
'turbo',
10+
'move',
11+
]
12+
13+
FAKE_CATEGORIES = [
14+
'Python',
15+
'Ruby',
16+
'Go',
17+
'Bash',
18+
'AppleScript',
19+
'C',
20+
'Perl',
21+
]
22+
23+
FAKE_TAGS = [
24+
'django',
25+
'django rest',
26+
'linux',
27+
'macos',
28+
'stdlib',
29+
]
30+
31+
32+
class UserFactory(factory.django.DjangoModelFactory):
33+
class Meta:
34+
model = get_user_model()
35+
django_get_or_create = ('username',)
36+
37+
username = fuzzy.FuzzyChoice(FAKE_USERNAMES)
38+
email = factory.Faker('email')
39+
password = factory.PostGenerationMethodCall('set_password', 'defaultpassword')
40+
41+
42+
class CategoryFactory(factory.django.DjangoModelFactory):
43+
class Meta:
44+
model = Category
45+
django_get_or_create = ('name',)
46+
47+
name = factory.Iterator(FAKE_CATEGORIES)
48+
49+
50+
class TagFactory(factory.django.DjangoModelFactory):
51+
class Meta:
52+
model = Tag
53+
django_get_or_create = ('name',)
54+
55+
name = fuzzy.FuzzyChoice(FAKE_TAGS)
56+
57+
58+
class PostFactory(factory.django.DjangoModelFactory):
59+
class Meta:
60+
model = Post
61+
django_get_or_create = ('title',)
62+
63+
author = factory.SubFactory(UserFactory)
64+
category = factory.SubFactory(CategoryFactory)
65+
title = factory.Sequence(lambda n: f'Book about {FAKE_CATEGORIES[n % len(FAKE_CATEGORIES)]} - {n}')
66+
audience = fuzzy.FuzzyChoice(AudienceChoices.choices, getter=lambda c: c[0])
67+
68+
@factory.post_generation
69+
def tags(self, create, extracted, **kwargs): # noqa: ARG002
70+
if not create:
71+
return
72+
73+
if extracted:
74+
self.tags.add(*extracted)
75+
else:
76+
tags = TagFactory.create_batch(len(FAKE_TAGS))
77+
self.tags.add(*tags)

Diff for: tests/testproject/testapp/migrations/__init__.py

Whitespace-only changes.

Diff for: tests/testproject/testapp/models.py

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
from django.conf import settings
2+
from django.db import models
3+
4+
5+
class Category(models.Model):
6+
name = models.CharField(max_length=255)
7+
8+
def __str__(self):
9+
return self.name
10+
11+
12+
class Tag(models.Model):
13+
name = models.CharField(max_length=255)
14+
15+
def __str__(self):
16+
return self.name
17+
18+
19+
class AudienceChoices(models.TextChoices):
20+
BEGINNER = 'beginner', 'Beginer'
21+
INTERMEDIATE = 'intermediate', 'Intermediate'
22+
PRO = 'pro', 'Pro'
23+
24+
25+
class Post(models.Model):
26+
author = models.ForeignKey(
27+
to=settings.AUTH_USER_MODEL,
28+
on_delete=models.CASCADE,
29+
related_name='posts',
30+
)
31+
category = models.ForeignKey(
32+
to='Category',
33+
on_delete=models.CASCADE,
34+
related_name='posts',
35+
)
36+
tags = models.ManyToManyField(to='Tag', blank=True)
37+
audience = models.CharField(
38+
max_length=100,
39+
choices=AudienceChoices.choices,
40+
default=AudienceChoices.BEGINNER,
41+
)
42+
43+
title = models.CharField(max_length=255)
44+
45+
def __str__(self):
46+
return self.title

0 commit comments

Comments
 (0)