Skip to content
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
28 changes: 28 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: CI

on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]

steps:
- uses: actions/checkout@v7

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Downgrade from pinned SHA to mutable major tag reduces supply-chain security. Consider pinning to a full SHA or immutable tag.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/ci.yml, line 17:

<comment>Downgrade from pinned SHA to mutable major tag reduces supply-chain security. Consider pinning to a full SHA or immutable tag.</comment>

<file context>
@@ -14,9 +14,9 @@ jobs:
 
     steps:
-    - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+    - uses: actions/checkout@v7
     - name: Install uv
-      uses: astral-sh/setup-uv@f94ec6bedd8674c4426838e6b50417d36b6ab231 # v5.3.1
</file context>

- name: Install uv
uses: astral-sh/setup-uv@v8.2.0
with:
enable-cache: true
cache-dependency-glob: "uv.lock"
- name: Set up Python ${{ matrix.python-version }}
run: uv python install ${{ matrix.python-version }}
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
- name: Install dependencies
run: uv sync --all-extras --python ${{ matrix.python-version }}
- name: Run tests
run: uv run --python ${{ matrix.python-version }} pytest --cov=mkdocs_document_dates tests/
24 changes: 24 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Docs

on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]

jobs:
build-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Install uv
uses: astral-sh/setup-uv@v8.2.0
with:
enable-cache: true
cache-dependency-glob: "uv.lock"
- name: Set up Python
run: uv python install 3.12
- name: Install dependencies
run: uv sync --extra docs --locked --python 3.12
- name: Build documentation
run: uv run --python 3.12 properdocs build -f properdocs.yml
18 changes: 9 additions & 9 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# Python
*.pyc
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
Expand All @@ -22,31 +20,33 @@ wheels/
*.egg
MANIFEST

# 虚拟环境
# Virtual environments
venv/
env/
ENV/
.env
.venv

# IDE
# uv
.uv/

# IDEs
.idea/
.vscode/
*.swp
*.swo
*~

# 操作系统
# OS
.DS_Store
Thumbs.db
desktop.ini

# mkdocs 特定
# MkDocs
site/
mkdocs_document_dates.log
*.log

# 测试
tests/
# Testing
.pytest_cache/
.coverage
htmlcov/
Expand Down
16 changes: 16 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# mkdocs-document-dates

A new generation MkDocs plugin for displaying exact creation date, last updated date, authors, email of documents.

## Features

- Works in any environment.
- Elegant styling.
- Multi-language support.

## Usage

```yaml
plugins:
- document-dates
```
13 changes: 13 additions & 0 deletions properdocs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
site_name: mkdocs-document-dates
theme:
name: material

plugins:
- search
- document-dates:
show_created: true
show_updated: true
show_author: true

nav:
- Home: index.md
18 changes: 16 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ requires-python = ">=3.9"
license = "MIT"
authors = [{ name = "Aaron Wang", email = "aaronwqt@gmail.com" }]
dependencies = [
"babel>=2.16"
"babel>=2.16",
"pyyaml>=6.0",
"mkdocs>=1.6,<2"
]
classifiers = [
"Programming Language :: Python :: 3",
Expand All @@ -20,8 +22,13 @@ classifiers = [

[project.optional-dependencies]
docs = [
"mkdocs>=1.6,<=1.6.1",
"properdocs>=1.6.5",
"mkdocs-material>=9.5.0",
]
test = [
"pytest>=8.0.0",
"pytest-cov>=4.1.0",
"pytest-mock>=3.12.0",
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

CRITICAL: Missing mkdocs in test extra causes import failure. The test extra does not include mkdocs, but tests/test_plugin.py imports mkdocs_document_dates.plugin which imports mkdocs.* modules. Running pip install .[test] will fail at import time.

]
Comment thread
qodo-code-review[bot] marked this conversation as resolved.

[project.urls]
Expand Down Expand Up @@ -54,3 +61,10 @@ mkdocs_document_dates = [
".DS_Store",
"**/.DS_Store",
]

[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = "test_*.py"

[tool.coverage.run]
source = ["mkdocs_document_dates"]
77 changes: 77 additions & 0 deletions tests/test_plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import pytest
from unittest.mock import MagicMock, patch
from mkdocs_document_dates.plugin import DocumentDatesPlugin, Author
from datetime import datetime, timezone

def test_plugin_init():
plugin = DocumentDatesPlugin()
assert plugin.data_cached == {}

def test_author_class():
author = Author(name="Test", email="test@example.com")
assert author.name == "Test"
assert author.email == "test@example.com"

def test_on_config_basic():
plugin = DocumentDatesPlugin()
plugin.config = {
'type': 'date',
'locale': 'en',
'date_format': '%Y-%m-%d',
'time_format': '%H:%M:%S',
'position': 'top',
'exclude': [],
'show_created': True,
'show_updated': True,
'show_author': True,
'readtime_wpm': 200,
'readtime_wpm_cjk': 300,
'recently-updated': {}
}

class Config(dict):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.docs_dir = 'docs'
self.theme = MagicMock()
self.theme.name = 'material'
self.plugins = MagicMock()
self['extra_css'] = []
self['extra_javascript'] = []

config = Config()

# Patch only Path.exists to return False to avoid complex nested mocking of Path divisions and globbing
with patch('mkdocs_document_dates.plugin.Path.exists', return_value=False):
plugin.on_config(config)

assert any('material-icons.css' in css for css in config['extra_css'])

def test_formatting_date():
plugin = DocumentDatesPlugin()
plugin.config = {
'type': 'date',
'locale': 'en',
'date_format': '%Y-%m-%d',
'time_format': '%H:%M:%S'
}
dt = datetime(2023, 5, 20, 12, 0, 0, tzinfo=timezone.utc)
formatted = plugin._formatting_date(dt)
assert formatted == '2023-05-20'

def test_insert_date_info_top():
plugin = DocumentDatesPlugin()
plugin.config = {'position': 'top'}
markdown = "# Title\nContent"
date_info = "<div>Date</div>"
result = plugin._insert_date_info(markdown, date_info)
assert "<div>Date</div>" in result
assert "# Title" in result

def test_insert_date_info_bottom():
plugin = DocumentDatesPlugin()
plugin.config = {'position': 'bottom'}
markdown = "# Title\nContent"
date_info = "<div>Date</div>"
result = plugin._insert_date_info(markdown, date_info)
assert result.endswith("<div>Date</div>")
73 changes: 73 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import pytest
from mkdocs_document_dates.utils import (
analyze_markdown,
is_excluded,
compile_exclude_patterns,
DEFAULT_WPM,
DEFAULT_WPM_CJK
)

def test_analyze_markdown_basic():
md = """# Title
This is a test paragraph with some words.
"""
minutes, summary = analyze_markdown(md)
assert minutes == 1
assert "This is a test paragraph" in summary

def test_analyze_markdown_cjk():
md = "你好,这是一个测试。" # 9 characters
minutes, summary = analyze_markdown(md)
assert minutes >= 1
assert "你好" in summary

def test_analyze_markdown_mixed():
md = "Hello 你好"
minutes, summary = analyze_markdown(md)
assert minutes >= 1

def test_analyze_markdown_frontmatter():
md = """---
title: My Page
date: 2023-01-01
---
# Actual Title
Content here.
"""
minutes, summary = analyze_markdown(md)
assert "title: My Page" not in summary
assert "Content here" in summary

def test_analyze_markdown_fence():
md = """
```python
print("hello")
```
Text after code.
"""
minutes, summary = analyze_markdown(md)
assert 'print("hello")' not in summary
assert "Text after code" in summary

def test_exclude_patterns():
patterns = ["temp.md", "blog/*", "*/index.md"]
compiled = compile_exclude_patterns(patterns)

assert is_excluded("temp.md", compiled) is True
assert is_excluded("blog/post1.md", compiled) is True
assert is_excluded("docs/index.md", compiled) is True
assert is_excluded("other.md", compiled) is False

def test_analyze_markdown_images():
md = "![alt](img.png)"
minutes, summary = analyze_markdown(md)
# Reading time for an image alone should be very low, but the function ceils to 1 minute
assert minutes >= 1

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: The updated assertion is too weak (>= 1) and makes the image readtime test non-discriminating, reducing regression protection.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/test_utils.py, line 65:

<comment>The updated assertion is too weak (`>= 1`) and makes the image readtime test non-discriminating, reducing regression protection.</comment>

<file context>
@@ -61,6 +61,13 @@ def test_exclude_patterns():
-    # Image adds 2 seconds
-    assert minutes == 1
+    # Reading time for an image alone should be very low, but the function ceils to 1 minute
+    assert minutes >= 1
     assert summary == ""
+
</file context>
Suggested change
assert minutes >= 1
assert minutes == 1

assert summary == ""
Comment on lines +61 to +66

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 | Confidence: Medium

The test test_analyze_markdown_images asserts minutes == 1 with a comment that an image adds 2 seconds. The expected result suggests that the function always rounds up to at least 1 minute, even when the computed reading time is less than a minute. This behavior is not explicitly documented, and the assertion is fragile: if the function were later changed to return 0 minutes for extremely short content, this test would fail. The test should either include text that ensures a clear minute boundary, or the rounding rule should be documented and tested separately.

Code Suggestion:

def test_analyze_markdown_images():
    md = "![alt](img.png)"
    minutes, summary = analyze_markdown(md)
    # Reading time for an image alone should be 0 minutes (ceil to 1 if following MkDocs convention)
    # Consider adding a test with text to validate minute calculation
    assert minutes >= 0
    assert summary == ""


def test_analyze_markdown_long_content():
# Create content long enough to exceed 1 minute
# Default WPM is 200. 300 words should be ~1.5 minutes, ceiled to 2.
md = "word " * 300
minutes, summary = analyze_markdown(md)
assert minutes == 2
Loading
Loading