diff --git a/CHANGELOG.md b/CHANGELOG.md
index 520d5b8a9..b798bb369 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@ The format is based on Keep a Changelog.
### Added
- Added `Response::back()` and the `back()` helper, which redirect to the referrer and fall back to `base_url()` when the request has none, or when the referrer is not on the application origin (same scheme, host and port), which is compared with `base_url()`: behind a proxy that terminates TLS, or where the `Host` header is not pinned, set `app.base_url`. They take the old input to keep for the next request, read with `old()` as after `redirectWith()`, and an optional status code (`back($data, $code)`) (#540)
+- The `DemoWeb` About page asks visitors to star Quantum on GitHub, at the top of the page: a button that links to `https://github.com/quantum-php/framework`, with the current star count from a shields.io badge, which the browser loads from the internet. The texts are translation keys, `common.star.message`, `common.star.button` and `common.star.badge_alt`, which the starter project adds to its `common` lang files in English and Spanish; until a project has them the page shows the key names. Modules generated before keep their page (#542)
### Changed
- **BREAKING:** Converted the constant classes `ReservedKeys`, `AuthKeys`, `Pagination` and the database `Type`, `Key` and `Relation` to PHP enums, and moved the table actions of `Table` into the new `TableAction` enum (#612). An enum case is not its value, so code that needs the text or number reads `->value`, and text that comes from outside is turned into a case with `::from()` or `::tryFrom()`:
diff --git a/src/Module/Templates/DemoWeb/src/Views/pages/about.php.tpl b/src/Module/Templates/DemoWeb/src/Views/pages/about.php.tpl
index 4570abaec..d22ac626f 100644
--- a/src/Module/Templates/DemoWeb/src/Views/pages/about.php.tpl
+++ b/src/Module/Templates/DemoWeb/src/Views/pages/about.php.tpl
@@ -4,6 +4,12 @@
+
+
+
+

+
+
diff --git a/tests/Unit/Module/Templates/AboutTemplateTest.php b/tests/Unit/Module/Templates/AboutTemplateTest.php
new file mode 100644
index 000000000..8bb29dd9a
--- /dev/null
+++ b/tests/Unit/Module/Templates/AboutTemplateTest.php
@@ -0,0 +1,70 @@
+template = (string) file_get_contents(
+ dirname(__DIR__, 4) . '/src/Module/Templates/DemoWeb/src/Views/pages/about.php.tpl'
+ );
+ }
+
+ public function testStarTextsAreTranslated(): void
+ {
+ $this->assertStringContainsString("_t('common.star.message')", $this->template);
+ $this->assertStringContainsString("_t('common.star.button')", $this->template);
+ $this->assertStringContainsString("_t('common.star.badge_alt')", $this->template);
+ }
+
+ public function testAboutPageHasNoHardCodedStarText(): void
+ {
+ $this->assertStringNotContainsString('starring Quantum', $this->template);
+ $this->assertStringNotContainsString('Star on GitHub', $this->template);
+ $this->assertStringNotContainsString('alt="GitHub stars"', $this->template);
+ }
+
+ public function testAboutPageLinksToTheFrameworkRepository(): void
+ {
+ $this->assertMatchesRegularExpression(
+ '#
]*href="https://github\.com/quantum-php/framework"[^>]*>#',
+ $this->template
+ );
+ }
+
+ public function testStarLinkOpensInANewTabSafely(): void
+ {
+ $this->assertMatchesRegularExpression(
+ '#]*href="https://github\.com/quantum-php/framework"[^>]*target="_blank"[^>]*rel="noopener noreferrer"[^>]*>#',
+ $this->template
+ );
+ }
+
+ public function testAboutPageShowsTheStarCountBadge(): void
+ {
+ $this->assertMatchesRegularExpression(
+ '#
]*src="https://img\.shields\.io/github/stars/quantum-php/framework[^"]*"[^>]*alt="<\?php _t\(\'common\.star\.badge_alt\'\); \?>"[^>]*>#',
+ $this->template
+ );
+ }
+
+ public function testStarMessageComesBeforeTheCliCommands(): void
+ {
+ $star = strpos($this->template, "_t('common.star.message')");
+ $commands = strpos($this->template, 'common.cli_commands');
+
+ $this->assertNotFalse($star);
+ $this->assertNotFalse($commands);
+ $this->assertLessThan($commands, $star);
+ }
+
+ public function testAboutPageDoesNotUseTheOldRepositoryName(): void
+ {
+ $this->assertStringNotContainsString('softberg', $this->template);
+ }
+}