Add eslint, prettier, djhtml, pytest-style & django-upgrade

This commit is contained in:
Gabriel Augendre 2021-12-27 22:57:05 +01:00
parent 09f17bdef1
commit 1dbf04db66
25 changed files with 223 additions and 128 deletions

51
.eslintrc Normal file
View file

@ -0,0 +1,51 @@
{
"env": {
"browser": true,
"es6": true,
"jquery": true
},
"extends": [
"eslint:recommended"
],
"ignorePatterns": ["dist/", "node_modules/"],
"rules": {
"block-scoped-var": "error",
"consistent-return": "error",
"curly": "error",
"default-case": "error",
"default-param-last": ["error"],
"dot-notation": "error",
"eqeqeq": "error",
"guard-for-in": "error",
"max-classes-per-file": "error",
"no-alert": "error",
"no-caller": "error",
"no-else-return": "error",
"no-empty-function": "error",
"no-floating-decimal": "error",
"no-implicit-coercion": "error",
"no-multi-spaces": "error",
"no-multi-str": "error",
"no-param-reassign": "error",
"no-return-assign": "error",
"no-return-await": "error",
"no-self-compare": "error",
"no-throw-literal": "error",
"no-useless-concat": "error",
"radix": ["error", "as-needed"],
"require-await": "error",
"yoda": "error",
"no-shadow": "off",
"prefer-destructuring": ["error", { "array": false, "object": true }],
"padding-line-between-statements": [
"error",
{ "blankLine": "always", "prev": "import", "next": "export" },
{ "blankLine": "always", "prev": "export", "next": "export" },
{ "blankLine": "always", "prev": "*", "next": "return" }
]
},
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
}
}

View file

@ -6,3 +6,5 @@ ignore =
W503, W503,
# class member shadows builtin # class member shadows builtin
A003, A003,
max-complexity = 10
format = %(path)s:%(row)d:%(col)d: %(code)s %(text)s https://lintlyci.github.io/Flake8Rules/rules/%(code)s.html

View file

@ -1,4 +1,4 @@
exclude: \.min\.(js|css)(\.map)?$ exclude: (\.min\.(js|css)(\.map)?$|/vendor/)
repos: repos:
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.0.1 rev: v4.0.1
@ -34,6 +34,15 @@ repos:
- id: pyupgrade - id: pyupgrade
args: args:
- --py310-plus - --py310-plus
- repo: https://github.com/adamchainz/django-upgrade
rev: 1.4.0
hooks:
- id: django-upgrade
args: [--target-version, "4.0"]
- repo: https://github.com/rtts/djhtml
rev: v1.4.10
hooks:
- id: djhtml
- repo: https://github.com/pycqa/flake8 - repo: https://github.com/pycqa/flake8
rev: 4.0.1 rev: 4.0.1
hooks: hooks:
@ -44,3 +53,18 @@ repos:
- flake8-builtins - flake8-builtins
- flake8-comprehensions - flake8-comprehensions
- flake8-eradicate - flake8-eradicate
- flake8-pytest-style
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v2.5.1
hooks:
- id: prettier
types_or: [javascript, css]
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v8.4.1
hooks:
- id: eslint
args: [--fix]
types_or: [javascript, css]
additional_dependencies:
- eslint@^7.29.0
- eslint-config-prettier@^8.3.0

5
.prettierrc Normal file
View file

@ -0,0 +1,5 @@
{
"tabWidth": 4,
"printWidth": 120,
"endOfLine": "auto"
}

View file

@ -1,4 +1,5 @@
#id_content, #id_custom_css { #id_content,
#id_custom_css {
font-family: "JetBrains Mono", monospace; font-family: "JetBrains Mono", monospace;
min-height: 30em; min-height: 30em;
resize: vertical; resize: vertical;
@ -9,6 +10,7 @@
height: 38em; height: 38em;
} }
label[for=id_content], label[for=id_custom_css] { label[for="id_content"],
label[for="id_custom_css"] {
display: none; display: none;
} }

View file

@ -9,7 +9,7 @@
--warning-text: #856404; --warning-text: #856404;
} }
@media(prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
:root { :root {
--info-background: #0c5460; --info-background: #0c5460;
--info-text: #d1ecf1; --info-text: #d1ecf1;

View file

@ -2,7 +2,7 @@
font-size: 60%; font-size: 60%;
background-color: var(--nc-tx-2); background-color: var(--nc-tx-2);
color: var(--nc-bg-1); color: var(--nc-bg-1);
padding: .5ex 1ex; padding: 0.5ex 1ex;
border-radius: 1ex; border-radius: 1ex;
vertical-align: 15%; vertical-align: 15%;
text-decoration: none; text-decoration: none;

View file

@ -2,8 +2,8 @@
function addCopyCode() { function addCopyCode() {
const codeBlocks = document.querySelectorAll("pre"); const codeBlocks = document.querySelectorAll("pre");
codeBlocks.forEach(pre => { codeBlocks.forEach((pre) => {
pre.addEventListener("click", event => { pre.addEventListener("click", (event) => {
if (event.detail === 4) { if (event.detail === 4) {
const selection = window.getSelection(); const selection = window.getSelection();
selection.setBaseAndExtent( selection.setBaseAndExtent(
@ -18,7 +18,6 @@ function addCopyCode() {
}); });
} }
((readyState) => { ((readyState) => {
if (readyState === "interactive") { if (readyState === "interactive") {
addCopyCode(); addCopyCode();

View file

@ -1,4 +1,4 @@
'use strict'; "use strict";
function bindKey() { function bindKey() {
const adminLinkElement = document.querySelector("a#admin-link"); const adminLinkElement = document.querySelector("a#admin-link");
@ -10,7 +10,7 @@ function bindKey() {
if (event.code === "KeyE") { if (event.code === "KeyE") {
window.location = adminLocation; window.location = adminLocation;
} }
}) });
} }
((readyState) => { ((readyState) => {

View file

@ -1,6 +1,6 @@
let preview = null; let preview = null;
function onLoad () { function onLoad() {
const previewButton = document.querySelector("input#_live_preview"); const previewButton = document.querySelector("input#_live_preview");
if (previewButton) { if (previewButton) {
previewButton.addEventListener("click", openPreviewPopup); previewButton.addEventListener("click", openPreviewPopup);
@ -34,11 +34,11 @@ function openPreviewPopup(event) {
function loadPreview() { function loadPreview() {
const id = Number(window.location.pathname.match(/\d+/)[0]); const id = Number(window.location.pathname.match(/\d+/)[0]);
const body = prepareBody(); const body = prepareBody();
fetch(`/api/render/${id}/`, {method: "POST", body: body}) fetch(`/api/render/${id}/`, { method: "POST", body: body })
.then(response => { .then((response) => {
return response.text(); return response.text();
}) })
.then(value => { .then((value) => {
preview.document.open("text/html", "replace"); preview.document.open("text/html", "replace");
preview.document.write(value); preview.document.write(value);
preview.document.close(); preview.document.close();
@ -79,8 +79,11 @@ function prepareBody() {
const element = document.querySelector(input.selector); const element = document.querySelector(input.selector);
body.set(input.to, element[input.property]); body.set(input.to, element[input.property]);
} }
const tagIds = Array.from(document.querySelector("#id_tags").selectedOptions).map(option => option.value).join(); const tagIds = Array.from(document.querySelector("#id_tags").selectedOptions)
.map((option) => option.value)
.join();
body.set("tag_ids", tagIds); body.set("tag_ids", tagIds);
return body; return body;
} }
@ -105,8 +108,10 @@ function setupLivePreview() {
*/ */
function debounce(func, wait) { function debounce(func, wait) {
let timeout; let timeout;
return function () { return function () {
const context = this, args = arguments; const context = this,
args = arguments;
const later = function () { const later = function () {
timeout = null; timeout = null;
func.apply(context, args); func.apply(context, args);

View file

@ -1,18 +1,22 @@
.d-none { .d-none {
display: none !important; display: none !important;
} }
.float-right { .float-right {
float: right; float: right;
} }
td, th, tr, tbody, tr:nth-child(2n) { td,
background-color: inherit; th,
border: none; tr,
padding-left: 0; tbody,
padding-right: 0; tr:nth-child(2n) {
background-color: inherit;
border: none;
padding-left: 0;
padding-right: 0;
} }
img { img {
background-color: white; background-color: white;
} }

View file

@ -3,7 +3,9 @@ body {
max-width: 750px; max-width: 750px;
} }
h1, h2, h3 { h1,
h2,
h3 {
border-bottom: unset; border-bottom: unset;
} }
@ -20,8 +22,9 @@ footer > :first-child {
margin-top: 1em; margin-top: 1em;
} }
nav a:not(:first-child):before, a.tag:not(:first-of-type):before { nav a:not(:first-child):before,
content: '\00B7'; a.tag:not(:first-of-type):before {
content: "\00B7";
margin: 0 5px; margin: 0 5px;
color: var(--nc-tx-1); color: var(--nc-tx-1);
text-decoration: none; text-decoration: none;

View file

@ -1,42 +1,42 @@
{% load static %} {% load static %}
{% spaceless %} {% spaceless %}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="Gabriel Augendre"> <meta name="author" content="Gabriel Augendre">
<meta name="color-scheme" content="light dark"> <meta name="color-scheme" content="light dark">
<meta name="theme-color" content="#F6F8FA" media="(prefers-color-scheme: light)"> <meta name="theme-color" content="#F6F8FA" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#111111" media="(prefers-color-scheme: dark)"> <meta name="theme-color" content="#111111" media="(prefers-color-scheme: dark)">
<title>{% block title %}Home | {% endblock %}{{ blog_title }} by {{ blog_author }}</title> <title>{% block title %}Home | {% endblock %}{{ blog_title }} by {{ blog_author }}</title>
{% block feed_link %} {% block feed_link %}
<link rel="alternate" type="application/rss+xml" title="{{ blog_title }}" href="{% url 'complete-feed' %}"> <link rel="alternate" type="application/rss+xml" title="{{ blog_title }}" href="{% url 'complete-feed' %}">
{% endblock %} {% endblock %}
{% include "articles/snippets/analytics_head.html" %} {% include "articles/snippets/analytics_head.html" %}
{% include "articles/snippets/page_metadata.html" %} {% include "articles/snippets/page_metadata.html" %}
<link rel="stylesheet" href="{% static "vendor/newcss.css" %}" type="text/css"> <link rel="stylesheet" href="{% static "vendor/newcss.css" %}" type="text/css">
<link rel="stylesheet" href="{% static "public.css" %}" type="text/css"> <link rel="stylesheet" href="{% static "public.css" %}" type="text/css">
<link rel="stylesheet" href="{% static "admonitions.css" %}" type="text/css"> <link rel="stylesheet" href="{% static "admonitions.css" %}" type="text/css">
{% if article and article.has_code %} {% if article and article.has_code %}
<link rel="stylesheet" href="{% static "vendor/codehilite.css" %}" type="text/css"> <link rel="stylesheet" href="{% static "vendor/codehilite.css" %}" type="text/css">
{% endif %} {% endif %}
{% if user.is_authenticated %} {% if user.is_authenticated %}
<link rel="stylesheet" href="{% static "authenticated.css" %}"> <link rel="stylesheet" href="{% static "authenticated.css" %}">
{% endif %} {% endif %}
{% block append_css %} {% block append_css %}
{% endblock %} {% endblock %}
{% include "articles/snippets/favicon.html" %} {% include "articles/snippets/favicon.html" %}
</head> </head>
<body> <body>
<header> <header>
<h1>{{ blog_title }}</h1> <h1>{{ blog_title }}</h1>
<p>{{ blog_description }}</p> <p>{{ blog_description }}</p>
{% include "articles/snippets/navigation.html" %} {% include "articles/snippets/navigation.html" %}
</header> </header>
{% endspaceless %} {% endspaceless %}
<main> <main>
@ -45,31 +45,31 @@
</main> </main>
{% spaceless %} {% spaceless %}
<footer> <footer>
<hr> <hr>
<p> <p>
Thoughts written here are my own and dont reflect any of my past, present Thoughts written here are my own and dont reflect any of my past, present
or future employer's position. or future employer's position.
The platform behind this blog is <a href="{{ blog_repo_homepage }}">free software</a>. The platform behind this blog is <a href="{{ blog_repo_homepage }}">free software</a>.
This blog and all articles by Gabriel Augendre are licensed under the This blog and all articles by Gabriel Augendre are licensed under the
<a href="http://creativecommons.org/licenses/by-sa/4.0/">CC BY-SA 4.0 International License</a>. <a href="http://creativecommons.org/licenses/by-sa/4.0/">CC BY-SA 4.0 International License</a>.
Code blocks by Gabriel Augendre are licensed under the Code blocks by Gabriel Augendre are licensed under the
<a href="https://www.gnu.org/licenses/gpl-3.0.html">GNU GENERAL PUBLIC LICENSE version 3</a>.<br> <a href="https://www.gnu.org/licenses/gpl-3.0.html">GNU GENERAL PUBLIC LICENSE version 3</a>.<br>
Currently deployed version: <a href="{{ git_version_url }}">{{ git_version }}</a>. Currently deployed version: <a href="{{ git_version_url }}">{{ git_version }}</a>.
{% if blog_status_url %} {% if blog_status_url %}
Status of services can be found <a href="{{ blog_status_url }}">here</a> Status of services can be found <a href="{{ blog_status_url }}">here</a>
{% endif %} {% endif %}
</p> </p>
</footer> </footer>
{% include "articles/snippets/analytics.html" %} {% include "articles/snippets/analytics.html" %}
{% if user.is_authenticated %} {% if user.is_authenticated %}
<script src="{% static 'edit-keymap.js' %}" async defer></script> <script src="{% static 'edit-keymap.js' %}" async defer></script>
{% endif %} {% endif %}
{% if article and article.has_code %} {% if article and article.has_code %}
<script src="{% static 'copy-code.js' %}" async defer></script> <script src="{% static 'copy-code.js' %}" async defer></script>
{% endif %} {% endif %}
</body> </body>
</html> </html>
{% endspaceless %} {% endspaceless %}

View file

@ -2,9 +2,9 @@
{% if not user.is_authenticated and goatcounter_domain is not None %} {% if not user.is_authenticated and goatcounter_domain is not None %}
<script async defer src="{% static "vendor/goatcounter.js" %}" <script async defer src="{% static "vendor/goatcounter.js" %}"
data-goatcounter="https://{{ goatcounter_domain }}/count"></script> data-goatcounter="https://{{ goatcounter_domain }}/count"></script>
<noscript> <noscript>
<img src="https://{{ goatcounter_domain }}/count?p={{ request.get_full_path }}" <img src="https://{{ goatcounter_domain }}/count?p={{ request.get_full_path }}"
alt="GoatCounter tracking pixel"> alt="GoatCounter tracking pixel">
</noscript> </noscript>
{% endif %} {% endif %}

View file

@ -3,6 +3,6 @@
&centerdot; {{ article.get_read_time }} min read &centerdot; {{ article.get_read_time }} min read
{% include "articles/snippets/admin_link.html" %} {% include "articles/snippets/admin_link.html" %}
{% if tags %} {% if tags %}
<br><span>{% for tag in tags %}<a href="{% url "tag" slug=tag.slug %}" class="tag">{{ tag.name }}</a>{% endfor %}</span> <br><span>{% for tag in tags %}<a href="{% url "tag" slug=tag.slug %}" class="tag">{{ tag.name }}</a>{% endfor %}</span>
{% endif %} {% endif %}
</p> </p>

View file

@ -8,19 +8,19 @@ from articles.models import Article, Tag, User
@pytest.fixture() @pytest.fixture()
@pytest.mark.django_db @pytest.mark.django_db()
def author() -> User: def author() -> User:
return User.objects.create_user("gaugendre", is_staff=True, is_superuser=True) return User.objects.create_user("gaugendre", is_staff=True, is_superuser=True)
@pytest.fixture() @pytest.fixture()
@pytest.mark.django_db @pytest.mark.django_db()
def tag() -> Tag: def tag() -> Tag:
return Tag.objects.create(name="This is a new tag", slug="this-new-tag") return Tag.objects.create(name="This is a new tag", slug="this-new-tag")
@pytest.fixture() @pytest.fixture()
@pytest.mark.django_db @pytest.mark.django_db()
def published_article(author: User, tag: Tag) -> Article: def published_article(author: User, tag: Tag) -> Article:
article = Article.objects.create( article = Article.objects.create(
title="Some interesting article title", title="Some interesting article title",
@ -41,7 +41,7 @@ def published_article(author: User, tag: Tag) -> Article:
@pytest.fixture() @pytest.fixture()
@pytest.mark.django_db @pytest.mark.django_db()
def unpublished_article(author: User) -> Article: def unpublished_article(author: User) -> Article:
return Article.objects.create( return Article.objects.create(
title="Some interesting article title, but sorry it is not public yet", title="Some interesting article title, but sorry it is not public yet",
@ -55,5 +55,5 @@ def unpublished_article(author: User) -> Article:
@pytest.fixture(autouse=True, scope="session") @pytest.fixture(autouse=True, scope="session")
def collect_static(): def _collect_static():
call_command("collectstatic", "--no-input", "--clear") call_command("collectstatic", "--no-input", "--clear")

View file

@ -5,7 +5,7 @@ from django.urls import reverse
from articles.models import User from articles.models import User
@pytest.mark.django_db @pytest.mark.django_db()
# @pytest.mark.skip("Fails for no apparent reason") # @pytest.mark.skip("Fails for no apparent reason")
@pytest.mark.flaky(reruns=5, reruns_delay=3) @pytest.mark.flaky(reruns=5, reruns_delay=3)
def test_can_access_add_article(client: Client, author: User): def test_can_access_add_article(client: Client, author: User):

View file

@ -6,7 +6,7 @@ from articles.models import Article
from articles.utils import format_article_content from articles.utils import format_article_content
@pytest.mark.django_db @pytest.mark.django_db()
def test_unauthenticated_render_redirects(published_article: Article, client: Client): def test_unauthenticated_render_redirects(published_article: Article, client: Client):
api_res = client.post( api_res = client.post(
reverse("api-render-article", kwargs={"article_pk": published_article.pk}), reverse("api-render-article", kwargs={"article_pk": published_article.pk}),
@ -15,7 +15,7 @@ def test_unauthenticated_render_redirects(published_article: Article, client: Cl
assert api_res.status_code == 302 assert api_res.status_code == 302
@pytest.mark.django_db @pytest.mark.django_db()
def test_render_article_same_content(published_article: Article, client: Client): def test_render_article_same_content(published_article: Article, client: Client):
client.force_login(published_article.author) client.force_login(published_article.author)
api_res = post_article(client, published_article, published_article.content) api_res = post_article(client, published_article, published_article.content)
@ -35,7 +35,7 @@ def test_render_article_same_content(published_article: Article, client: Client)
assert api_content == standard_content assert api_content == standard_content
@pytest.mark.django_db @pytest.mark.django_db()
def test_render_article_change_content(published_article: Article, client: Client): def test_render_article_change_content(published_article: Article, client: Client):
client.force_login(published_article.author) client.force_login(published_article.author)
preview_content = "This is a different content **with strong emphasis**" preview_content = "This is a different content **with strong emphasis**"
@ -46,7 +46,7 @@ def test_render_article_change_content(published_article: Article, client: Clien
assert html_preview_content in api_content assert html_preview_content in api_content
@pytest.mark.django_db @pytest.mark.django_db()
def test_render_article_doesnt_save(published_article, client: Client): def test_render_article_doesnt_save(published_article, client: Client):
client.force_login(published_article.author) client.force_login(published_article.author)
original_content = published_article.content original_content = published_article.content
@ -57,7 +57,7 @@ def test_render_article_doesnt_save(published_article, client: Client):
assert published_article.content == original_content assert published_article.content == original_content
@pytest.mark.django_db @pytest.mark.django_db()
def test_render_article_no_tags(published_article, client: Client): def test_render_article_no_tags(published_article, client: Client):
client.force_login(published_article.author) client.force_login(published_article.author)
api_res = client.post( api_res = client.post(

View file

@ -7,7 +7,7 @@ from articles.models import Article, User
from articles.views.feeds import CompleteFeed from articles.views.feeds import CompleteFeed
@pytest.mark.django_db @pytest.mark.django_db()
def test_can_access_feed(client: Client, published_article): def test_can_access_feed(client: Client, published_article):
res = client.get(reverse("complete-feed")) res = client.get(reverse("complete-feed"))
assert res.status_code == 200 assert res.status_code == 200
@ -16,7 +16,7 @@ def test_can_access_feed(client: Client, published_article):
assert published_article.title in content assert published_article.title in content
@pytest.mark.django_db @pytest.mark.django_db()
def test_feed_limits_number_of_articles(client: Client, author: User): def test_feed_limits_number_of_articles(client: Client, author: User):
baker.make(Article, 100, status=Article.PUBLISHED, author=author) baker.make(Article, 100, status=Article.PUBLISHED, author=author)
res = client.get(reverse("complete-feed")) res = client.get(reverse("complete-feed"))

View file

@ -6,7 +6,7 @@ from model_bakery import baker
from articles.models import Article, User from articles.models import Article, User
@pytest.mark.django_db @pytest.mark.django_db()
def test_can_access_list(client: Client, published_article: Article): def test_can_access_list(client: Client, published_article: Article):
res = client.get(reverse("articles-list")) res = client.get(reverse("articles-list"))
assert res.status_code == 200 assert res.status_code == 200
@ -15,7 +15,7 @@ def test_can_access_list(client: Client, published_article: Article):
assert published_article.get_abstract() not in content assert published_article.get_abstract() not in content
@pytest.mark.django_db @pytest.mark.django_db()
def test_only_title_shown_on_list(client: Client, author: User): def test_only_title_shown_on_list(client: Client, author: User):
title = "This is a very long title mouahahaha" title = "This is a very long title mouahahaha"
abstract = "Some abstract" abstract = "Some abstract"
@ -34,7 +34,7 @@ def test_only_title_shown_on_list(client: Client, author: User):
assert after not in content assert after not in content
@pytest.mark.django_db @pytest.mark.django_db()
def test_access_article_by_slug(client: Client, published_article: Article): def test_access_article_by_slug(client: Client, published_article: Article):
_test_access_article_by_slug(client, published_article) _test_access_article_by_slug(client, published_article)
@ -52,7 +52,7 @@ def _assert_article_is_rendered(item: Article, res):
assert html in content assert html in content
@pytest.mark.django_db @pytest.mark.django_db()
def test_anonymous_cant_access_draft_detail( def test_anonymous_cant_access_draft_detail(
client: Client, unpublished_article: Article client: Client, unpublished_article: Article
): ):
@ -62,7 +62,7 @@ def test_anonymous_cant_access_draft_detail(
assert res.status_code == 404 assert res.status_code == 404
@pytest.mark.django_db @pytest.mark.django_db()
def test_anonymous_can_access_draft_detail_with_key( def test_anonymous_can_access_draft_detail_with_key(
client: Client, unpublished_article: Article client: Client, unpublished_article: Article
): ):
@ -73,7 +73,7 @@ def test_anonymous_can_access_draft_detail_with_key(
_assert_article_is_rendered(unpublished_article, res) _assert_article_is_rendered(unpublished_article, res)
@pytest.mark.django_db @pytest.mark.django_db()
def test_user_can_access_draft_detail( def test_user_can_access_draft_detail(
client: Client, author: User, unpublished_article: Article client: Client, author: User, unpublished_article: Article
): ):
@ -81,7 +81,7 @@ def test_user_can_access_draft_detail(
_test_access_article_by_slug(client, unpublished_article) _test_access_article_by_slug(client, unpublished_article)
@pytest.mark.django_db @pytest.mark.django_db()
def test_anonymous_cant_access_drafts_list( def test_anonymous_cant_access_drafts_list(
client: Client, unpublished_article: Article client: Client, unpublished_article: Article
): ):
@ -89,7 +89,7 @@ def test_anonymous_cant_access_drafts_list(
assert res.status_code == 302 assert res.status_code == 302
@pytest.mark.django_db @pytest.mark.django_db()
def test_user_can_access_drafts_list( def test_user_can_access_drafts_list(
client: Client, author: User, unpublished_article: Article client: Client, author: User, unpublished_article: Article
): ):
@ -100,7 +100,7 @@ def test_user_can_access_drafts_list(
assert unpublished_article.title in content assert unpublished_article.title in content
@pytest.mark.django_db @pytest.mark.django_db()
def test_has_goatcounter_if_set(client: Client, settings): def test_has_goatcounter_if_set(client: Client, settings):
settings.GOATCOUNTER_DOMAIN = "gc.gabnotes.org" settings.GOATCOUNTER_DOMAIN = "gc.gabnotes.org"
res = client.get(reverse("articles-list")) res = client.get(reverse("articles-list"))
@ -109,7 +109,7 @@ def test_has_goatcounter_if_set(client: Client, settings):
assert f"{settings.GOATCOUNTER_DOMAIN}/count" in content assert f"{settings.GOATCOUNTER_DOMAIN}/count" in content
@pytest.mark.django_db @pytest.mark.django_db()
def test_doesnt_have_goatcounter_if_unset(client: Client, settings): def test_doesnt_have_goatcounter_if_unset(client: Client, settings):
settings.GOATCOUNTER_DOMAIN = None settings.GOATCOUNTER_DOMAIN = None
res = client.get(reverse("articles-list")) res = client.get(reverse("articles-list"))
@ -118,7 +118,7 @@ def test_doesnt_have_goatcounter_if_unset(client: Client, settings):
assert f"{settings.GOATCOUNTER_DOMAIN}/count" not in content assert f"{settings.GOATCOUNTER_DOMAIN}/count" not in content
@pytest.mark.django_db @pytest.mark.django_db()
def test_logged_in_user_doesnt_have_goatcounter(client: Client, author: User, settings): def test_logged_in_user_doesnt_have_goatcounter(client: Client, author: User, settings):
client.force_login(author) client.force_login(author)
settings.GOATCOUNTER_DOMAIN = "gc.gabnotes.org" settings.GOATCOUNTER_DOMAIN = "gc.gabnotes.org"
@ -128,7 +128,7 @@ def test_logged_in_user_doesnt_have_goatcounter(client: Client, author: User, se
assert f"{settings.GOATCOUNTER_DOMAIN}/count" not in content assert f"{settings.GOATCOUNTER_DOMAIN}/count" not in content
@pytest.mark.django_db @pytest.mark.django_db()
def test_image_is_lazy(client: Client, published_article: Article): def test_image_is_lazy(client: Client, published_article: Article):
res = client.get(reverse("article-detail", kwargs={"slug": published_article.slug})) res = client.get(reverse("article-detail", kwargs={"slug": published_article.slug}))
assert res.status_code == 200 assert res.status_code == 200

View file

@ -2,6 +2,6 @@ import pytest
from django.core.management import call_command from django.core.management import call_command
@pytest.mark.django_db @pytest.mark.django_db()
def test_missing_migrations(): def test_missing_migrations():
call_command("makemigrations", "--check") call_command("makemigrations", "--check")

View file

@ -3,7 +3,7 @@ import pytest
from articles.models import Article, User from articles.models import Article, User
@pytest.mark.django_db @pytest.mark.django_db()
def test_publish_article(unpublished_article: Article): def test_publish_article(unpublished_article: Article):
assert unpublished_article.status == Article.DRAFT assert unpublished_article.status == Article.DRAFT
assert unpublished_article.published_at is None assert unpublished_article.published_at is None
@ -12,7 +12,7 @@ def test_publish_article(unpublished_article: Article):
assert published_article.published_at is not None assert published_article.published_at is not None
@pytest.mark.django_db @pytest.mark.django_db()
def test_unpublish_article(published_article: Article): def test_unpublish_article(published_article: Article):
assert published_article.status == Article.PUBLISHED assert published_article.status == Article.PUBLISHED
assert published_article.published_at is not None assert published_article.published_at is not None
@ -21,7 +21,7 @@ def test_unpublish_article(published_article: Article):
assert unpublished_article.published_at is None assert unpublished_article.published_at is None
@pytest.mark.django_db @pytest.mark.django_db()
def test_save_article_adds_missing_slug(author: User): def test_save_article_adds_missing_slug(author: User):
# Explicitly calling bulk_create with one article because it doesn't call save(). # Explicitly calling bulk_create with one article because it doesn't call save().
articles = Article.objects.bulk_create( articles = Article.objects.bulk_create(
@ -33,7 +33,7 @@ def test_save_article_adds_missing_slug(author: User):
assert article.slug != "" assert article.slug != ""
@pytest.mark.django_db @pytest.mark.django_db()
def test_save_article_doesnt_change_existing_slug(published_article: Article): def test_save_article_doesnt_change_existing_slug(published_article: Article):
original_slug = published_article.slug original_slug = published_article.slug
published_article.title = "This is a brand new title" published_article.title = "This is a brand new title"
@ -41,19 +41,19 @@ def test_save_article_doesnt_change_existing_slug(published_article: Article):
assert published_article.slug == original_slug assert published_article.slug == original_slug
@pytest.mark.django_db @pytest.mark.django_db()
def test_empty_custom_css_minified(published_article): def test_empty_custom_css_minified(published_article):
published_article.custom_css = "" published_article.custom_css = ""
assert published_article.get_minified_custom_css == "" assert published_article.get_minified_custom_css == ""
@pytest.mark.django_db @pytest.mark.django_db()
def test_simple_custom_css_minified(published_article): def test_simple_custom_css_minified(published_article):
published_article.custom_css = ".cls {\n background-color: red;\n}" published_article.custom_css = ".cls {\n background-color: red;\n}"
assert published_article.get_minified_custom_css == ".cls{background-color:red}" assert published_article.get_minified_custom_css == ".cls{background-color:red}"
@pytest.mark.django_db @pytest.mark.django_db()
def test_larger_custom_css_minified(published_article): def test_larger_custom_css_minified(published_article):
published_article.custom_css = """\ published_article.custom_css = """\
.profile { .profile {

View file

@ -2,10 +2,10 @@ function copy(event) {
const text = event.target.dataset.toCopy; const text = event.target.dataset.toCopy;
navigator.clipboard.writeText(text).then(() => { navigator.clipboard.writeText(text).then(() => {
console.log("Copied"); console.log("Copied");
}) });
} }
$(document).ready(function() { $(document).ready(function () {
const buttons = document.querySelectorAll(".copy-button"); const buttons = document.querySelectorAll(".copy-button");
for (const button of buttons) { for (const button of buttons) {
button.addEventListener("click", copy); button.addEventListener("click", copy);

View file

@ -6,9 +6,9 @@ from django.core.files import File
from attachments.models import Attachment from attachments.models import Attachment
@pytest.mark.block_network @pytest.mark.block_network()
@pytest.mark.vcr @pytest.mark.vcr()
@pytest.mark.django_db @pytest.mark.django_db()
def test_attachment_is_processed_by_shortpixel(): def test_attachment_is_processed_by_shortpixel():
# This path manipulation is required to make the test run from this directory # This path manipulation is required to make the test run from this directory
# or from upper in the hierarchy (e.g.: settings.BASE_DIR) # or from upper in the hierarchy (e.g.: settings.BASE_DIR)

View file

@ -3,7 +3,7 @@
{% block title %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %} {% block title %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %}
{% block branding %} {% block branding %}
<h1 id="site-name"><a href="{% url 'admin:index' %}">{{ site_header|default:_('Django administration') }}</a></h1> <h1 id="site-name"><a href="{% url 'admin:index' %}">{{ site_header|default:_('Django administration') }}</a></h1>
{% endblock %} {% endblock %}
{% block extrahead %} {% block extrahead %}