Add eslint, prettier, djhtml, pytest-style & django-upgrade
This commit is contained in:
parent
09f17bdef1
commit
1dbf04db66
25 changed files with 223 additions and 128 deletions
51
.eslintrc
Normal file
51
.eslintrc
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
2
.flake8
2
.flake8
|
@ -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
|
||||||
|
|
|
@ -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
5
.prettierrc
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"tabWidth": 4,
|
||||||
|
"printWidth": 120,
|
||||||
|
"endOfLine": "auto"
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 %}
|
||||||
|
|
|
@ -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 %}
|
||||||
|
|
|
@ -3,6 +3,6 @@
|
||||||
· {{ article.get_read_time }} min read
|
· {{ 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>
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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"))
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 %}
|
||||||
|
|
Reference in a new issue