| 1 |
<?php |
| 2 |
|
| 3 |
namespace Extendify\Tests\Integration\Toolbar; |
| 4 |
|
| 5 |
use WP_UnitTestCase; |
| 6 |
|
| 7 |
/** |
| 8 |
* Brand-leak guard for the Toolbar + Quick Edit features. |
| 9 |
* |
| 10 |
* On white-label partner sites the "Extendify" brand must never reach |
| 11 |
* a user-facing surface. This statically scans every WP i18n call in |
| 12 |
* app/Toolbar + app/QuickEdit and fails if any translatable string |
| 13 |
* contains "Extendify" — catching reintroduction anywhere in those |
| 14 |
* features, not just the one aria-label we neutralized. |
| 15 |
* |
| 16 |
* Pairs with .github/translations/scan-redflags.py, which guards the |
| 17 |
* translated side (no "Extendify" in any locale's msgstr). |
| 18 |
*/ |
| 19 |
class BrandLeakGuardTest extends WP_UnitTestCase |
| 20 |
{ |
| 21 |
public function testNoExtendifyInToolbarOrQuickEditStrings(): void |
| 22 |
{ |
| 23 |
$root = dirname(__DIR__, 3); |
| 24 |
$offenders = []; |
| 25 |
foreach (['app/Toolbar', 'app/QuickEdit'] as $dir) { |
| 26 |
foreach ($this->i18nStrings($root, $dir) as [$where, $text]) { |
| 27 |
if (stripos($text, 'extendify') !== false) { |
| 28 |
$offenders[] = sprintf('%s "%s"', $where, $text); |
| 29 |
} |
| 30 |
} |
| 31 |
} |
| 32 |
|
| 33 |
$this->assertSame( |
| 34 |
[], |
| 35 |
$offenders, |
| 36 |
"User-facing string(s) leak the Extendify brand:\n" . implode("\n", $offenders) |
| 37 |
); |
| 38 |
} |
| 39 |
|
| 40 |
/** |
| 41 |
* Yield ["relPath:line", firstStringArg] for every WP i18n call in |
| 42 |
* the .php files under $root/$dir. |
| 43 |
* |
| 44 |
* @return \Generator<array{0:string,1:string}> |
| 45 |
*/ |
| 46 |
private function i18nStrings(string $root, string $dir): \Generator |
| 47 |
{ |
| 48 |
$abs = $root . '/' . $dir; |
| 49 |
if (!is_dir($abs)) { |
| 50 |
return; |
| 51 |
} |
| 52 |
// Quote-aware: i18n fn, opening quote, escape-aware body, matching close. |
| 53 |
$re = <<<'REGEX' |
| 54 |
/\b(?:esc_html_x|esc_attr_x|esc_html__|esc_attr__|esc_html_e|esc_attr_e|_nx|_x|_n|__|_e)\s*\(\s*(['"])((?:\\.|(?!\1).)*)\1/ |
| 55 |
REGEX; |
| 56 |
$it = new \RecursiveIteratorIterator( |
| 57 |
new \RecursiveDirectoryIterator($abs, \FilesystemIterator::SKIP_DOTS) |
| 58 |
); |
| 59 |
foreach ($it as $file) { |
| 60 |
if (strtolower($file->getExtension()) !== 'php') { |
| 61 |
continue; |
| 62 |
} |
| 63 |
$rel = $dir . substr($file->getPathname(), strlen($abs)); |
| 64 |
foreach (file($file->getPathname(), FILE_IGNORE_NEW_LINES) as $n => $text) { |
| 65 |
if (preg_match_all($re, $text, $matches, PREG_SET_ORDER)) { |
| 66 |
foreach ($matches as $hit) { |
| 67 |
yield [$rel . ':' . ($n + 1), $hit[2]]; |
| 68 |
} |
| 69 |
} |
| 70 |
} |
| 71 |
} |
| 72 |
} |
| 73 |
} |
| 74 |
|