Files
xamxam/tests/phpunit/TfeControllerOgTest.php
Pontoporeia 99125cc8e3 Add autosave draft system for partage form with HTMX-based session persistence
- New fragment endpoint POST/GET /partage/fragments/draft.php:
  saves all form fields to PHP session, excludes file/csrf/slug fields
  GET returns JSON for JS hydration on page load
  rotates both global CSRF and share CSRF tokens in sync

- form.php accepts optional $formExtraAttrs and $showAutosaveStatus:
  allows injecting HTMX attributes and 'Brouillon enregistré' indicator

- renderShareLinkForm adds hx-post with change/input debounce trigger,
  loads autosave-handler.js, hydrate fields from draft on page load

- Draft cleared on successful form submission in handleShareLinkSubmission

- autosave-handler.js now also updates share_link_token hidden input
  when rotating CSRF token (partage form uses both csrf_token and share_link_token)

- Added .autosave-status CSS to form.css (was admin.css-only)

- Updated fragment routing to accept GET requests (needed for draft hydration)
2026-06-11 11:04:49 +02:00

200 lines
6.5 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
use PHPUnit\Framework\TestCase;
/**
* TfeControllerOgTest — Pure logic tests for TfeController::buildOgTags()
* and buildMetaDescription().
*
* These methods are protected; we use a thin subclass to expose them.
*/
class TfeControllerOgTest extends TestCase
{
/**
* Subclass that exposes protected methods for testing.
*/
private static function makeController(): object
{
return new class () extends TfeController {
public function __construct()
{
// Skip parent constructor — we don't need DB for these pure methods
}
public function exposedBuildOgTags(array $data, int $thesisId, string $metaDescription): array
{
return $this->buildOgTags($data, $thesisId, $metaDescription);
}
public function exposedBuildMetaDescription(string $synopsis): string
{
return $this->buildMetaDescription($synopsis);
}
public function exposedResolveOgImage(array $files): string
{
return $this->resolveOgImage($files);
}
};
}
// ── buildOgTags() ─────────────────────────────────────────────────────────
public function testBuildOgTagsReturnsAllRequiredKeys(): void
{
$ctrl = self::makeController();
$data = [
'title' => 'My Thesis',
'authors' => 'Jane Doe',
'year' => '2024',
'files' => [],
];
$tags = $ctrl->exposedBuildOgTags($data, 42, 'A description');
$requiredKeys = ['type', 'title', 'description', 'url', 'image', 'image_alt', 'site_name', 'article_author', 'article_published_time'];
foreach ($requiredKeys as $key) {
$this->assertArrayHasKey($key, $tags);
$this->assertIsString($tags[$key]);
}
}
public function testBuildOgTagsTitleIncludesAuthors(): void
{
$ctrl = self::makeController();
$data = [
'title' => 'The Title',
'authors' => 'Author Name',
'year' => '2024',
'files' => [],
];
$tags = $ctrl->exposedBuildOgTags($data, 1, 'desc');
$this->assertStringContainsString('The Title Author Name', $tags['title']);
// Without authors, just the title
$data['authors'] = '';
$tags2 = $ctrl->exposedBuildOgTags($data, 1, 'desc');
$this->assertSame('The Title', $tags2['title']);
}
public function testBuildOgTagsImageEmptyWhenNoFiles(): void
{
$ctrl = self::makeController();
$data = [
'title' => 'No Files',
'authors' => 'A',
'year' => '2023',
'files' => [],
];
$tags = $ctrl->exposedBuildOgTags($data, 5, 'desc');
$this->assertSame('', $tags['image']);
}
public function testBuildOgTagsImageFromCover(): void
{
$ctrl = self::makeController();
$data = [
'title' => 'Cover Test',
'authors' => 'Author',
'year' => '2024',
'files' => [
['file_path' => 'documents/2024-001/cover.jpg', 'file_type' => 'cover'],
],
];
$tags = $ctrl->exposedBuildOgTags($data, 1, 'desc');
$this->assertStringContainsString('cover.jpg', $tags['image']);
}
public function testBuildOgTagsImageFallbackToFirstImage(): void
{
$ctrl = self::makeController();
$data = [
'title' => 'Image Test',
'authors' => 'B',
'year' => '2024',
'files' => [
['file_path' => 'documents/2024-001/doc.pdf', 'file_type' => 'tfe'],
['file_path' => 'documents/2024-001/screenshot.png', 'file_type' => 'tfe'],
],
];
$tags = $ctrl->exposedBuildOgTags($data, 2, 'desc');
$this->assertStringContainsString('screenshot.png', $tags['image']);
}
public function testBuildOgTagsUrlIncludesThesisId(): void
{
$ctrl = self::makeController();
$data = ['title' => 'URL Test', 'authors' => '', 'year' => '2024', 'files' => []];
$tags = $ctrl->exposedBuildOgTags($data, 99, 'desc');
$this->assertStringContainsString('tfe?id=99', $tags['url']);
}
public function testBuildOgTagsPublishedTimeFormatted(): void
{
$ctrl = self::makeController();
$data = ['title' => 'T', 'authors' => '', 'year' => '2025', 'files' => []];
$tags = $ctrl->exposedBuildOgTags($data, 1, 'desc');
$this->assertSame('2025-01-01', $tags['article_published_time']);
}
public function testBuildOgTagsPublishedTimeEmptyWhenNoYear(): void
{
$ctrl = self::makeController();
$data = ['title' => 'T', 'authors' => '', 'files' => []];
$tags = $ctrl->exposedBuildOgTags($data, 1, 'desc');
$this->assertSame('', $tags['article_published_time']);
}
// ── buildMetaDescription() ────────────────────────────────────────────────
public function testBuildMetaDescriptionTruncatesLongSynopsis(): void
{
$ctrl = self::makeController();
$long = str_repeat('abcdefghij', 20); // 200 chars
$desc = $ctrl->exposedBuildMetaDescription($long);
$this->assertLessThanOrEqual(160, strlen($desc));
$this->assertStringEndsWith('…', $desc);
}
public function testBuildMetaDescriptionKeepsShortSynopsis(): void
{
$ctrl = self::makeController();
$desc = $ctrl->exposedBuildMetaDescription('A short synopsis.');
$this->assertSame('A short synopsis.', $desc);
}
public function testBuildMetaDescriptionEmptySynopsisReturnsDefault(): void
{
$ctrl = self::makeController();
$desc = $ctrl->exposedBuildMetaDescription('');
$this->assertStringContainsString('Mémoire', $desc);
$this->assertStringContainsString('XAMXAM', $desc);
}
public function testBuildMetaDescriptionStripsHtmlTags(): void
{
$ctrl = self::makeController();
$desc = $ctrl->exposedBuildMetaDescription('<p>Hello <b>world</b></p>');
$this->assertStringNotContainsString('<p>', $desc);
$this->assertStringNotContainsString('<b>', $desc);
$this->assertStringContainsString('Hello world', $desc);
}
}