diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..51aaee4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,62 @@ +name: CI + +on: + push: + branches: [ main, master, develop ] + pull_request: + branches: [ main, master, develop ] + +jobs: + tests: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php-version: ['8.0', '8.1', '8.2', '8.3', '8.4'] + dependency-version: [prefer-lowest, prefer-stable] + + name: PHP ${{ matrix.php-version }} - ${{ matrix.dependency-version }} + + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: mbstring, xml, curl, xdebug + coverage: xdebug + + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v3 + with: + path: vendor + key: ${{ runner.os }}-php-${{ matrix.php-version }}-${{ matrix.dependency-version }}-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php-${{ matrix.php-version }}-${{ matrix.dependency-version }}- + ${{ runner.os }}-php-${{ matrix.php-version }}- + ${{ runner.os }}-php- + + - name: Install dependencies + run: composer update --${{ matrix.dependency-version }} --prefer-dist --no-progress --no-suggest + + - name: Run test suite + run: vendor/bin/phpunit + + - name: Generate coverage report (only on PHP 8.1 stable) + if: matrix.php-version == '8.1' && matrix.dependency-version == 'prefer-stable' + run: | + XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-clover coverage.xml + + - name: Upload coverage to Codecov + if: matrix.php-version == '8.1' && matrix.dependency-version == 'prefer-stable' + uses: codecov/codecov-action@v3 + with: + file: ./coverage.xml + flags: unittests + name: codecov-umbrella + fail_ci_if_error: false \ No newline at end of file diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 0000000..177a60a --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,74 @@ +name: Code Coverage + +on: + push: + branches: [ main, master ] + pull_request: + branches: [ main, master ] + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + coverage: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.0' + extensions: mbstring, xml, curl, xdebug + coverage: xdebug + + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v3 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: Run test suite + run: composer run-script test + + - name: Generate coverage report + run: | + XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-html coverage-report --coverage-clover coverage.xml --log-junit junit.xml + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3 + with: + file: ./coverage.xml + flags: unittests + name: codecov-umbrella + + - name: Setup Pages + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' + uses: actions/configure-pages@v4 + + - name: Upload coverage artifact + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' + uses: actions/upload-pages-artifact@v3 + with: + path: './coverage-report' + + - name: Deploy to GitHub Pages + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' + id: deployment + uses: actions/deploy-pages@v4 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 0da9deb..e9d8301 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,44 @@ -.idea -vendor +# IDEs +.idea/ +.vscode/ +*.swp +*.swo + +# Dependencies +vendor/ composer.lock -.phpunit.cache/test-results + +# Testing +.phpunit.cache/ +phpunit.xml.local +.phpunit.result.cache + +# Coverage reports +coverage/ +.coverage/ +clover.xml + +# Logs +*.log + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# PHP CS Fixer +.php-cs-fixer.cache + +# PHPStan +.phpstan.cache + +# Psalm +.psalm/ + +# Temporary files +*.tmp +*.temp diff --git a/README.md b/README.md index 1bb35ab..adf9d06 100644 --- a/README.md +++ b/README.md @@ -1,58 +1,132 @@ Prepositioner ============= -PHP Prepositioner for replacing prepositions with &nbsp; after preposition +PHP Prepositioner for replacing prepositions with ` ` after preposition [![Code Climate](https://codeclimate.com/github/tomaj/prepositioner/badges/gpa.svg)](https://codeclimate.com/github/tomaj/prepositioner) [![Test Coverage](https://api.codeclimate.com/v1/badges/d82eb747d9ac33571be3/test_coverage)](https://codeclimate.com/github/tomaj/prepositioner/test_coverage) [![Latest Stable Version](https://poser.pugx.org/tomaj/prepositioner/v/stable.svg)](https://packagist.org/packages/tomaj/prepositioner) [![License](https://poser.pugx.org/tomaj/prepositioner/license.svg)](https://packagist.org/packages/tomaj/prepositioner) +## Requirements -Instalation ------------ +- **PHP 8.0** or higher +- Uses modern PHP 8.0+ features including: + - Constructor property promotion + - Readonly properties + - Match expressions + - Attributes instead of docblock annotations + - Union types + - Spread operator in arrays + +## Installation Install package via composer: -``` bash -$ composer require tomaj/prepositioner +```bash +composer require tomaj/prepositioner +``` + +## Usage + +### Simple usage without Factory + +```php +use Tomaj\Prepositioner\Prepositioner; + +$prepositioner = new Prepositioner(['one', 'two']); +$result = $prepositioner->formatText($inputText); +``` + +This example replaces all occurrences of *'one'* or *'two'* strings in `$inputText` as *'one '* and *'two '*. + +### Using Factory with language support + +```php +use Tomaj\Prepositioner\Factory; + +$prepositioner = Factory::build('slovak'); +$result = $prepositioner->formatText($inputText); ``` -Usage ------ +### Supported Languages -Simple usage without *Factory* is very simple: +- **Slovak** - `Factory::build('slovak')` +- **Czech** - `Factory::build('czech')` +- **Romanian** - `Factory::build('romanian')` +- **Empty** - `Factory::build('empty')` - for testing or custom usage -``` php -$prepositioner = new Tomaj\Prepositioner\Prepositioner(['one', 'two']); -$prepositioner->formatText($inputText); +### Custom escape string + +```php +use Tomaj\Prepositioner\Factory; + +$prepositioner = Factory::build('slovak', '###CUSTOM###'); +$result = $prepositioner->formatText($inputText); ``` -This example replaces all occurences of *'one'* or *'two'* strings in ```$inputText``` as *'one&nbsp;'* and *'two&nbsp;'*. +## Extending + +To add support for a new language, implement the `LanguageInterface`: -For using with *Factory* which contains language support try: +```php +formatText($inputText); +namespace Tomaj\Prepositioner\Language; + +final class MyLanguage implements LanguageInterface +{ + private const PREPOSITIONS = ['my', 'custom', 'prepositions']; + + /** + * @return array + */ + public function prepositions(): array + { + return self::PREPOSITIONS; + } +} ``` -Extending ---------- +Then use it directly: -For new language support you need to implement new language class which implements *LanguageInterface* with prepositions. See *SlovakLanguage* for details. +```php +use Tomaj\Prepositioner\Prepositioner; +use Tomaj\Prepositioner\Language\MyLanguage; +$language = new MyLanguage(); +$prepositioner = new Prepositioner($language->prepositions()); +``` -Upgrade -------- +Or register it for use with the Factory by placing it in the `Tomaj\Prepositioner\Language` namespace. -**From version 2 to 3** -- Minimum php version is **7.3** from now -- If you are using custom *Language* file from otside or from this repository (and don't use `Tomaj\Prepositioner\Factory`) you have to change namespace from `\Tomaj\Prepositioner\MyLanguage` to `\Tomaj\Prepositioner\Language\MyLanguage` -- *Note:* new version includes `declare(strict_types=1);` in all files +## What's New in v4.0 +- **PHP 8.0+ required** - modernized codebase with latest PHP features +- **Constructor property promotion** - cleaner, more concise code +- **Readonly properties** - improved immutability and type safety +- **Match expressions** - better performance and readability in Factory +- **PHPUnit 10** - latest testing framework with attributes +- **PSR-4 autoloading** - improved autoloader efficiency +- **Final classes** - better encapsulation and performance +- **Typed arrays** - better documentation and IDE support -Known issue ------------ +## Migration from v3.x -1. each new language has to be in *Tomaj\Prepositioner\Language* namespace if you would like to use Factory +- Minimum PHP version is now **8.0** +- All functionality remains the same - no breaking changes to public API +- If extending classes, note that most classes are now `final` +- Tests now use PHPUnit 10 with attributes instead of docblock annotations + +## Development + +Run tests: +```bash +composer test +``` + +Code style: +```bash +composer cs +``` diff --git a/composer.json b/composer.json index ada749f..556ee75 100644 --- a/composer.json +++ b/composer.json @@ -11,16 +11,38 @@ } ], "require": { - "php": ">= 7.2" + "php": "^8.0" }, "require-dev": { - "phpunit/phpunit": "^8.0|^9.0", - "squizlabs/php_codesniffer": "~3.5" + "phpunit/phpunit": "^9.5", + "squizlabs/php_codesniffer": "^3.7" }, "minimum-stability": "stable", "autoload": { - "classmap": [ - "src/" + "psr-4": { + "Tomaj\\Prepositioner\\": "src/Prepositioner/" + } + }, + "autoload-dev": { + "psr-4": { + "Tomaj\\Prepositioner\\Tests\\": "tests/" + } + }, + "scripts": { + "test": "phpunit", + "test-coverage": "phpunit --coverage-html coverage", + "cs-check": "phpcs --standard=PSR12 src tests", + "cs-fix": "phpcbf --standard=PSR12 src tests", + "quality": [ + "@cs-check", + "@test" ] + }, + "config": { + "optimize-autoloader": true, + "sort-packages": true, + "allow-plugins": { + "*": false + } } } diff --git a/phpunit.xml b/phpunit.xml index d6b64f8..16d5897 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,26 +1,19 @@ + failOnWarning="true"> - tests + tests - - + + src - - + + diff --git a/src/Prepositioner/Factory.php b/src/Prepositioner/Factory.php index d29bd75..27b9f98 100644 --- a/src/Prepositioner/Factory.php +++ b/src/Prepositioner/Factory.php @@ -1,18 +1,43 @@ prepositions(), $escapeString); + $languageInstance = self::createLanguageInstance($language); + return new Prepositioner($languageInstance->prepositions(), $escapeString); + } + + private static function createLanguageInstance(string $language): LanguageInterface + { + $className = match (strtolower($language)) { + 'slovak' => SlovakLanguage::class, + 'czech' => CzechLanguage::class, + 'romanian' => RomanianLanguage::class, + 'empty' => EmptyLanguage::class, + default => '\Tomaj\Prepositioner\Language\\' . ucfirst($language) . 'Language' + }; + + if (!class_exists($className)) { + throw new LanguageNotExistsException("Language class '$className' doesn't exist"); + } + + $instance = new $className(); + + if (!$instance instanceof LanguageInterface) { + throw new LanguageNotExistsException("Language class '$className' must implement LanguageInterface"); } - throw new LanguageNotExistsException("Language class '$className' doesn't exists"); + return $instance; } } diff --git a/src/Prepositioner/Language/CzechLanguage.php b/src/Prepositioner/Language/CzechLanguage.php index ded20dc..7c2b4bc 100644 --- a/src/Prepositioner/Language/CzechLanguage.php +++ b/src/Prepositioner/Language/CzechLanguage.php @@ -1,22 +1,18 @@ + */ public function prepositions(): array { - return [ - /* 1 letter */ - 'a', - 'i', - 'k', - 'o', - 'v', - 'u', - 'z', - 's', - ]; + return self::PREPOSITIONS; } } diff --git a/src/Prepositioner/Language/EmptyLanguage.php b/src/Prepositioner/Language/EmptyLanguage.php index ee29052..58b79c8 100644 --- a/src/Prepositioner/Language/EmptyLanguage.php +++ b/src/Prepositioner/Language/EmptyLanguage.php @@ -1,10 +1,14 @@ + */ public function prepositions(): array { return []; diff --git a/src/Prepositioner/Language/LanguageInterface.php b/src/Prepositioner/Language/LanguageInterface.php index 0bbfb95..d3e2af9 100644 --- a/src/Prepositioner/Language/LanguageInterface.php +++ b/src/Prepositioner/Language/LanguageInterface.php @@ -1,9 +1,15 @@ Array of prepositions + */ public function prepositions(): array; } diff --git a/src/Prepositioner/Language/RomanianLanguage.php b/src/Prepositioner/Language/RomanianLanguage.php index 1c43a42..4dd210a 100644 --- a/src/Prepositioner/Language/RomanianLanguage.php +++ b/src/Prepositioner/Language/RomanianLanguage.php @@ -1,37 +1,28 @@ + */ public function prepositions(): array { return [ - /* 2 letter */ - 'cu', - 'de', - 'în', - 'la', - 'pe', - - /* 3 letter */ - 'cât', - 'pro', - - /* 4 letter */ - 'fără', - 'până', - 'prin', - 'spre', - - /* 5 letter */ - 'între', - 'peste', - - /* 6 letter */ - 'dintre', - 'pentru', + ...self::TWO_LETTER, + ...self::THREE_LETTER, + ...self::FOUR_LETTER, + ...self::FIVE_LETTER, + ...self::SIX_LETTER, ]; } } diff --git a/src/Prepositioner/Language/SlovakLanguage.php b/src/Prepositioner/Language/SlovakLanguage.php index 7823e57..3cfb0a9 100644 --- a/src/Prepositioner/Language/SlovakLanguage.php +++ b/src/Prepositioner/Language/SlovakLanguage.php @@ -1,46 +1,26 @@ + */ public function prepositions(): array { return [ - /* 1 letter */ - 'a', - 'i', - 'k', - 'o', - 'v', - 'u', - 'z', - 's', - - /* 2 letter */ - 'do', - 'od', - 'zo', - 'ku', - 'na', - 'po', - 'so', - 'za', - 'vo', - 'či', - - /* 3 letter */ - 'cez', - 'pre', - 'nad', - 'pod', - 'pri', - - /* 4 letter */ - 'spod', - 'pred', - 'skrz', + ...self::ONE_LETTER, + ...self::TWO_LETTER, + ...self::THREE_LETTER, + ...self::FOUR_LETTER, ]; } } diff --git a/src/Prepositioner/LanguageNotExistsException.php b/src/Prepositioner/LanguageNotExistsException.php index 96f96d1..a5ce8fe 100644 --- a/src/Prepositioner/LanguageNotExistsException.php +++ b/src/Prepositioner/LanguageNotExistsException.php @@ -1,11 +1,14 @@ prepositionsArray = $prepositionsArray; - $this->escapeString = $escapeString; + private const QUOTATION_MARKS = ["\"", "'", "„", "‚", "“", "‘", "«", "‹"]; + private const SPACE_CHARACTER = " "; + + /** + * @param array $prepositionsArray + */ + public function __construct( + private readonly array $prepositionsArray, + private readonly string $escapeString = '#####' + ) { } public function formatText(string $text): string @@ -25,17 +25,19 @@ public function formatText(string $text): string } $prepositions = implode('|', $this->prepositionsArray); - $quotationMarks = implode('|', $this->quotationMarkArray); + $quotationMarks = implode('|', self::QUOTATION_MARKS); $pattern = "#(\s|^|>|;|{$quotationMarks})({$prepositions})\s+(?=[^>]*(<|$))#i"; - $replacement = "$1$2{$this->spaceCharacter}"; + $replacement = "$1$2" . self::SPACE_CHARACTER; + // Apply the pattern twice for edge cases $text = preg_replace($pattern, $replacement, $text); $text = preg_replace($pattern, $replacement, $text); - $pattern = "/{$this->escapeString}({$prepositions}){$this->escapeString}/i"; - $text = preg_replace($pattern, "$1", $text); + // Restore escaped prepositions + $escapePattern = "/{$this->escapeString}({$prepositions}){$this->escapeString}/i"; + $text = preg_replace($escapePattern, "$1", $text); - return $text; + return $text ?? ''; } } diff --git a/tests/Language/CzechLanguageTest.php b/tests/Language/CzechLanguageTest.php index 195b041..7f7cca4 100644 --- a/tests/Language/CzechLanguageTest.php +++ b/tests/Language/CzechLanguageTest.php @@ -1,15 +1,15 @@ assertInstanceOf(LanguageInterface::class, $language); + } + + #[DataProvider('languageProvider')] + public function testLanguageReturnsArray(LanguageInterface $language): void + { + $prepositions = $language->prepositions(); + $this->assertIsArray($prepositions); + + foreach ($prepositions as $preposition) { + $this->assertIsString($preposition); + $this->assertNotEmpty($preposition); + } + } + + public static function languageProvider(): array + { + return [ + 'Slovak language' => [new SlovakLanguage()], + 'Czech language' => [new CzechLanguage()], + 'Romanian language' => [new RomanianLanguage()], + 'Empty language' => [new EmptyLanguage()], + ]; + } + + public function testSlovakLanguageHasExpectedPrepositions(): void + { + $slovak = new SlovakLanguage(); + $prepositions = $slovak->prepositions(); + + $this->assertContains('a', $prepositions); + $this->assertContains('na', $prepositions); + $this->assertContains('pre', $prepositions); + $this->assertContains('pred', $prepositions); + + // Test specific Slovak prepositions + $this->assertContains('či', $prepositions); + $this->assertContains('skrz', $prepositions); + } + + public function testCzechLanguageHasExpectedPrepositions(): void + { + $czech = new CzechLanguage(); + $prepositions = $czech->prepositions(); + + $this->assertContains('a', $prepositions); + $this->assertContains('k', $prepositions); + $this->assertContains('o', $prepositions); + $this->assertContains('v', $prepositions); + } + + public function testRomanianLanguageHasExpectedPrepositions(): void + { + $romanian = new RomanianLanguage(); + $prepositions = $romanian->prepositions(); + + $this->assertContains('cu', $prepositions); + $this->assertContains('de', $prepositions); + $this->assertContains('în', $prepositions); + $this->assertContains('pentru', $prepositions); + } + + public function testEmptyLanguageReturnsEmptyArray(): void + { + $empty = new EmptyLanguage(); + $this->assertEmpty($empty->prepositions()); + } +} diff --git a/tests/Language/RomanianLanguageTest.php b/tests/Language/RomanianLanguageTest.php index a94ae7d..07880b4 100644 --- a/tests/Language/RomanianLanguageTest.php +++ b/tests/Language/RomanianLanguageTest.php @@ -1,4 +1,5 @@ assertEquals('Tomaj\Prepositioner\Prepositioner', get_class($prepositioner)); + $this->assertInstanceOf(Prepositioner::class, $prepositioner); } public function testFactoryThrowExceptionOnUnknownLanguage(): void { $this->expectException(LanguageNotExistsException::class); - $prepositioner = Factory::build('asfsdgsdgdsgf'); + $this->expectExceptionMessage("Language class '\\Tomaj\\Prepositioner\\Language\\NonexistentLanguage' doesn't exist"); + + Factory::build('nonexistent'); + } + + public function testFactorySupportsKnownLanguages(): void + { + $languages = ['slovak', 'czech', 'romanian', 'empty']; + + foreach ($languages as $language) { + $prepositioner = Factory::build($language); + $this->assertInstanceOf(Prepositioner::class, $prepositioner); + } + } + + public function testFactoryWithCustomEscapeString(): void + { + $customEscape = '###ESCAPE###'; + $prepositioner = Factory::build('empty', $customEscape); + + $this->assertInstanceOf(Prepositioner::class, $prepositioner); + } + + public function testFactoryThrowExceptionWhenClassDoesNotImplementInterface(): void + { + // We need to test the case where class exists but doesn't implement LanguageInterface + // Since our factory looks for classes in the Language namespace, we need to create + // a test that simulates this scenario. We'll use reflection to test this edge case. + + $this->expectException(LanguageNotExistsException::class); + $this->expectExceptionMessage("must implement LanguageInterface"); + + // Create a temporary class file to test this scenario + $tempClassName = 'Tomaj\\Prepositioner\\Language\\TestInvalidLanguage'; + + // We need to use eval to create a class in the correct namespace for this test + // phpcs:ignore PSR1.Classes.ClassDeclaration.MultipleClasses + eval(' + namespace Tomaj\\Prepositioner\\Language; + class TestInvalidLanguage { + public function notTheRightMethod() { + return []; + } + } + '); + + Factory::build('testInvalid'); } } diff --git a/tests/PrepositionerTest.php b/tests/PrepositionerTest.php index c4ef539..aca0155 100644 --- a/tests/PrepositionerTest.php +++ b/tests/PrepositionerTest.php @@ -1,22 +1,53 @@ assertEquals("dsfoihdf s asd a sdfds asdf asd", $prepositioner->formatText($input)); + $this->assertEquals($expected, $prepositioner->formatText($input)); + } + + public static function basicFormatProvider(): array + { + return [ + 'basic preposition replacement' => [ + ['a', 'asdf', 'vd'], + "dsfoihdf s asd a sdfds asdf asd", + "dsfoihdf s asd a sdfds asdf asd" + ], + 'no replacement in middle of words' => [ + ['a'], + "dsfdsfa dfsdg", + "dsfdsfa dfsdg" + ], + 'first word replacement' => [ + ['prvé'], + "prvé slovo", + "prvé slovo" + ], + 'multiple same prepositions' => [ + ['a'], + "a slovo a ešte", + "a slovo a ešte" + ], + 'case insensitive' => [ + ['a'], + "A slovo", + "A slovo" + ], + ]; } public function testInWordReplace(): void @@ -29,119 +60,129 @@ public function testInWordReplace(): void public function testFirstWord(): void { - $words = ['a']; + $words = ['prvé']; $prepositioner = new Prepositioner($words); - $input = "a asfs a asfd"; - $this->assertEquals("a asfs a asfd", $prepositioner->formatText($input)); + $input = "prvé slovo"; + $this->assertEquals("prvé slovo", $prepositioner->formatText($input)); } - public function testLastWord(): void + public function testMultipleSamePrepositions(): void { $words = ['a']; $prepositioner = new Prepositioner($words); - $input = "asfd a"; - $this->assertEquals("asfd a", $prepositioner->formatText($input)); + $input = "a slovo a ešte"; + $this->assertEquals("a slovo a ešte", $prepositioner->formatText($input)); } - public function testSimplePreposition(): void + public function testCaseInsensitive(): void { $words = ['a']; $prepositioner = new Prepositioner($words); - $input = "a"; - $this->assertEquals("a", $prepositioner->formatText($input)); + $input = "A slovo"; + $this->assertEquals("A slovo", $prepositioner->formatText($input)); } - public function testUpperLowerCase(): void + public function testAnotherCaseInsensitive(): void { - $words = ['a', 'AsD', 'C', 'EE']; + $words = ['a']; $prepositioner = new Prepositioner($words); - $input = "A acd asd fef c xxx Ee grgr"; - $this->assertEquals("A acd asd fef c xxx Ee grgr", $prepositioner->formatText($input)); + $input = "slovo A slovo"; + $this->assertEquals("slovo A slovo", $prepositioner->formatText($input)); } - public function testMultipleSpaces(): void + public function testEmpty(): void { - $words = ['a']; + $words = []; $prepositioner = new Prepositioner($words); - $input = "asd a asd"; - $this->assertEquals("asd a asd", $prepositioner->formatText($input)); + $input = "a slovo a ešte"; + $this->assertEquals("a slovo a ešte", $prepositioner->formatText($input)); } - public function testHtmlTextElementReplace(): void + public function testAfterComma(): void { $words = ['a']; $prepositioner = new Prepositioner($words); - $input = "asd a fs sa a sd"; - $this->assertEquals("asd a fs sa a sd", $prepositioner->formatText($input)); + $input = ", a slovo"; + $this->assertEquals(", a slovo", $prepositioner->formatText($input)); } - - public function testHtmlContentDoesntReplace(): void + + public function testAfterOpeningQuote(): void { $words = ['a']; $prepositioner = new Prepositioner($words); - $input = "asd a fs

sdasd

"; - $this->assertEquals("asd a fs

sdasd

", $prepositioner->formatText($input)); - - $input = "asd a fs

sdasd

"; - $this->assertEquals("asd a fs

sdasd

", $prepositioner->formatText($input)); + $input = '„a slovo"'; + $this->assertEquals('„a slovo"', $prepositioner->formatText($input)); } - public function testSpecialCharacters(): void + public function testInHtml(): void { $words = ['a']; $prepositioner = new Prepositioner($words); - $input = "asd a\t\tx a\nasdcdcd a
asd"; - $this->assertEquals("asd a x a asdcdcd a
asd", $prepositioner->formatText($input)); - - $input = "asd\t\ta\tx \na asdcdcd a
asd"; - $this->assertEquals("asd\t\ta x \na asdcdcd a
asd", $prepositioner->formatText($input)); + $input = "
a slovo
"; + $this->assertEquals("
a slovo
", $prepositioner->formatText($input)); } - public function testFirstWordInTag(): void + public function testInHtmlAttribute(): void { $words = ['a']; $prepositioner = new Prepositioner($words); - $input = "

a bout

"; - $this->assertEquals("

a bout

", $prepositioner->formatText($input)); - } - - public function testDisablePrepositionsReplace(): void - { - $words = ['a', 'b']; - $prepositioner = new Prepositioner($words, '#####'); - $input = "asd #####a##### asdsa b cc a asd s b"; - $this->assertEquals("asd a asdsa b cc a asd s b", $prepositioner->formatText($input)); + $input = '
some content
'; + $this->assertEquals('
some content
', $prepositioner->formatText($input)); } - public function testMorePreposition(): void + public function testEscaping(): void { - $words = ['a', 'b', 'c']; + $words = ['a']; $prepositioner = new Prepositioner($words); - $input = "asd a c b asd b c"; - $this->assertEquals("asd a c b asd b c", $prepositioner->formatText($input)); + $input = "test #####a##### escaped"; + $this->assertEquals("test a escaped", $prepositioner->formatText($input)); } - public function testMultiplePreposition(): void + public function testCustomEscapeString(): void { - $words = ['a', 'b', 'c']; - $prepositioner = new Prepositioner($words); - $input = "a b c a b b c"; - $this->assertEquals("a b c a b b c", $prepositioner->formatText($input)); + $words = ['a']; + $customEscape = '%%%'; + $prepositioner = new Prepositioner($words, $customEscape); + $input = "test %%%a%%% escaped"; + $this->assertEquals("test a escaped", $prepositioner->formatText($input)); } - public function testPrepositionAfterStraightQuotationMark(): void + public function testComplexText(): void { - $words = ['on', 'to', 'the']; + $words = ['a', 'o', 'v', 'na']; $prepositioner = new Prepositioner($words); - $input = 'He said: "on to the hill, man"'; - $this->assertEquals('He said: "on to the hill, man"', $prepositioner->formatText($input)); + $input = "Toto je text a tu máme predložky o ktorých sa bavíme. Sú v texte na rôznych miestach."; + $expected = "Toto je text a tu máme predložky o ktorých sa bavíme. Sú v texte na rôznych miestach."; + $this->assertEquals($expected, $prepositioner->formatText($input)); } - public function testPrepositionAfterLeftDoubleQuotationMark(): void + #[DataProvider('htmlTestProvider')] + public function testHtmlContexts(string $input, string $expected): void { - $words = ['on', 'to', 'the']; + $words = ['a', 'v']; $prepositioner = new Prepositioner($words); - $input = 'He said: “on to the hill, man”'; - $this->assertEquals('He said: “on to the hill, man”', $prepositioner->formatText($input)); + $this->assertEquals($expected, $prepositioner->formatText($input)); + } + + public static function htmlTestProvider(): array + { + return [ + 'simple html tag' => [ + '

a slovo v texte

', + '

a slovo v texte

' + ], + 'html with attributes' => [ + '
a slovo
', + '
a slovo
' + ], + 'nested html' => [ + '
a slovo v texte
', + '
a slovo v texte
' + ], + 'html attribute should not be changed' => [ + 'a slovo', + 'a slovo' + ], + ]; } }