diff --git a/.gitattributes b/.gitattributes
index ecb4618d..3323417e 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -8,4 +8,5 @@ composer.json export-ignore
composer.lock export-ignore
grumphp.yml export-ignore
phpcs.xml export-ignore
-psalm.xml export-ignore
\ No newline at end of file
+psalm.xml export-ignore
+tests export-ignore
\ No newline at end of file
diff --git a/.github/workflows/php-quality.yml b/.github/workflows/php-quality.yml
index 3887269a..b4ef1524 100644
--- a/.github/workflows/php-quality.yml
+++ b/.github/workflows/php-quality.yml
@@ -1,6 +1,6 @@
name: PHP Quality
-on: [push, pull_request]
+on: [ pull_request ]
jobs:
run:
@@ -8,7 +8,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- php-versions: [ 7.4' ]
+ php-versions: [ '7.4' ]
name: PHP ${{ matrix.php-versions }} Test
steps:
- name: Checkout
@@ -18,8 +18,6 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
- extensions: mbstring, intl #optional, setup extensions
-
- name: Check composer
run: composer validate
@@ -32,4 +30,13 @@ jobs:
- name: Check psalm
run: |
mkdir -p dist
- composer psalm
\ No newline at end of file
+ composer psalm
+
+ - name: Check unit test
+ run: composer tests-cov -- --coverage-clover=./tests/coverage/xml/index.xml
+
+ - name: Run codacy-coverage-reporter
+ uses: codacy/codacy-coverage-reporter-action@v1
+ with:
+ project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
+ coverage-reports: tests/coverage/xml/index.xml
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index d34578a3..a841e817 100644
--- a/.gitignore
+++ b/.gitignore
@@ -75,3 +75,7 @@ package-lock.json
### config
/vendor/
composer.lock
+
+### Tests
+.phpunit.result.cache
+tests/coverage/
\ No newline at end of file
diff --git a/composer.json b/composer.json
index aaeac585..45fb53d6 100644
--- a/composer.json
+++ b/composer.json
@@ -9,7 +9,8 @@
"phpro/grumphp-shim": "^0.19.1",
"squizlabs/php_codesniffer": "^3.5",
"vimeo/psalm": "^3.13",
- "wp-coding-standards/wpcs": "^2.3"
+ "wp-coding-standards/wpcs": "^2.3",
+ "10up/wp_mock": "^0.4.2"
},
"scripts": {
"cs": [
@@ -20,7 +21,9 @@
],
"psalm": [
"./vendor/bin/psalm"
- ]
+ ],
+ "tests": "./vendor/bin/phpunit",
+ "tests-cov": "phpdbg -qrr ./vendor/bin/phpunit --coverage-html ./tests/coverage/"
},
"autoload": {
"psr-4": {
diff --git a/inc/Services/Acf.php b/inc/Services/Acf.php
index 34373dbf..1ce64020 100644
--- a/inc/Services/Acf.php
+++ b/inc/Services/Acf.php
@@ -52,7 +52,7 @@ public function warning(): void {
return;
}
- wp_die( sprintf( __( 'This theme can\'t work without ACF plugin. Please login to admin, and activate it !', 'framework-textdomain' ), esc_url( wp_login_url() ) ) ); // phpcs:ignore WordPress.WP.I18n.MissingTranslatorsComment, WordPress.Security.EscapeOutput.OutputNotEscaped
+ \wp_die( sprintf( __( 'This theme can\'t work without ACF plugin. Please login to admin, and activate it !', 'framework-textdomain' ), esc_url( wp_login_url() ) ) ); // phpcs:ignore WordPress.WP.I18n.MissingTranslatorsComment, WordPress.Security.EscapeOutput.OutputNotEscaped
}
/**
@@ -147,7 +147,6 @@ public function init_acf(): void {
if ( ! is_file( get_theme_file_path( $this->path . $file . '.php' ) ) ) {
continue;
}
-
require_once get_theme_file_path( $this->path . $file . '.php' );
}
}
diff --git a/inc/Services/Assets.php b/inc/Services/Assets.php
index 032e3138..76631da0 100644
--- a/inc/Services/Assets.php
+++ b/inc/Services/Assets.php
@@ -59,17 +59,17 @@ public function register_assets(): void {
// Js theme
// Theme js dependencies
- $scripts_dependencies = [ 'jquery' ];
+ $scripts_dependencies = [ 'jquery', 'global-polyfill' ];
- // Async and footer
- $file = $this->is_minified() ? $this->get_min_file( 'js' ) : 'app.js';
+ // Polyfill
+ \wp_register_script( 'global-polyfill', 'https://cdn.polyfill.io/v3/polyfill.min.js?features=es5,es6,fetch,Array.prototype.includes,CustomEvent,Element.prototype.closest,NodeList.prototype.forEach', null, null, true ); //phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
- // Do not add version if minified
- $version = $this->is_minified() ? null : $theme->get( 'Version' );
- $this->assets_tools->register_script( 'scripts', 'dist/' . $file, $scripts_dependencies, $version, true );
+ // Async and footer
+ $file = ( ! defined( 'SCRIPT_DEBUG' ) || SCRIPT_DEBUG === false ) ? $this->get_min_file( 'js' ) : 'app.js';
+ $this->assets_tools->register_script( 'scripts', 'dist/' . $file, $scripts_dependencies, $theme->get( 'Version' ), true );
// CSS
- wp_register_style( 'theme-style', get_stylesheet_uri(), [], $version );
+ \wp_register_style( 'theme-style', \get_stylesheet_uri(), [], $theme->get( 'Version' ) );
}
/**
@@ -97,7 +97,8 @@ public function enqueue_styles(): void {
* @author Nicolas Juen
*/
public function stylesheet_uri( string $stylesheet_uri ): string {
- if ( $this->is_minified() ) {
+ if ( ! defined( 'SCRIPT_DEBUG' ) || SCRIPT_DEBUG === false ) {
+
$file = $this->get_min_file( 'css' );
if ( ! empty( $file ) && file_exists( \get_theme_file_path( '/dist/' . $file ) ) ) {
return \get_theme_file_uri( '/dist/' . $file );
@@ -167,21 +168,11 @@ public function get_min_file( string $type ): string {
return $file;
}
- /**
- * Check if we are on minified environment.
- *
- * @return bool
- * @author Nicolas JUEN
- */
- private function is_minified(): bool {
- return ( ! defined( 'SCRIPT_DEBUG' ) || SCRIPT_DEBUG === false );
- }
-
/**
* Change login CSS URL
* @return string
*/
public function login_stylesheet_uri(): string {
- return $this->is_minified() ? 'dist/' . $this->get_min_file( 'login' ) : 'dist/login.css';
+ return ( ! defined( 'SCRIPT_DEBUG' ) || SCRIPT_DEBUG === false ) ? 'dist/' . $this->get_min_file( 'login' ) : 'dist/login.css';
}
}
diff --git a/phpcs.xml b/phpcs.xml
index d3a05a26..10ed28a3 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -14,6 +14,7 @@
dist/
src/
scripts/
+ tests/
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 00000000..2c91f844
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,14 @@
+
+
+
+
+ functions.php
+ inc
+
+
+
+
+ tests
+
+
+
diff --git a/psalm.xml b/psalm.xml
index a1814797..44c2e5b6 100644
--- a/psalm.xml
+++ b/psalm.xml
@@ -13,6 +13,7 @@
+
diff --git a/tests/FrameworkTest.php b/tests/FrameworkTest.php
new file mode 100644
index 00000000..f1ab22b3
--- /dev/null
+++ b/tests/FrameworkTest.php
@@ -0,0 +1,37 @@
+assertFalse( Framework::get_container()->get_service( 'not-a-service' ) );
+ }
+
+ public function testSameContainer() {
+ $container = Framework::get_container();
+
+ $this->assertSame( $container, Framework::get_container() );
+ }
+
+ public function testServiceSet() {
+ Framework::register_service( Acf::class );
+
+ $this->assertNotEmpty( Framework::get_container()->get_service( Acf::class ) );
+ }
+}
diff --git a/tests/Helpers/Formatting/EscapeTest.php b/tests/Helpers/Formatting/EscapeTest.php
new file mode 100644
index 00000000..82617434
--- /dev/null
+++ b/tests/Helpers/Formatting/EscapeTest.php
@@ -0,0 +1,40 @@
+assertSame( 'ok', $value );
+
+ $value = escape_attribute_value( 'ok', 'test_escape' );
+ $this->assertSame( 'esc_ok', $value );
+ }
+ public function testContent() {
+ $value = escape_content_value( 'ok', '' );
+ $this->assertSame( 'ok', $value );
+
+ $value = escape_content_value( 'ok', 'test_escape' );
+ $this->assertSame( 'esc_ok', $value );
+ }
+}
+
diff --git a/tests/Helpers/Formatting/ImageTest.php b/tests/Helpers/Formatting/ImageTest.php
new file mode 100644
index 00000000..4a691329
--- /dev/null
+++ b/tests/Helpers/Formatting/ImageTest.php
@@ -0,0 +1,55 @@
+ '',
+ ]
+ );
+
+ $this->assertSame( '', get_the_image( 0, [] ) );
+ }
+
+ public function testGetImageEmpty() {
+ WP_Mock::userFunction( 'wp_get_attachment_image', [
+ 'return' => '',
+ ]
+ );
+
+ $this->assertSame( '', get_the_image( 10, [ 'size' => '', 'data-location' => '' ] ) );
+ }
+
+ public function testImageBeforeAfter() {
+ WP_Mock::userFunction( 'wp_get_attachment_image', [
+ 'return' => '
',
+ ]
+ );
+
+ $this->assertSame( 'b
', get_the_image( 10, [], [ 'before' => 'b' ] ) );
+ $this->assertSame( '
a', get_the_image( 10, [], [ 'after' => 'a' ] ) );
+ $this->assertSame( 'b
a', get_the_image( 10, [], [ 'after' => 'a', 'before' => 'b' ] ) );
+ }
+}
+
diff --git a/tests/Helpers/Formatting/LinkTest.php b/tests/Helpers/Formatting/LinkTest.php
new file mode 100644
index 00000000..9cc5ccea
--- /dev/null
+++ b/tests/Helpers/Formatting/LinkTest.php
@@ -0,0 +1,202 @@
+assertSame( '', get_the_link( [] ) );
+ }
+
+ public function testGetTheLinkTargetBlank() {
+
+ // Auto noopener
+ $this->assertSame(
+ 'New window',
+ get_the_link(
+ [
+ 'href' => 'https://localhost.dev',
+ 'target' => '_blank',
+ 'title' => 'TITLE LINK',
+ ]
+ )
+ );
+
+ // self
+ $this->assertSame(
+ '',
+ get_the_link(
+ [
+ 'href' => 'https://localhost.dev',
+ 'target' => '_self',
+ ]
+ )
+ );
+
+ // self title
+ $this->assertSame(
+ '',
+ get_the_link(
+ [
+ 'href' => 'https://localhost.dev',
+ 'target' => '_self',
+ 'title' => 'TITLE LINK',
+ ]
+ )
+ );
+
+ // Custom attribute
+ $this->assertSame(
+ '',
+ get_the_link(
+ [
+ 'href' => 'https://localhost.dev',
+ 'data-seo' => 'ok',
+ ]
+ )
+ );
+ $this->assertSame(
+ '',
+ get_the_link(
+ [
+ 'href' => 'https://localhost.dev',
+ 'empty-data' => null,
+ ]
+ )
+ );
+
+ // Before/after
+ $this->assertSame(
+ 'b',
+ get_the_link(
+ [
+ 'href' => 'https://localhost.dev',
+ ],
+ [
+ 'before' => 'b',
+ ]
+ )
+ );
+ $this->assertSame(
+ 'a',
+ get_the_link(
+ [
+ 'href' => 'https://localhost.dev',
+ ],
+ [
+ 'after' => 'a',
+ ]
+ )
+ );
+ $this->assertSame(
+ 'ba',
+ get_the_link(
+ [
+ 'href' => 'https://localhost.dev',
+ ],
+ [
+ 'after' => 'a',
+ 'before' => 'b',
+ ]
+ )
+ );
+
+ $this->assertSame(
+ 'Content',
+ get_the_link(
+ [
+ 'href' => 'https://localhost.dev',
+ ],
+ [
+ 'content' => 'Content',
+ ]
+ )
+ );
+
+ $this->assertSame(
+ 'Content',
+ get_the_link(
+ [
+ 'href' => 'https://localhost.dev',
+ 'title' => 'TITLE',
+ ],
+ [
+ 'content' => 'Content',
+ ]
+ )
+ );
+ }
+
+ public function testGetAcfLinkEmptyURLOrTitle() {
+ $this->assertSame( '', get_acf_link( [] ) );
+ $this->assertSame( '', get_acf_link( [ 'field' => [ 'url' => 'ok' ] ] ) );
+ $this->assertSame( '', get_acf_link( [ 'field' => [ 'title' => 'ok' ] ] ) );
+ }
+
+ public function testGetAcfLinkWithAttributes() {
+ $this->assertSame(
+ 'New windowTitle',
+ get_acf_link(
+ [
+ 'field' => [
+ 'title' => 'Title',
+ 'url' => 'https://localhost.dev',
+ 'target' => '_blank',
+ ],
+ ]
+ )
+ );
+
+ $this->assertSame(
+ 'Title',
+ get_acf_link(
+ [
+ 'field' => [
+ 'title' => 'Title',
+ 'url' => 'https://localhost.dev',
+ 'target' => '',
+ ],
+ ]
+ )
+ );
+
+ $this->assertSame(
+ 'CONTENT',
+ get_acf_link(
+ [
+ 'field' => [
+ 'title' => 'Title',
+ 'url' => 'https://localhost.dev',
+ 'target' => '',
+ ],
+ ],
+ [
+ 'content' => 'CONTENT',
+ ]
+ )
+ );
+ }
+}
+
diff --git a/tests/Helpers/Formatting/TextTest.php b/tests/Helpers/Formatting/TextTest.php
new file mode 100644
index 00000000..1d8d4263
--- /dev/null
+++ b/tests/Helpers/Formatting/TextTest.php
@@ -0,0 +1,140 @@
+assertEmpty( $test );
+
+ ob_start();
+ the_text( '' );
+ $buffer = ob_get_clean();
+ $this->assertSame( $test, $buffer );
+ }
+
+ public function testBeforeAfter() {
+ $val = get_the_text( 'val', [ 'before' => 'b' ] );
+ $val2 = get_the_text( 'val', [ 'after' => 'a' ] );
+ $val3 = get_the_text( 'val', [ 'before' => 'b', 'after' => 'a' ] );
+
+ ob_start();
+ the_text( 'val', [ 'before' => 'b' ] );
+ $buffer = ob_get_clean();
+
+ ob_start();
+ the_text( 'val', [ 'after' => 'a' ] );
+ $buffer2 = ob_get_clean();
+
+ ob_start();
+ the_text( 'val', [ 'before' => 'b', 'after' => 'a' ] );
+ $buffer3 = ob_get_clean();
+
+ // Test returns
+ $this->assertSame( 'bval', $val );
+ $this->assertSame( 'vala', $val2 );
+ $this->assertSame( 'bvala', $val3 );
+
+ // Test Buffer and generated the same
+ $this->assertSame( $val, $buffer );
+ $this->assertSame( $val2, $buffer2 );
+ $this->assertSame( $val3, $buffer3 );
+ }
+
+ public function testEscape() {
+
+
+ $this->assertSame( 'esc_val', get_the_text( 'val', [
+ 'before' => '',
+ 'after' => '',
+ 'escape' => 'test_escape',
+ ] ) );
+ $this->assertSame( 'besc_val', get_the_text( 'val', [
+ 'before' => 'b',
+ 'after' => '',
+ 'escape' => 'test_escape',
+ ] ) );
+ $this->assertSame( 'esc_vala', get_the_text( 'val', [
+ 'before' => '',
+ 'after' => 'a',
+ 'escape' => 'test_escape',
+ ] ) );
+ $this->assertSame( 'besc_vala', get_the_text( 'val', [
+ 'before' => 'b',
+ 'after' => 'a',
+ 'escape' => 'test_escape',
+ ] ) );
+ }
+
+ public function testFilterSettings() {
+
+ WP_Mock::onFilter( 'bea_theme_framework_text_settings' )->with(
+ [
+ 'before' => '',
+ 'after' => '',
+ 'escape' => 'test_escape',
+ ],
+ 'val'
+ )->reply(
+ [
+ 'before' => 'b',
+ 'after' => 'a',
+ 'escape' => 'esc_html',
+ ]
+ );
+
+ $this->assertSame(
+ 'bvala',
+ get_the_text(
+ 'val',
+ [
+ 'before' => 'b',
+ 'after' => 'a',
+ 'escape' => 'esc_html',
+ ]
+ )
+ );
+ }
+
+ public function testFilterValue() {
+
+ WP_Mock::onFilter( 'bea_theme_framework_text_value' )->with(
+ 'esc_val',
+ [
+ 'before' => '',
+ 'after' => '',
+ 'escape' => 'test_escape',
+ ]
+ )->reply( 'filtered' );
+
+ $this->assertSame(
+ 'filtered',
+ get_the_text(
+ 'val',
+ [
+ 'before' => '',
+ 'after' => '',
+ 'escape' => 'test_escape',
+ ]
+ )
+ );
+ }
+}
+
diff --git a/tests/Services/AcfTest.php b/tests/Services/AcfTest.php
new file mode 100644
index 00000000..a5f602ae
--- /dev/null
+++ b/tests/Services/AcfTest.php
@@ -0,0 +1,117 @@
+assertEquals( 'acf', $acf->get_service_name() );
+ }
+
+ public function testWarning() {
+ $acf = new Acf();
+
+ WP_Mock::userFunction( 'wp_die', [
+ 'times' => 1,
+ ] );
+
+ WP_Mock::userFunction( 'esc_url', [
+ 'times' => 1,
+ ] );
+
+ WP_Mock::userFunction( 'wp_login_url', [
+ 'times' => 1,
+ ] );
+ $acf->warning();
+
+ // Test we do not launch the functions on existing get_field function
+ WP_Mock::userFunction( 'get_field' );
+ $acf->warning();
+ }
+
+ public function testGetSetFiles() {
+ $acf = new Acf();
+
+ $acf->register_files( [ 'myfile' ] );
+ $this->assertSame( [ 'myfile' => 'myfile' ], $acf->get_files() );
+
+ $acf->register_files( [ 'myfile', '' ] );
+ $this->assertSame( [ 'myfile' => 'myfile' ], $acf->get_files() );
+
+ $acf->register_files( [ 'myfile' ] );
+ $this->assertSame( [ 'myfile' => 'myfile' ], $acf->get_files() );
+ }
+
+ public function testRegisterOptionPage() {
+ $acf = new Acf();
+
+ // Function not existing
+ $this->assertFalse( $acf->acf_add_options_page( [] ) );
+
+ WP_Mock::userFunction( 'acf_add_options_page', [ 'return' => 'ok' ] );
+ $this->assertSame( 'ok', $acf->acf_add_options_page( [ 'menu_slug' => 'ok' ] ) );
+
+ // Exception is ok
+ $this->expectException( InvalidArgumentException::class );
+ $acf->acf_add_options_page( [] );
+
+ }
+
+ public function testRegisterOptionSubPage() {
+ $acf = new Acf();
+
+ // Function not existing
+ $this->assertFalse( $acf->acf_add_options_sub_page( [] ) );
+
+ WP_Mock::userFunction( 'acf_add_options_sub_page', [ 'return' => 'ok' ] );
+ $this->assertSame( 'ok', $acf->acf_add_options_sub_page( [ 'menu_slug' => 'ok' ] ) );
+
+ // Exception is ok
+ $this->expectException( InvalidArgumentException::class );
+ $acf->acf_add_options_sub_page( [] );
+ }
+
+ public function testPath() {
+ $acf = new Acf();
+
+ // Function not existing
+ $acf->set_path( 'test/path' );
+ }
+
+ public function testInitAcf() {
+ $acf = new Acf();
+
+ // Function not existing
+ $this->assertNull( $acf->init_acf() );
+
+ WP_Mock::userFunction( 'get_theme_file_path', [
+ 'return_in_order' => [
+ __DIR__ . '/../data/',
+ __DIR__ . '/../data/myfile.php',
+ __DIR__ . '/../data/myfile.php',
+ __DIR__ . '/../data/myfile2.php',
+ __DIR__ . '/../data/myfile3.php',
+ ],
+ 'times' => 5,
+ ] );
+
+ $acf->register_files( [ 'myfile', 'myfile2', 'myfile3' ] );
+ $acf->init_acf();
+ }
+
+}
diff --git a/tests/Services/AssetsTest.php b/tests/Services/AssetsTest.php
new file mode 100644
index 00000000..a05ec408
--- /dev/null
+++ b/tests/Services/AssetsTest.php
@@ -0,0 +1,211 @@
+assertEquals( 'assets', $assets->get_service_name() );
+ }
+
+ public function testStyleSheetURIWithScriptDebugAndNoFiles() {
+ $assets = new Assets();
+
+ define( 'SCRIPT_DEBUG', true );
+
+ WP_Mock::userFunction( 'get_theme_file_path', [
+ 'times' => 1,
+ 'return' => 'ok.css',
+ 'args' => [ '/dist/app.css' ],
+ ] );
+
+ $this->assertSame( 'ok.css', $assets->stylesheet_uri( 'ok.css' ) );
+ }
+
+ public function testStyleSheetURIWithScriptDebugAndFiles() {
+ $assets = new Assets();
+
+ define( 'SCRIPT_DEBUG', true );
+
+ $assets_file = __DIR__ . '/../data/assets/app.css';
+
+ WP_Mock::userFunction(
+ 'get_theme_file_path',
+ [
+ 'times' => 1,
+ 'return' => $assets_file,
+ 'args' => [
+ '/dist/app.css',
+ ],
+ ]
+ );
+ WP_Mock::userFunction(
+ 'get_theme_file_uri',
+ [
+ 'times' => 1,
+ 'args' => [
+ '/dist/app.css',
+ ],
+ 'return' => 'https://localhost.dev/data/assets/app.css',
+ ]
+ );
+
+ $this->assertSame( 'https://localhost.dev/data/assets/app.css', $assets->stylesheet_uri( 'ok.css' ) );
+ }
+
+ public function testStyleSheetURIWithOutScriptDebugAndNoFiles() {
+ $assets = new Assets();
+
+ define( 'SCRIPT_DEBUG', false );
+
+ WP_Mock::passthruFunction( 'get_theme_file_path', [ 'times' => 2 ] );
+
+ $this->assertSame( 'ok.css', $assets->stylesheet_uri( 'ok.css' ) );
+ }
+
+ public function testGetMinFileEmpty() {
+ $assets = new Assets();
+
+ $this->assertSame( '', $assets->get_min_file( '' ) );
+ }
+
+ public function testGetMinFileAssetsExistsAndEmpty() {
+ $assets = new Assets();
+ $assets_file = __DIR__ . '/../data/assets/assets-empty.json';
+
+ WP_Mock::userFunction( 'get_theme_file_path', [ 'args' => '/dist/assets.json', 'return' => $assets_file ] );
+
+ $this->assertSame( '', $assets->get_min_file( 'css' ) );
+ }
+
+ public function testGetMinFileAssetsExistsNotEmptyNonExistingType() {
+ $assets = new Assets();
+ $assets_file = __DIR__ . '/../data/assets/assets.json';
+
+ WP_Mock::userFunction( 'get_theme_file_path', [ 'args' => '/dist/assets.json', 'return' => $assets_file ] );
+
+ $this->assertSame( '', $assets->get_min_file( 'non-existing' ) );
+ }
+
+ public function testGetMinFileAssetsExistsNotEmptyExistingType() {
+ $assets = new Assets();
+ $assets_file = __DIR__ . '/../data/assets/assets.json';
+
+ WP_Mock::userFunction( 'get_theme_file_path', [ 'args' => '/dist/assets.json', 'return' => $assets_file ] );
+
+ // Existing
+ $this->assertSame( 'app.min.css', $assets->get_min_file( 'css' ) );
+ $this->assertSame( 'app.min.js', $assets->get_min_file( 'js' ) );
+ $this->assertSame( 'editor.min.css', $assets->get_min_file( 'editor.css' ) );
+ $this->assertSame( 'editor.min.js', $assets->get_min_file( 'editor.js' ) );
+ $this->assertSame( 'login.min.css', $assets->get_min_file( 'login' ) );
+
+ // Custom
+ $this->assertSame( 'custom.min.css', $assets->get_min_file( 'custom.css' ) );
+
+ // Non existing
+ $this->assertSame( '', $assets->get_min_file( 'custom.min.css' ) );
+ }
+
+ public function testGetLoginStyleSheet() {
+ $assets = new Assets();
+ $assets_file = __DIR__ . '/../data/assets/assets.json';
+
+ WP_Mock::userFunction( 'get_theme_file_path', [ 'args' => '/dist/assets.json', 'return' => $assets_file ] );
+
+ // Existing
+ $this->assertSame( 'dist/login.min.css', $assets->login_stylesheet_uri() );
+ }
+
+ public function testGetLoginStyleSheetDebug() {
+ $assets = new Assets();
+ $assets_file = __DIR__ . '/../data/assets/assets.json';
+ define( 'SCRIPT_DEBUG', true );
+
+ WP_Mock::userFunction( 'get_theme_file_path', [ 'args' => '/dist/assets.json', 'return' => $assets_file ] );
+
+ // Existing
+ $this->assertSame( 'dist/login.css', $assets->login_stylesheet_uri() );
+ }
+
+ public function testRegisterFilesAdmin() {
+ $assets = new Assets();
+
+ WP_Mock::userFunction( 'is_admin', [ 'return' => true, 'times' => 1 ] );
+ WP_Mock::passthruFunction( 'wp_get_theme', [ 'times' => 0 ] );
+
+ $this->assertNull( $assets->register_assets() );
+ }
+
+ public function testRegisterFiles() {
+ $assets = new Assets();
+ $container = Framework::get_container();
+ $theme_mock = $this->getMockBuilder( stdclass::class )->addMethods( [ 'get' ] )->getMock();
+ $assets->register( $container );
+
+ WP_Mock::userFunction(
+ 'is_admin',
+ [
+ 'return' => false,
+ 'times' => 1,
+ ]
+ );
+ WP_Mock::userFunction(
+ 'wp_get_theme',
+ [
+ 'times' => 1,
+ 'return' => $theme_mock,
+ ]
+ );
+ WP_Mock::passthruFunction( 'wp_register_script', [ 'times' => 2 ] );
+ WP_Mock::passthruFunction( 'wp_register_style', [ 'times' => 1 ] );
+ WP_Mock::passthruFunction( 'get_theme_file_path', [ 'times' => 1 ] );
+ WP_Mock::passthruFunction( 'get_theme_file_uri' );
+ WP_Mock::userFunction( 'get_stylesheet_uri', [ 'return' => false ] );
+
+ // Check the version is called
+ $theme_mock->expects( $this->atLeastOnce() )->method( 'get' );
+
+ $this->assertNull( $assets->register_assets() );
+ }
+
+ public function testCheckFiltersAdded() {
+ $assets = new Assets();
+
+ // Check filters
+ WP_Mock::expectFilterAdded( 'stylesheet_uri', [ $assets, 'stylesheet_uri' ] );
+ WP_Mock::expectFilterAdded( 'wp_login_page_theme_css', [ $assets, 'login_stylesheet_uri' ] );
+
+ // Check Action
+ WP_Mock::expectActionAdded( 'wp', [ $assets, 'register_assets' ] );
+ WP_Mock::expectActionAdded( 'wp_enqueue_scripts', [ $assets, 'enqueue_scripts' ] );
+ WP_Mock::expectActionAdded( 'wp_print_styles', [ $assets, 'enqueue_styles' ] );
+
+ $assets->boot( Framework::get_container() );
+ }
+}
diff --git a/tests/Services/SvgTest.php b/tests/Services/SvgTest.php
new file mode 100644
index 00000000..a151c125
--- /dev/null
+++ b/tests/Services/SvgTest.php
@@ -0,0 +1,86 @@
+assertEquals( 'svg', $svg->get_service_name() );
+ }
+
+ public function testTags() {
+ $svg = new Svg();
+ $this->assertIsArray( $svg->allow_svg_tag( [] ) );
+ $this->assertArrayHasKey( 'path', $svg->allow_svg_tag( [] ) );
+ $this->assertArrayHasKey( 'svg', $svg->allow_svg_tag( [] ) );
+ $this->assertArrayHasKey( 'use', $svg->allow_svg_tag( [] ) );
+ }
+
+ public function testRegister() {
+ $svg = new Svg();
+ $container = $this->createStub( Service_Container::class );
+
+ WP_Mock::expectFilterAdded( 'wp_kses_allowed_html', [ $svg, 'allow_svg_tag' ] );
+
+ $svg->register( $container );
+ }
+
+ public function testEmptyIcon() {
+ $svg = new Svg();
+ $this->assertEmpty( $svg->get_the_icon('') );
+ }
+
+ public function testGetIcon() {
+ $svg = new Svg();
+ WP_Mock::passthruFunction( 'sanitize_html_class' );
+ WP_Mock::userFunction( 'get_theme_file_uri', [
+ 'return' => 'test.example.fr',
+ ] );
+
+ $this->assertSame(
+ '',
+ $svg->get_the_icon( 'test' )
+ );
+ }
+ public function testTheIcon() {
+ $svg = new Svg();
+ WP_Mock::passthruFunction( 'sanitize_html_class' );
+ WP_Mock::userFunction( 'get_theme_file_uri', [
+ 'return' => 'test.example.fr',
+ ] );
+
+ ob_start();
+ $this->AssertNull( $svg->the_icon( 'test' ) );
+ $this->AssertNull( $svg->the_icon( 'test', [ 'class1', 'class2' ] ) );
+ ob_end_clean();
+ }
+
+ public function testGetIconWithClass() {
+ $svg = new Svg();
+ WP_Mock::passthruFunction( 'sanitize_html_class' );
+
+ WP_Mock::userFunction( 'get_theme_file_uri', [
+ 'return' => 'test.example.fr',
+ ] );
+
+ $this->assertSame(
+ '',
+ $svg->get_the_icon( 'test', [ 'class2', 'class3' ] )
+ );
+ }
+}
diff --git a/tests/Tools/AssetsTest.php b/tests/Tools/AssetsTest.php
new file mode 100644
index 00000000..cde1f4e8
--- /dev/null
+++ b/tests/Tools/AssetsTest.php
@@ -0,0 +1,88 @@
+ 1 ] );
+ WP_Mock::passthruFunction(
+ 'wp_register_script',
+ [
+ 'times' => 1,
+ 'args' => [
+ 'handle',
+ 'src',
+ [],
+ false,
+ false,
+ ],
+ ]
+ );
+
+ $assets = new Assets();
+ $assets->register_script( 'handle', 'src' );
+ }
+
+ public function testEnqueueScript() {
+ WP_Mock::passthruFunction(
+ 'wp_enqueue_script',
+ [
+ 'times' => 1,
+ 'args' => [
+ 'handle',
+ ],
+ ]
+ );
+
+ $assets = new Assets();
+ $assets->enqueue_script( 'handle' );
+ }
+
+ public function testRegisterStyle() {
+ WP_Mock::passthruFunction( 'get_theme_file_uri', [ 'times' => 1 ] );
+ WP_Mock::passthruFunction(
+ 'wp_register_style',
+ [
+ 'times' => 1,
+ 'args' => [
+ 'handle',
+ 'src',
+ [],
+ false,
+ 'all',
+ ],
+ ]
+ );
+
+ $assets = new Assets();
+ $assets->register_style( 'handle', 'src' );
+ }
+
+ public function testEnqueueStyle() {
+ WP_Mock::passthruFunction(
+ 'wp_enqueue_style',
+ [
+ 'times' => 1,
+ 'args' => [
+ 'handle',
+ ],
+ ]
+ );
+
+ $assets = new Assets();
+ $assets->enqueue_style( 'handle' );
+ }
+}
diff --git a/tests/Tools/Body_Class_Test.php b/tests/Tools/Body_Class_Test.php
new file mode 100644
index 00000000..307f7100
--- /dev/null
+++ b/tests/Tools/Body_Class_Test.php
@@ -0,0 +1,56 @@
+add( 'test' );
+ $this->assertEquals( [ 'test' ], $body_class->body_class( [] ) );
+
+ }
+
+ public function testRemove() {
+ $body_class = new Body_Class();
+
+ $body_class->add( 'test' );
+ $body_class->remove( 'test' );
+ $this->assertEquals( [], $body_class->body_class( [] ) );
+ }
+
+ public function testAddRemoveWithInitialData() {
+ $body_class = new Body_Class();
+
+ $body_class->add( 'test' );
+ $body_class->remove( 'test' );
+ $this->assertEquals( [ 'leaveme' ], $body_class->body_class( [ 'leaveme' ] ) );
+ }
+
+ public function testName() {
+ $body_class = new Body_Class();
+ self::assertEquals( 'body-class', $body_class->get_service_name() );
+ }
+
+ public function testBoot() {
+ $body_class = new Body_Class();
+ $container = $this->createStub( Service_Container::class );
+
+ WP_Mock::expectFilterAdded( 'body_class', [ $body_class, 'body_class' ] );
+
+ $body_class->boot( $container );
+ }
+}
diff --git a/tests/Tools/Template_Parts_Test.php b/tests/Tools/Template_Parts_Test.php
new file mode 100644
index 00000000..fa679a02
--- /dev/null
+++ b/tests/Tools/Template_Parts_Test.php
@@ -0,0 +1,59 @@
+assertTrue( $template->add_var( 'slug', 'key', 'value' ) );
+ }
+
+ public function testGet() {
+ $template = new Template_Parts();
+
+ $this->assertNull( $template->get_var( 'slug', 'key' ) );
+
+ $template->add_var( 'slug', 'key', 'value' );
+
+ $this->assertSame( 'value', $template->get_var( 'slug', 'key' ) );
+ }
+
+ public function testGetOtherslug() {
+ $template = new Template_Parts();
+
+ $template->add_var( 'slug', 'key', 'value' );
+
+ $this->assertEmpty( $template->get_var( 'slug2', 'key' ) );
+ }
+
+ public function testGetVars() {
+ $template = new Template_Parts();
+
+ $template->add_var( 'slug', 'key', 'value' );
+ $template->add_var( 'slug', 'key2', 'value3' );
+ $template->add_var( 'slug3', 'key', 'value3' );
+
+ $this->assertSame( [ 'key' => 'value', 'key2' => 'value3' ], $template->get_vars( 'slug' ) );
+ $this->assertNull( $template->get_vars( 'slug2' ) );
+ $this->assertSame( 'value3', $template->get_var( 'slug3', 'key' ) );
+ }
+
+ public function testName() {
+ $template = new Template_Parts();
+ self::assertEquals( 'template-parts', $template->get_service_name() );
+ }
+
+}
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
new file mode 100644
index 00000000..d1b42513
--- /dev/null
+++ b/tests/bootstrap.php
@@ -0,0 +1,14 @@
+