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
2 changes: 1 addition & 1 deletion styles/all/template/recent_topics_body_side.html
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
{% if S_POSTLOVE and recent_topics.TOPIC_LIKES %}<span class="rt-likes"><i class="icon fa-heart fa-fw icon-red" aria-hidden="true"></i> {{ recent_topics.TOPIC_LIKES }}</span>{% endif %}
<a href="{{ recent_topics.U_LAST_POST }}" title="{{ lang('GOTO_LAST_POST')|e('html_attr') }}">
<i class="icon fa-external-link-square fa-fw {% if recent_topics.S_UNREAD_TOPIC and not S_IS_BOT %}icon-red icon-md{% else %}icon-lightgrey icon-md{% endif %}" aria-hidden="true"></i>
<span class="sr-only">{{ VIEW_LATEST_POST }}</span>
<span class="sr-only">{{ lang('GOTO_LAST_POST') }}</span>
</a>
</span>
</dd>
Expand Down
2 changes: 1 addition & 1 deletion styles/all/template/recent_topics_page.html
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@
{% if not S_IS_BOT %}
<a href="{{ recent_topics.U_LAST_POST }}" title="{{ lang('GOTO_LAST_POST')|e('html_attr') }}">
<i class="icon fa-external-link-square fa-fw icon-lightgray icon-md" aria-hidden="true"></i>
<span class="sr-only">{{ VIEW_LATEST_POST }}</span>
<span class="sr-only">{{ lang('GOTO_LAST_POST') }}</span>
</a> {% endif %}<br />{{ recent_topics.LAST_POST_TIME }}
</span>
</dd>
Expand Down
2 changes: 1 addition & 1 deletion styles/all/template/recent_topics_simple.html
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
{% if not S_IS_BOT %}
<a href="{{ recent_topics.U_LAST_POST }}" title="{{ lang('GOTO_LAST_POST')|e('html_attr') }}">
<i class="icon fa-external-link-square fa-fw icon-lightgray icon-md" aria-hidden="true"></i>
<span class="sr-only">{{ VIEW_LATEST_POST }}</span>
<span class="sr-only">{{ lang('GOTO_LAST_POST') }}</span>
</a> {% endif %}<br />{{ recent_topics.LAST_POST_TIME }}
</span>
</dd>
Expand Down
2 changes: 1 addition & 1 deletion styles/pbtech/template/recent_topics_body_side.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ <h2>{{ lang('RECENT_TOPICS') }}</h2>
{% if S_POSTLOVE and recent_topics.TOPIC_LIKES %}<span class="rt-likes"><i class="icon fa-heart fa-fw icon-red" aria-hidden="true"></i> {{ recent_topics.TOPIC_LIKES }}</span>{% endif %}
<a href="{{ recent_topics.U_LAST_POST }}" title="{{ lang('GOTO_LAST_POST')|e('html_attr') }}">
<i class="icon fa-external-link-square fa-fw {% if recent_topics.S_UNREAD_TOPIC and not S_IS_BOT %}icon-red icon-md{% else %}icon-lightgrey icon-md{% endif %}" aria-hidden="true"></i>
<span class="sr-only">{{ VIEW_LATEST_POST }}</span>
<span class="sr-only">{{ lang('GOTO_LAST_POST') }}</span>
</a>
</span>
</dd>
Expand Down
2 changes: 1 addition & 1 deletion styles/pbwow3/template/recent_topics_body_side.html
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ <h2>{{ lang('RECENT_TOPICS') }}</h2>
{% if S_POSTLOVE and recent_topics.TOPIC_LIKES %}<span class="rt-likes"><i class="icon fa-heart fa-fw icon-red" aria-hidden="true"></i> {{ recent_topics.TOPIC_LIKES }}</span>{% endif %}
<a href="{{ recent_topics.U_LAST_POST }}" title="{{ lang('GOTO_LAST_POST')|e('html_attr') }}">
<i class="icon fa-external-link-square fa-fw {% if recent_topics.S_UNREAD_TOPIC and not S_IS_BOT %}icon-red icon-md{% else %}icon-lightgrey icon-md{% endif %}" aria-hidden="true"></i>
<span class="sr-only">{{ VIEW_LATEST_POST }}</span>
<span class="sr-only">{{ lang('GOTO_LAST_POST') }}</span>
</a>
</span>
</dd>
Expand Down
2 changes: 1 addition & 1 deletion styles/pbwow3/template/recent_topics_body_topbottom.html
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ <h2>{{ lang('RECENT_TOPICS') }}</h2>
{% if not S_IS_BOT %}
<a href="{{ recent_topics.U_LAST_POST }}" title="{{ lang('GOTO_LAST_POST') }}">
<i class="icon fa-external-link-square fa-fw icon-lightgray icon-md" aria-hidden="true"></i>
<span class="sr-only">{{ VIEW_LATEST_POST }}</span>
<span class="sr-only">{{ lang('GOTO_LAST_POST') }}</span>
</a> {% endif %}<br />{{ recent_topics.LAST_POST_TIME }}
</span>
</dd>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
{% if not S_IS_BOT %}
<a href="{{ recent_topics.U_LAST_POST }}" title="{{ lang('GOTO_LAST_POST')|e('html_attr') }}">
<i class="icon fa-external-link-square fa-fw icon-lightgray icon-md" aria-hidden="true"></i>
<span class="sr-only">{{ VIEW_LATEST_POST }}</span>
<span class="sr-only">{{ lang('GOTO_LAST_POST') }}</span>
</a> {% endif %}<br />{{ recent_topics.LAST_POST_TIME }}
</span>
</dd>
Expand Down
47 changes: 47 additions & 0 deletions tests/template/template_vars_test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php
/**
*
* @package Recent Topics Extension
* @copyright (c) 2026 Andreas Vandenberghe
* @license GNU General Public License, version 2 (GPL-2.0)
*
* Static checks on the extension's templates.
*
* See: https://github.com/avatharbe/RecentTopics/issues/215
*/

namespace avathar\recenttopics\tests\template;

class template_vars_test extends \phpbb_test_case
{
/**
* One row per template file the extension ships.
*/
public function template_data()
{
$root = dirname(__DIR__, 2) . '/styles';
$data = [];
$files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($root, \FilesystemIterator::SKIP_DOTS));
foreach ($files as $file)
{
if ($file->getExtension() === 'html')
{
$data[substr($file->getPathname(), strlen($root) + 1)] = [$file->getPathname()];
}
}
ksort($data);

return $data;
}

/**
* VIEW_LATEST_POST is never assigned, so it rendered an empty screen-reader label on the
* last-post link. The link's text comes from lang('GOTO_LAST_POST'), matching its title (#215).
*
* @dataProvider template_data
*/
public function test_no_unassigned_view_latest_post($file)
{
$this->assertStringNotContainsString('VIEW_LATEST_POST', file_get_contents($file));
}
}
18 changes: 17 additions & 1 deletion tests/tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ tests/
│ └── recenttopics_events_test.php Public event API contract (contrib/events.md)
├── language/
│ └── viewonline_lang_test.php Who Is Online label in every language pack
├── template/
│ └── template_vars_test.php No unassigned template variables in shipped templates
└── functional/
└── recenttopics_test.php End-to-end browser tests
```
Expand Down Expand Up @@ -257,7 +259,21 @@ It runs `fill_template()` on one page of four topics in time order (normal, anno

---

## 11. Functional tests (`functional/recenttopics_test.php`)
## 11. Template variables (`template/template_vars_test.php`)

**What this code does:**
The topic-list templates in `styles/*/template/` render a hidden screen-reader label inside each last-post link.

**What this test file does:**
It reads every `.html` template the extension ships (one data-provider row per file) and checks it for variables the PHP never assigns.

| Test | Scenario | What it verifies |
|------|----------|-----------------|
| `test_no_unassigned_view_latest_post` | Every template file | No `VIEW_LATEST_POST`. It was never assigned, so the last-post link's screen-reader label rendered empty; the templates now use `lang('GOTO_LAST_POST')`, matching the link's title (#215) |

---

## 12. Functional tests (`functional/recenttopics_test.php`)

**What this code does:**
These tests start a real phpBB installation (using the test framework's built-in install), enable the extension, and make HTTP requests using a real browser-like crawler (Symfony DomCrawler). They check the actual rendered HTML for specific elements.
Expand Down
Loading