├── .travis.yml ├── composer.json ├── inc └── functions.php ├── phpcs.xml.dist ├── phpunit.xml.dist ├── src ├── FunctionExpectations.php ├── Generators.php ├── MonkeyWpUser.php ├── Provider │ ├── Comment.php │ ├── Error.php │ ├── FunctionMockerProvider.php │ ├── Post.php │ ├── PostType.php │ ├── Provider.php │ ├── Site.php │ ├── Taxonomy.php │ ├── Term.php │ └── User.php └── Providers.php └── tests ├── cases ├── functional │ └── BrainFakerTest.php └── unit │ ├── GeneratorsTest.php │ ├── Provider │ ├── CommentTest.php │ ├── ErrorTest.php │ ├── PostTest.php │ ├── PostTypeTest.php │ ├── SiteTest.php │ ├── TaxonomyTest.php │ ├── TermTest.php │ └── UserTest.php │ ├── ProvidersInitTest.php │ └── ProvidersTest.php └── src ├── FunctionalTestCase.php ├── ProviderTestCase.php └── TestCase.php /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.2 5 | - 7.3 6 | - 7.4 7 | 8 | stages: 9 | - name: analyze 10 | - name: test 11 | 12 | jobs: 13 | fast_finish: true 14 | include: 15 | - stage: analyze 16 | php: 7.4 17 | script: 18 | - composer phpcs 19 | after_success: skip 20 | 21 | before_script: 22 | - travis_retry composer update --no-interaction --prefer-source --classmap-authoritative 23 | 24 | script: 25 | - composer tests:coverage 26 | 27 | after_success: 28 | - bash <(curl -s https://codecov.io/bash) -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "brain/faker", 3 | "description": "Faker (phpfaker/faker) for WordPress via Brain Monkey", 4 | "minimum-stability": "stable", 5 | "license": "MIT", 6 | "keywords": [ 7 | "wordpress", 8 | "faker", 9 | "test data", 10 | "unit tests data", 11 | "fake data", 12 | "testing", 13 | "test", 14 | "mockery", 15 | "patchwork", 16 | "mock", 17 | "mock functions", 18 | "runkit", 19 | "redefinition", 20 | "monkey patching", 21 | "interception" 22 | ], 23 | "authors": [ 24 | { 25 | "name": "Giuseppe Mazzapica", 26 | "email": "giuseppe.mazzapica@gmail.com", 27 | "homepage": "https://gmazzap.me", 28 | "role": "Developer" 29 | } 30 | ], 31 | "support": { 32 | "issues": "https://github.com/Brain-WP/BrainFaker/issues", 33 | "source": "https://github.com/Brain-WP/BrainFaker" 34 | }, 35 | "require": { 36 | "php": ">=7.2", 37 | "brain/monkey": "^2.3.1", 38 | "fakerphp/faker": "^1.20" 39 | }, 40 | "require-dev": { 41 | "phpunit/phpunit": "^9.5", 42 | "inpsyde/php-coding-standards": "^1.0" 43 | }, 44 | "autoload": { 45 | "files": [ 46 | "inc/functions.php" 47 | ], 48 | "psr-4": { 49 | "Brain\\Faker\\": "src/" 50 | } 51 | }, 52 | "autoload-dev": { 53 | "psr-4": { 54 | "Brain\\Faker\\Tests\\": [ 55 | "tests/src/", 56 | "tests/cases/unit" 57 | ] 58 | } 59 | }, 60 | "scripts": { 61 | "tests": "@php ./vendor/phpunit/phpunit/phpunit --no-coverage", 62 | "tests:coverage": "@php ./vendor/phpunit/phpunit/phpunit --coverage-clover=coverage.xml", 63 | "phpcs": [ 64 | "@php ./vendor/squizlabs/php_codesniffer/bin/phpcs -p . --ignore=*/vendor/* --extensions=php --basepath=./ --runtime-set testVersion 7.2-", 65 | "@php ./vendor/squizlabs/php_codesniffer/bin/phpcs -p . --standard=PHPCompatibility --ignore=*/vendor/* --extensions=php --basepath=./ --runtime-set testVersion 7.2-" 66 | ] 67 | }, 68 | "config": { 69 | "optimize-autoloader": true, 70 | "allow-plugins": { 71 | "dealerdirect/phpcodesniffer-composer-installer": true 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /inc/functions.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ./src 5 | ./tests 6 | ./inc 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | ./src 12 | 13 | ./vendor 14 | 15 | 16 | 17 | 18 | 19 | ./tests/cases/unit 20 | 21 | 22 | ./tests/cases/unit/Provider 23 | 24 | 25 | ./tests/cases/functional 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/FunctionExpectations.php: -------------------------------------------------------------------------------- 1 | functionExpectations[$function] = Functions\expect($function); 31 | 32 | return $this->functionExpectations[$function]; 33 | } 34 | 35 | /** 36 | * @param string $function 37 | * @return Expectation 38 | */ 39 | public function replace(string $function): Expectation 40 | { 41 | if (isset($this->functionExpectations[$function])) { 42 | /** @var Expectation $expectation */ 43 | $expectation = $this->functionExpectations[$function]; 44 | /** @noinspection PhpUndefinedMethodInspection */ 45 | $expectation->byDefault(); 46 | 47 | return $expectation->andAlsoExpectIt(); 48 | } 49 | 50 | return Functions\expect($function); 51 | } 52 | 53 | /** 54 | * @return void 55 | */ 56 | public function reset(): void 57 | { 58 | foreach ($this->functionExpectations as $expectation) { 59 | /** @noinspection PhpUndefinedMethodInspection */ 60 | $expectation->byDefault(); 61 | } 62 | 63 | $this->functionExpectations = []; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Generators.php: -------------------------------------------------------------------------------- 1 | ['post', 'posts'], 25 | Provider\User::class => ['user', 'users'], 26 | Provider\PostType::class => ['postType', 'postTypes'], 27 | Provider\Term::class => ['term', 'terms'], 28 | Provider\Taxonomy::class => ['taxonomy', 'taxonomies'], 29 | Provider\Comment::class => ['comment', 'comments'], 30 | Provider\Site::class => ['site', 'sites'], 31 | Provider\Error::class => ['error', 'errors'], 32 | ]; 33 | 34 | /** 35 | * @var array 36 | */ 37 | private static $generators = []; 38 | 39 | /** 40 | * @param string $locale 41 | * @return Generator 42 | */ 43 | public static function create(string $locale = FakerFactory::DEFAULT_LOCALE): Generator 44 | { 45 | return static::fromGenerator(FakerFactory::create($locale), $locale); 46 | } 47 | 48 | /** 49 | * @param Generator $faker 50 | * @param string $locale 51 | * @return Generator 52 | */ 53 | public static function fromGenerator( 54 | Generator $faker, 55 | string $locale = FakerFactory::DEFAULT_LOCALE 56 | ): Generator { 57 | 58 | if (array_key_exists($locale, static::$generators)) { 59 | return static::$generators[$locale][0]; 60 | } 61 | 62 | $provider = new Providers($faker); 63 | 64 | foreach (self::PROVIDERS as $className => [$methodOne, $methodMany]) { 65 | $provider->__addProviderClass($className, $methodOne, $methodMany); 66 | } 67 | 68 | $faker->addProvider($provider); 69 | 70 | static::$generators[$locale] = [$faker, $provider]; 71 | 72 | return $faker; 73 | } 74 | 75 | /** 76 | * @return void 77 | */ 78 | public static function reset(): void 79 | { 80 | /** 81 | * @var Generator $generator 82 | * @var Providers $providers 83 | */ 84 | foreach (static::$generators as [$generator, $providers]) { 85 | $generator->unique(true); 86 | $providers->__reset(); 87 | } 88 | 89 | static::$generators = []; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/MonkeyWpUser.php: -------------------------------------------------------------------------------- 1 | 'comment_post_ID', 33 | 'comment_author_ip' => 'comment_author_IP', 34 | ]; 35 | 36 | /** @var array $properties */ 37 | $properties = array_change_key_case($properties, CASE_LOWER); 38 | 39 | $hasCommentId = array_key_exists('comment_id', $properties); 40 | $id = $hasCommentId || array_key_exists('id', $properties) 41 | ? ($hasCommentId ? $properties['comment_id'] : $properties['id']) 42 | : $this->uniqueGenerator->numberBetween(1, 99999999); 43 | 44 | $date = $this->generator->dateTime('now', $this->generator->timezone); 45 | $gmt = new \DateTimeZone('GMT'); 46 | 47 | $type = $this->generator->randomElement(['comment', 'trackback', 'pingback']); 48 | 49 | $defaults = [ 50 | 'comment_post_id' => $this->generator->randomNumber(), 51 | 'comment_author' => $this->generator->name, 52 | 'comment_author_email' => $this->generator->email, 53 | 'comment_author_url' => $this->generator->url, 54 | 'comment_author_ip' => $this->generator->ipv4, 55 | 'comment_date' => $date->format('Y-m-d H:i:s'), 56 | 'comment_date_gmt' => $date->setTimezone($gmt)->format('Y-m-d H:i:s'), 57 | 'comment_content' => $this->generator->randomHtml(4, 8), 58 | 'comment_karma' => 0, 59 | 'comment_approved' => $this->generator->randomElement(self::STATUSES), 60 | 'comment_agent' => $this->generator->userAgent, 61 | 'comment_type' => $type, 62 | 'comment_parent' => rand(1, 100) > 75 ? 0 : $this->generator->numberBetween(1, 999999), 63 | 'user_id' => $this->generator->numberBetween(0, 999999), 64 | ]; 65 | 66 | $comment = \Mockery::mock(\WP_Comment::class); 67 | $comment->comment_ID = (int)$id; 68 | 69 | $toArray = ['comment_ID' => (int)$id]; 70 | foreach ($defaults as $key => $value) { 71 | $hasKey = array_key_exists($key, $properties); 72 | if (!$hasKey && strpos($key, 'comment_') === 0) { 73 | $noPrefixKey = substr($key, 8); 74 | $hasKey = array_key_exists($noPrefixKey, $properties); 75 | $hasKey and $properties[$key] = $properties[$noPrefixKey]; 76 | } 77 | 78 | $mappedKey = $keysMap[$key] ?? $key; 79 | $field = $hasKey ? $properties[$key] : $value; 80 | $toArray[$mappedKey] = $field; 81 | $comment->{$mappedKey} = $field; 82 | } 83 | 84 | $comment->shouldReceive('to_array')->andReturn($toArray)->byDefault(); 85 | 86 | return $comment; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Provider/Error.php: -------------------------------------------------------------------------------- 1 | generator->word; 36 | $errors[$code] = [$single]; 37 | unset($properties['message'], $properties['error'], $properties['code']); 38 | } 39 | 40 | $error = \Mockery::mock(\WP_Error::class); 41 | $error->errors = $errors; 42 | $error->error_data = $errorData; 43 | 44 | $error->shouldReceive('has_errors') 45 | ->andReturnUsing( 46 | static function () use (&$error) { // phpcs:ignore 47 | return isset($error->errors) && $error->errors; 48 | } 49 | ) 50 | ->byDefault(); 51 | 52 | $error->shouldReceive('get_error_codes') 53 | ->andReturnUsing( 54 | static function () use (&$error) { // phpcs:ignore 55 | return $error->has_errors() ? array_keys($error->errors) : []; 56 | } 57 | ) 58 | ->byDefault(); 59 | 60 | $error->shouldReceive('get_error_code') 61 | ->andReturnUsing( 62 | static function () use (&$error) { // phpcs:ignore 63 | return $error->get_error_codes()[0] ?? ''; 64 | } 65 | ) 66 | ->byDefault(); 67 | 68 | $error->shouldReceive('get_error_messages') 69 | ->withNoArgs() 70 | ->andReturnUsing( 71 | static function () use (&$error) { // phpcs:ignore 72 | $allMessages = []; 73 | foreach ($error->errors as $codeMessages) { 74 | $allMessages = array_merge($allMessages, $codeMessages); 75 | } 76 | 77 | return $allMessages; 78 | } 79 | ) 80 | ->byDefault(); 81 | 82 | $error->shouldReceive('get_error_messages') 83 | ->with(\Mockery::type('scalar')) 84 | ->andReturnUsing( 85 | static function ($code) use (&$error) { // phpcs:ignore 86 | if (!$code) { 87 | return $error->get_error_messages(); 88 | } 89 | 90 | return $error->errors[$code] ?? []; 91 | } 92 | ) 93 | ->byDefault(); 94 | 95 | $error->shouldReceive('get_error_message') 96 | ->withNoArgs() 97 | ->andReturnUsing( 98 | static function () use (&$error) { // phpcs:ignore 99 | return $error->errors[$error->get_error_code()][0] ?? ''; 100 | } 101 | ) 102 | ->byDefault(); 103 | 104 | $error->shouldReceive('get_error_message') 105 | ->with(\Mockery::type('scalar')) 106 | ->andReturnUsing( 107 | static function ($code) use (&$error) { // phpcs:ignore 108 | return $error->errors[$code ?: $error->get_error_code()][0] ?? ''; 109 | } 110 | ) 111 | ->byDefault(); 112 | 113 | $error->shouldReceive('get_error_data') 114 | ->withNoArgs() 115 | ->andReturnUsing( 116 | static function () use (&$error) { // phpcs:ignore 117 | return $error->error_data[$error->get_error_code()] ?? null; 118 | } 119 | ) 120 | ->byDefault(); 121 | 122 | $error->shouldReceive('get_error_data') 123 | ->with(\Mockery::type('scalar')) 124 | ->andReturnUsing( 125 | static function ($code) use (&$error) { // phpcs:ignore 126 | return $error->error_data[$code ?: $error->get_error_code()] ?? null; 127 | } 128 | ) 129 | ->byDefault(); 130 | 131 | $error->shouldReceive('add') 132 | ->with(\Mockery::type('scalar'), \Mockery::type('string')) 133 | ->andReturnUsing( 134 | static function ($code, $message) use (&$error) { // phpcs:ignore 135 | array_key_exists($code, $error->errors) or $error->errors[$code] = []; 136 | $error->errors[$code][] = $message; 137 | } 138 | ) 139 | ->byDefault(); 140 | 141 | $error->shouldReceive('add') 142 | ->with(\Mockery::type('scalar'), \Mockery::type('string'), \Mockery::any()) 143 | ->andReturnUsing( 144 | static function ($code, $message, $data) use (&$error) { // phpcs:ignore 145 | array_key_exists($code, $error->errors) or $error->errors[$code] = []; 146 | $error->errors[$code][] = $message; 147 | if (!$data) { 148 | return; 149 | } 150 | array_key_exists($code, $error->error_data) or $error->error_data[$code] = []; 151 | $error->error_data[$code] = $data; 152 | } 153 | ) 154 | ->byDefault(); 155 | 156 | $error->shouldReceive('add_data') 157 | ->with(\Mockery::any()) 158 | ->andReturnUsing( 159 | static function ($data) use (&$error) { // phpcs:ignore 160 | $code = $error->get_error_code(); 161 | array_key_exists($code, $error->error_data) or $error->error_data[$code] = []; 162 | $error->error_data[$code] = $data; 163 | } 164 | ) 165 | ->byDefault(); 166 | 167 | $error->shouldReceive('add_data') 168 | ->with(\Mockery::any(), \Mockery::type('scalar')) 169 | ->andReturnUsing( 170 | static function ($data, $code) use (&$error) { // phpcs:ignore 171 | array_key_exists($code, $error->error_data) or $error->error_data[$code] = []; 172 | $error->error_data[$code] = $data; 173 | } 174 | ) 175 | ->byDefault(); 176 | 177 | $error->shouldReceive('remove') 178 | ->with(\Mockery::type('scalar')) 179 | ->andReturnUsing( 180 | static function ($code) use (&$error) { // phpcs:ignore 181 | unset($error->errors[$code]); 182 | unset($error->error_data[$code]); 183 | } 184 | ) 185 | ->byDefault(); 186 | 187 | return $error; 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/Provider/FunctionMockerProvider.php: -------------------------------------------------------------------------------- 1 | functionExpectations = $expectations; 37 | } 38 | 39 | /** 40 | * @return void 41 | */ 42 | public function reset(): void 43 | { 44 | $this->functionsMocked = false; 45 | parent::reset(); 46 | } 47 | 48 | /** 49 | * @return bool 50 | */ 51 | public function canMockFunctions(): bool 52 | { 53 | return !$this->functionsMocked; 54 | } 55 | 56 | /** 57 | * @return void 58 | */ 59 | public function stopMockingFunctions(): void 60 | { 61 | $this->functionsMocked = true; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Provider/Post.php: -------------------------------------------------------------------------------- 1 | ID === (int)$post->ID; 66 | }; 67 | } 68 | 69 | /** 70 | * @return void 71 | */ 72 | public function reset(): void 73 | { 74 | $this->posts = []; 75 | parent::reset(); 76 | } 77 | 78 | /** 79 | * @param array $properties 80 | * @return \WP_Post 81 | * 82 | * phpcs:disable Inpsyde.CodeQuality.FunctionLength.TooLong 83 | */ 84 | public function __invoke(array $properties = []): \WP_Post 85 | { 86 | // phpcs:enable Inpsyde.CodeQuality.FunctionLength.TooLong 87 | 88 | $zone = $this->generator->timezone; 89 | 90 | $properties = array_change_key_case($properties, CASE_LOWER); 91 | $properties = $this->setupDateProperties($properties, $zone); 92 | 93 | $id = array_key_exists('id', $properties) 94 | ? $properties['id'] 95 | : $this->uniqueGenerator->numberBetween(1, 99999); 96 | 97 | $publish = ($properties['post_status'] ?? null) === 'publish'; 98 | 99 | $gmt = new \DateTimeZone('GMT'); 100 | 101 | $date = $this->generator->dateTimeThisCentury($publish ? '-1 hour' : '+10 years', $zone); 102 | $dateMod = $this->generator->dateTimeThisCentury('now', $zone); 103 | 104 | $title = $this->generator->sentence($this->generator->numberBetween(1, 5)); 105 | 106 | $content = $properties['post_content'] 107 | ?? $properties['content'] 108 | ?? $this->generateRandomHtml(); 109 | 110 | $contentFiltered = $properties['post_content_filtered'] 111 | ?? $properties['content_filtered'] 112 | ?? trim(strip_tags($content)); 113 | 114 | $defaults = [ 115 | 'post_author' => (string)$this->generator->numberBetween(1, 99999), 116 | 'post_date' => $date->format('Y-m-d H:i:s'), 117 | 'post_date_gmt' => $date->setTimezone($gmt)->format('Y-m-d H:i:s'), 118 | 'post_title' => $title, 119 | 'post_excerpt' => $this->generator->sentences(3, true), 120 | 'post_status' => '', 121 | 'comment_status' => $this->generator->randomElement(['open', 'closed']), 122 | 'ping_status' => $this->generator->randomElement(['open', 'closed']), 123 | 'post_password' => rand(1, 100) > 80 ? $this->generator->password(4, 8) : '', 124 | 'post_name' => preg_replace('/[^a-z0-9-_]/i', '-', $title), 125 | 'to_ping' => '', 126 | 'pinged' => '', 127 | 'post_modified' => $dateMod->format('Y-m-d H:i:s'), 128 | 'post_modified_gmt' => $dateMod->setTimezone($gmt)->format('Y-m-d H:i:s'), 129 | 'post_parent' => rand(1, 100) > 75 ? $this->generator->numberBetween(1, 99999) : 0, 130 | 'guid' => sprintf('http://%s?p=%d', $this->generator->domainName, $id), 131 | 'menu_order' => $this->generator->numberBetween(0, 100), 132 | 'post_type' => $this->generator->randomElement(['post', 'page']), 133 | 'post_mime_type' => '', 134 | 'comment_count' => '0', 135 | 'filter' => $this->generator->randomElement(['raw', null]), 136 | 'ancestors' => [], 137 | 'page_template' => '', 138 | 'post_category' => [], 139 | 'tags_input' => [], 140 | ]; 141 | 142 | $post = \Mockery::mock(\WP_Post::class); 143 | $post->ID = (int)$id; 144 | 145 | $toArray = ['ID' => (int)$id]; 146 | foreach ($defaults as $key => $value) { 147 | $hasKey = array_key_exists($key, $properties); 148 | if (!$hasKey && strpos($key, 'post_') === 0) { 149 | $noPrefixKey = substr($key, 5); 150 | $hasKey = array_key_exists($noPrefixKey, $properties); 151 | $hasKey and $properties[$key] = $properties[$noPrefixKey]; 152 | } 153 | 154 | $field = $hasKey ? $properties[$key] : $value; 155 | $post->{$key} = $field; 156 | $toArray[$key] = $field; 157 | } 158 | 159 | $post->post_content = $content; 160 | $toArray['post_content'] = $content; 161 | $post->post_content_filtered = $contentFiltered; 162 | $toArray['post_content_filtered'] = $contentFiltered; 163 | 164 | if ($post->post_type === 'attachment') { 165 | $mime = $properties['post_mime_type'] ?? $properties['mime_type'] ?? null; 166 | $mime === null and $mime = $this->generator->randomElement(self::MIME_TYPES); 167 | $post->post_mime_type = $mime; 168 | $toArray['post_mime_type'] = $mime; 169 | } 170 | 171 | if (!$post->post_status) { 172 | $dateGmt = \DateTime::createFromFormat('Y-m-d H:i:s', $post->post_date_gmt, $gmt); 173 | $status = $dateGmt > new \DateTimeImmutable('now', $gmt) 174 | ? 'future' 175 | : $this->generator->randomElement(['publish', 'draft']); 176 | $post->post_status = $status; 177 | $toArray['post_status'] = $status; 178 | } 179 | 180 | $post->shouldReceive('to_array')->andReturn($toArray)->byDefault(); 181 | 182 | $this->posts[$post->ID] = $toArray; 183 | $this->mockFunctions(); 184 | 185 | return $post; 186 | } 187 | 188 | private function mockFunctions(): void 189 | { 190 | if (!$this->canMockFunctions()) { 191 | return; 192 | } 193 | 194 | $this->functionExpectations->mock('get_post') 195 | ->zeroOrMoreTimes() 196 | ->with(\Mockery::any()) 197 | ->andReturnUsing( 198 | function ($post) { // phpcs:ignore 199 | $postId = is_object($post) ? $post->ID : $post; 200 | if (!$postId || !is_numeric($postId)) { 201 | return false; 202 | } 203 | 204 | $data = $this->posts[(int)$postId] ?? null; 205 | 206 | return $data ? $this->__invoke($data) : false; 207 | } 208 | ); 209 | 210 | $this->functionExpectations->mock('get_post_field') 211 | ->zeroOrMoreTimes() 212 | ->andReturnUsing( 213 | function ($field = null, $post = null) { // phpcs:ignore 214 | $postId = is_object($post) ? $post->ID : $post; 215 | if (!$postId || !is_numeric($postId) || !is_string($field)) { 216 | return ''; 217 | } 218 | 219 | return $this->posts[(int)$postId][$field] ?? ''; 220 | } 221 | ); 222 | 223 | $this->stopMockingFunctions(); 224 | } 225 | 226 | /** 227 | * @param array $properties 228 | * @param string $randomZone 229 | * @return array 230 | * @throws \Exception 231 | */ 232 | private function setupDateProperties(array $properties, string $randomZone): array 233 | { 234 | $dateKeys = [ 235 | 'date' => [false, 'date_gmt'], 236 | 'date_gmt' => [true, 'date'], 237 | 'modified' => [false, 'modified_gmt'], 238 | 'modified_gmt' => [true, 'modified'], 239 | ]; 240 | 241 | $original = $properties; 242 | foreach ($dateKeys as $key => [$isGmt, $altKey]) { 243 | $hasDateKey = array_key_exists("post_{$key}", $original); 244 | if (!$hasDateKey && !array_key_exists($key, $original)) { 245 | continue; 246 | } 247 | 248 | $dateRaw = $hasDateKey ? $original["post_{$key}"] : $original[$key]; 249 | $date = $this->formatDate($dateRaw, $isGmt ? 'GMT' : $randomZone); 250 | 251 | $properties["post_{$key}"] = $date; 252 | 253 | if (!$hasDateKey) { 254 | unset($properties[$key]); 255 | } 256 | 257 | $hasAltDateKey = array_key_exists("post_{$altKey}", $original); 258 | if ($hasAltDateKey || array_key_exists($altKey, $original)) { 259 | continue; 260 | } 261 | 262 | $targetZone = $isGmt ? new \DateTimeZone($randomZone) : new \DateTimeZone('GMT'); 263 | 264 | $altDate = \DateTime::createFromFormat(self::DATE_FORMAT, $date) 265 | ->setTimezone($targetZone) 266 | ->format(self::DATE_FORMAT); 267 | 268 | $properties["post_{$altKey}"] = $altDate; 269 | 270 | if (!$hasAltDateKey) { 271 | unset($properties[$key]); 272 | } 273 | } 274 | 275 | return $properties; 276 | } 277 | 278 | /** 279 | * @param $date 280 | * @param string|null $zone 281 | * @return string 282 | * 283 | * phpcs:disable Inpsyde.CodeQuality.ArgumentTypeDeclaration 284 | */ 285 | private function formatDate($date, ?string $zone = null): string 286 | { 287 | // phpcs:enable Inpsyde.CodeQuality.ArgumentTypeDeclaration 288 | 289 | if ($date instanceof \DateTimeInterface) { 290 | return $date->format(self::DATE_FORMAT); 291 | } 292 | 293 | if (is_int($date)) { 294 | return (new \DateTime('now', new \DateTimeZone($zone ?? 'UTC'))) 295 | ->setTimestamp($date) 296 | ->format(self::DATE_FORMAT); 297 | } 298 | 299 | if (!is_string($date) || strtolower($date) === 'now') { 300 | return (new \DateTime('now', new \DateTimeZone($zone ?? 'UTC'))) 301 | ->format(self::DATE_FORMAT); 302 | } 303 | 304 | if (preg_match('/^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}$/', $date)) { 305 | return $date; 306 | } 307 | 308 | return $this->formatDate((int)(strtotime($date) ?: (int)time()), $zone); 309 | } 310 | 311 | /** 312 | * Faker randomHtml is slow for long text and it generates full-page HTML, not fragment like 313 | * post content is. 314 | * 315 | * @return string 316 | */ 317 | private function generateRandomHtml(): string 318 | { 319 | $output = ''; 320 | $max = $this->generator->numberBetween(0, 50); 321 | if (!$max) { 322 | return ''; 323 | } 324 | 325 | $listItems = $this->generator->numberBetween(0, 10); 326 | $list = ''; 327 | if ($listItems) { 328 | $listTag = $this->generator->randomElement(['ul', 'ol']); 329 | $list .= "<{$listTag}>"; 330 | for ($listI = 0; $listI < $listItems; $listI++) { 331 | $list .= "\n
  • "; 332 | $list .= $this->generator->sentence($this->generator->numberBetween(1, 3)); 333 | $list .= "
  • "; 334 | } 335 | $list .= "\n"; 336 | } 337 | 338 | for ($i = 0; $i < $max; $i++) { 339 | $tag = $this->generator->randomElement(['p', 'div']); 340 | $output .= "\n<{$tag}>\n"; 341 | $image = $this->generator->randomElement([true, false]); 342 | if ($image) { 343 | $output .= sprintf( 344 | "\"%s\"\n", 345 | $this->generator->sentence(1, 3), 346 | $this->generator->imageUrl() 347 | ); 348 | } 349 | $output .= $this->generator->sentences($this->generator->numberBetween(1, 5), true); 350 | $output .= "\n"; 351 | $output .= $tag === 'div' ? "{$list}\n" : "\n{$list}"; 352 | } 353 | 354 | return trim($output); 355 | } 356 | } 357 | -------------------------------------------------------------------------------- /src/Provider/Provider.php: -------------------------------------------------------------------------------- 1 | generator = $generator; 35 | $this->uniqueGenerator = $generator->unique(true); 36 | } 37 | 38 | /** 39 | * @return void 40 | */ 41 | final public function resetUnique(): void 42 | { 43 | $this->uniqueGenerator = $this->generator->unique(true); 44 | } 45 | 46 | /** 47 | * @return void 48 | */ 49 | public function reset(): void 50 | { 51 | $this->resetUnique(); 52 | } 53 | 54 | /** 55 | * @param array $args 56 | * @return object 57 | */ 58 | abstract public function __invoke(array $args = []); 59 | } 60 | -------------------------------------------------------------------------------- /src/Provider/Site.php: -------------------------------------------------------------------------------- 1 | term_id === (int)$site->term_id; 29 | }; 30 | } 31 | 32 | /** 33 | * @return void 34 | */ 35 | public function reset(): void 36 | { 37 | $this->sites = []; 38 | parent::reset(); 39 | } 40 | 41 | /** 42 | * @param array $properties 43 | * @return \WP_Site 44 | * 45 | * phpcs:disable Inpsyde.CodeQuality.FunctionLength.TooLong 46 | */ 47 | public function __invoke(array $properties = []): \WP_Site 48 | { 49 | // phpcs:enable Inpsyde.CodeQuality.FunctionLength.TooLong 50 | 51 | if (function_exists('is_multisite') && !is_multisite()) { 52 | throw new \Error('WP_Site class is only available in multisite installations.'); 53 | } 54 | 55 | $hasBlogId = array_key_exists('blog_id', $properties); 56 | $id = $hasBlogId || array_key_exists('id', $properties) 57 | ? ($hasBlogId ? $properties['blog_id'] : $properties['id']) 58 | : $this->uniqueGenerator->numberBetween(1, 99999999); 59 | 60 | $siteId = $properties['site_id'] 61 | ?? $properties['network_id'] 62 | ?? (rand(1, 100) > 90 ? $this->generator->numberBetween(2, 5) : 1); 63 | 64 | $url = $properties['site_url'] 65 | ?? $properties['home'] 66 | ?? $properties['url'] 67 | ?? null; 68 | 69 | if ($url) { 70 | parse_url($url, PHP_URL_SCHEME) or $url = "http://{$url}"; 71 | if (!array_key_exists('path', $properties)) { 72 | $path = parse_url($url, PHP_URL_PATH); 73 | $path and $properties['path'] = '/' . trim($path, '/'); 74 | } 75 | } 76 | 77 | $defaults = [ 78 | 'domain' => $this->generator->domainName, 79 | 'path' => rand(1, 100) > 50 ? $this->generator->domainWord : '', 80 | 'registered' => $this->generator->dateTimeThisDecade->format('Y-m-d H:i:s'), 81 | 'last_updated' => $this->generator->dateTimeThisMonth->format('Y-m-d H:i:s'), 82 | 'public' => rand(1, 100) > 80 ? '0' : '1', 83 | 'archived' => rand(1, 100) > 80 ? '1' : '0', 84 | 'mature' => rand(1, 100) > 80 ? '1' : '0', 85 | 'spam' => rand(1, 100) > 80 ? '1' : '0', 86 | 'deleted' => rand(1, 100) > 80 ? '1' : '0', 87 | 'lang_id' => rand(1, 100) > 80 ? '1' : '0', 88 | ]; 89 | 90 | $site = \Mockery::mock(\WP_Site::class); 91 | $site->blog_id = (string)((int)$id); 92 | $site->site_id = (string)((int)$siteId); 93 | $site->id = $site->blog_id; 94 | $site->network_id = $site->site_id; 95 | 96 | $toArray = ['blog_id' => $site->blog_id, 'site_id' => $site->site_id]; 97 | foreach ($defaults as $key => $value) { 98 | $field = array_key_exists($key, $properties) ? $properties[$key] : $value; 99 | is_numeric($field) and $field = (string)((int)$field); 100 | $toArray[$key] = $field; 101 | $site->{$key} = $field; 102 | } 103 | 104 | $details = [ 105 | 'blogname' => $this->generator->sentence, 106 | 'siteurl' => $url ?? "http://{$this->generator->domainName}/{$site->path}", 107 | 'home' => $url ?? "http://{$this->generator->domainName}/{$site->path}", 108 | 'post_count' => $this->generator->numberBetween(0, 9999), 109 | ]; 110 | 111 | $data = $toArray; 112 | 113 | foreach ($details as $key => $value) { 114 | $field = array_key_exists($key, $properties) ? $properties[$key] : $value; 115 | $site->{$key} = $field; 116 | $data[$key] = $field; 117 | } 118 | 119 | $site->shouldReceive('to_array')->andReturn($toArray)->byDefault(); 120 | $this->sites[$site->blog_id] = $data; 121 | $this->mockFunctions(); 122 | 123 | return $site; 124 | } 125 | 126 | /** 127 | * @return void 128 | */ 129 | private function mockFunctions(): void 130 | { 131 | if (!$this->canMockFunctions()) { 132 | return; 133 | } 134 | 135 | $this->functionExpectations->mock('get_site') 136 | ->zeroOrMoreTimes() 137 | ->andReturnUsing( 138 | function ($site = null) { // phpcs:ignore 139 | $siteId = is_object($site) ? ($site->blog_id ?? null) : $site; 140 | 141 | return $siteId && is_numeric($siteId) && isset($this->sites[(int)$siteId]) 142 | ? $this->__invoke($this->sites[(int)$siteId]) 143 | : null; 144 | } 145 | ); 146 | 147 | $this->stopMockingFunctions(); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/Provider/Taxonomy.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'name' => 'category', 19 | 'label' => 'Categories', 20 | 'labels' => [ 21 | 'name' => 'Categories', 22 | 'singular_name' => 'Category', 23 | 'search_items' => 'Search Categories', 24 | 'popular_items' => null, 25 | 'all_items' => 'All Categories', 26 | 'parent_item' => 'Parent Category', 27 | 'parent_item_colon' => 'Parent Category:', 28 | 'edit_item' => 'Edit Category', 29 | 'view_item' => 'View Category', 30 | 'update_item' => 'Update Category', 31 | 'add_new_item' => 'Add New Category', 32 | 'new_item_name' => 'New Category Name', 33 | 'separate_items_with_commas' => null, 34 | 'add_or_remove_items' => null, 35 | 'choose_from_most_used' => null, 36 | 'not_found' => 'No categories found.', 37 | 'no_terms' => 'No categories', 38 | 'items_list_navigation' => 'Categories list navigation', 39 | 'items_list' => 'Categories list', 40 | 'most_used' => 'Most Used', 41 | 'back_to_items' => '← Back to Categories', 42 | 'menu_name' => 'Categories', 43 | 'name_admin_bar' => 'category', 44 | ], 45 | 'description' => '', 46 | 'public' => true, 47 | 'publicly_queryable' => true, 48 | 'hierarchical' => true, 49 | 'show_ui' => true, 50 | 'show_in_menu' => true, 51 | 'show_in_nav_menus' => true, 52 | 'show_tagcloud' => true, 53 | 'show_in_quick_edit' => true, 54 | 'show_admin_column' => true, 55 | 'meta_box_cb' => 'post_categories_meta_box', 56 | 'meta_box_sanitize_cb' => 'taxonomy_meta_box_sanitize_cb_checkboxes', 57 | 'object_type' => ['post'], 58 | 'cap' => [ 59 | 'manage_terms' => 'manage_categories', 60 | 'edit_terms' => 'edit_categories', 61 | 'delete_terms' => 'delete_categories', 62 | 'assign_terms' => 'assign_categories', 63 | ], 64 | 'rewrite' => [ 65 | 'with_front' => true, 66 | 'hierarchical' => true, 67 | 'ep_mask' => 512, 68 | 'slug' => 'category', 69 | ], 70 | 'query_var' => 'category_name', 71 | 'update_count_callback' => '', 72 | 'show_in_rest' => true, 73 | 'rest_base' => 'categories', 74 | 'rest_controller_class' => 'WP_REST_Terms_Controller', 75 | '_builtin' => true, 76 | ], 77 | 'post_tag' => [ 78 | 'name' => 'post_tag', 79 | 'label' => 'Tags', 80 | 'labels' => [ 81 | 'name' => 'Tags', 82 | 'singular_name' => 'Tag', 83 | 'search_items' => 'Search Tags', 84 | 'popular_items' => 'Popular Tags', 85 | 'all_items' => 'All Tags', 86 | 'parent_item' => null, 87 | 'parent_item_colon' => null, 88 | 'edit_item' => 'Edit Tag', 89 | 'view_item' => 'View Tag', 90 | 'update_item' => 'Update Tag', 91 | 'add_new_item' => 'Add New Tag', 92 | 'new_item_name' => 'New Tag Name', 93 | 'separate_items_with_commas' => 'Separate tags with commas', 94 | 'add_or_remove_items' => 'Add or remove tags', 95 | 'choose_from_most_used' => 'Choose from the most used tags', 96 | 'not_found' => 'No tags found.', 97 | 'no_terms' => 'No tags', 98 | 'items_list_navigation' => 'Tags list navigation', 99 | 'items_list' => 'Tags list', 100 | 'most_used' => 'Most Used', 101 | 'back_to_items' => '← Back to Tags', 102 | 'menu_name' => 'Tags', 103 | 'name_admin_bar' => 'post_tag', 104 | ], 105 | 'description' => '', 106 | 'public' => true, 107 | 'publicly_queryable' => true, 108 | 'hierarchical' => false, 109 | 'show_ui' => true, 110 | 'show_in_menu' => true, 111 | 'show_in_nav_menus' => true, 112 | 'show_tagcloud' => true, 113 | 'show_in_quick_edit' => true, 114 | 'show_admin_column' => true, 115 | 'meta_box_cb' => 'post_tags_meta_box', 116 | 'meta_box_sanitize_cb' => 'taxonomy_meta_box_sanitize_cb_input', 117 | 'object_type' => ['post'], 118 | 'cap' => [ 119 | 'manage_terms' => 'manage_post_tags', 120 | 'edit_terms' => 'edit_post_tags', 121 | 'delete_terms' => 'delete_post_tags', 122 | 'assign_terms' => 'assign_post_tags', 123 | ], 124 | 'rewrite' => [ 125 | 'with_front' => true, 126 | 'hierarchical' => false, 127 | 'ep_mask' => 1024, 128 | 'slug' => 'tag', 129 | ], 130 | 'query_var' => 'tag', 131 | 'update_count_callback' => '', 132 | 'show_in_rest' => true, 133 | 'rest_base' => 'tags', 134 | 'rest_controller_class' => 'WP_REST_Terms_Controller', 135 | '_builtin' => true, 136 | ], 137 | 'nav_menu' => [ 138 | 'name' => 'nav_menu', 139 | 'label' => 'Navigation Menus', 140 | 'labels' => [ 141 | 'name' => 'Navigation Menus', 142 | 'singular_name' => 'Navigation Menu', 143 | 'search_items' => 'Search Tags', 144 | 'popular_items' => 'Popular Tags', 145 | 'all_items' => 'Navigation Menus', 146 | 'parent_item' => null, 147 | 'parent_item_colon' => null, 148 | 'edit_item' => 'Edit Tag', 149 | 'view_item' => 'View Tag', 150 | 'update_item' => 'Update Tag', 151 | 'add_new_item' => 'Add New Tag', 152 | 'new_item_name' => 'New Tag Name', 153 | 'separate_items_with_commas' => 'Separate tags with commas', 154 | 'add_or_remove_items' => 'Add or remove tags', 155 | 'choose_from_most_used' => 'Choose from the most used tags', 156 | 'not_found' => 'No tags found.', 157 | 'no_terms' => 'No tags', 158 | 'items_list_navigation' => 'Tags list navigation', 159 | 'items_list' => 'Tags list', 160 | 'most_used' => 'Most Used', 161 | 'back_to_items' => '← Back to Tags', 162 | 'menu_name' => 'Navigation Menus', 163 | 'name_admin_bar' => 'Navigation Menu', 164 | 'archives' => 'Navigation Menus', 165 | ], 166 | 'description' => '', 167 | 'public' => false, 168 | 'publicly_queryable' => false, 169 | 'hierarchical' => false, 170 | 'show_ui' => false, 171 | 'show_in_menu' => false, 172 | 'show_in_nav_menus' => false, 173 | 'show_tagcloud' => false, 174 | 'show_in_quick_edit' => false, 175 | 'show_admin_column' => false, 176 | 'meta_box_cb' => 'post_tags_meta_box', 177 | 'meta_box_sanitize_cb' => 'taxonomy_meta_box_sanitize_cb_input', 178 | 'object_type' => ['nav_menu_item'], 179 | 'cap' => [ 180 | 'manage_terms' => 'manage_categories', 181 | 'edit_terms' => 'manage_categories', 182 | 'delete_terms' => 'manage_categories', 183 | 'assign_terms' => 'edit_posts', 184 | ], 185 | 'rewrite' => false, 186 | 'query_var' => false, 187 | 'update_count_callback' => '', 188 | 'show_in_rest' => false, 189 | 'rest_base' => false, 190 | 'rest_controller_class' => false, 191 | '_builtin' => true, 192 | ], 193 | 'link_category' => [ 194 | 'name' => 'link_category', 195 | 'label' => 'Link Categories', 196 | 'labels' => [ 197 | 'name' => 'Link Categories', 198 | 'singular_name' => 'Link Category', 199 | 'search_items' => 'Search Link Categories', 200 | 'popular_items' => null, 201 | 'all_items' => 'All Link Categories', 202 | 'parent_item' => null, 203 | 'parent_item_colon' => null, 204 | 'edit_item' => 'Edit Link Category', 205 | 'view_item' => 'View Tag', 206 | 'update_item' => 'Update Link Category', 207 | 'add_new_item' => 'Add New Link Category', 208 | 'new_item_name' => 'New Link Category Name', 209 | 'separate_items_with_commas' => null, 210 | 'add_or_remove_items' => null, 211 | 'choose_from_most_used' => null, 212 | 'not_found' => 'No tags found.', 213 | 'no_terms' => 'No tags', 214 | 'items_list_navigation' => 'Tags list navigation', 215 | 'items_list' => 'Tags list', 216 | 'most_used' => 'Most Used', 217 | 'back_to_items' => '← Back to Link Categories', 218 | 'menu_name' => 'Link Categories', 219 | 'name_admin_bar' => 'Link Category', 220 | 'archives' => 'All Link Categories', 221 | ], 222 | 'description' => '', 223 | 'public' => false, 224 | 'publicly_queryable' => false, 225 | 'hierarchical' => false, 226 | 'show_ui' => true, 227 | 'show_in_menu' => true, 228 | 'show_in_nav_menus' => false, 229 | 'show_tagcloud' => true, 230 | 'show_in_quick_edit' => true, 231 | 'show_admin_column' => false, 232 | 'meta_box_cb' => 'post_tags_meta_box', 233 | 'meta_box_sanitize_cb' => 'taxonomy_meta_box_sanitize_cb_input', 234 | 'object_type' => ['link'], 235 | 'cap' => [ 236 | 'manage_terms' => 'manage_links', 237 | 'edit_terms' => 'manage_links', 238 | 'delete_terms' => 'manage_links', 239 | 'assign_terms' => 'manage_links', 240 | ], 241 | 'rewrite' => false, 242 | 'query_var' => false, 243 | 'update_count_callback' => '', 244 | 'show_in_rest' => false, 245 | 'rest_base' => false, 246 | 'rest_controller_class' => false, 247 | '_builtin' => true, 248 | ], 249 | 'post_format' => [ 250 | 'name' => 'post_format', 251 | 'label' => 'Formats', 252 | 'labels' => [ 253 | 'name' => 'Formats', 254 | 'singular_name' => 'Format', 255 | 'search_items' => 'Search Tags', 256 | 'popular_items' => 'Popular Tags', 257 | 'all_items' => 'Formats', 258 | 'parent_item' => null, 259 | 'parent_item_colon' => null, 260 | 'edit_item' => 'Edit Tag', 261 | 'view_item' => 'View Tag', 262 | 'update_item' => 'Update Tag', 263 | 'add_new_item' => 'Add New Tag', 264 | 'new_item_name' => 'New Tag Name', 265 | 'separate_items_with_commas' => 'Separate tags with commas', 266 | 'add_or_remove_items' => 'Add or remove tags', 267 | 'choose_from_most_used' => 'Choose from the most used tags', 268 | 'not_found' => 'No tags found.', 269 | 'no_terms' => 'No tags', 270 | 'items_list_navigation' => 'Tags list navigation', 271 | 'items_list' => 'Tags list', 272 | 'most_used' => 'Most Used', 273 | 'back_to_items' => '← Back to Tags', 274 | 'menu_name' => 'Formats', 275 | 'name_admin_bar' => 'Format', 276 | 'archives' => 'Formats', 277 | ], 278 | 'description' => '', 279 | 'public' => true, 280 | 'publicly_queryable' => true, 281 | 'hierarchical' => false, 282 | 'show_ui' => false, 283 | 'show_in_menu' => false, 284 | 'show_in_nav_menus' => false, 285 | 'show_tagcloud' => false, 286 | 'show_in_quick_edit' => false, 287 | 'show_admin_column' => false, 288 | 'meta_box_cb' => 'post_tags_meta_box', 289 | 'meta_box_sanitize_cb' => 'taxonomy_meta_box_sanitize_cb_input', 290 | 'object_type' => ['post'], 291 | 'cap' => [ 292 | 'manage_terms' => 'manage_categories', 293 | 'edit_terms' => 'manage_categories', 294 | 'delete_terms' => 'manage_categories', 295 | 'assign_terms' => 'edit_posts', 296 | ], 297 | 'rewrite' => [ 298 | 'with_front' => true, 299 | 'hierarchical' => false, 300 | 'ep_mask' => 0, 301 | 'slug' => 'type', 302 | ], 303 | 'query_var' => 'post_format', 304 | 'update_count_callback' => '', 305 | 'show_in_rest' => false, 306 | 'rest_base' => false, 307 | 'rest_controller_class' => false, 308 | '_builtin' => true, 309 | ], 310 | ]; 311 | 312 | /** 313 | * @var array[] 314 | */ 315 | private $taxonomies = []; 316 | 317 | /** 318 | * @return void 319 | */ 320 | public function reset(): void 321 | { 322 | $this->taxonomies = []; 323 | parent::reset(); 324 | } 325 | 326 | /** 327 | * @param array $properties 328 | * @return \WP_Taxonomy|\Mockery\MockInterface 329 | */ 330 | public function __invoke(array $properties = []): \WP_Taxonomy 331 | { 332 | $properties = array_change_key_case($properties, CASE_LOWER); 333 | $customName = array_key_exists('name', $properties) ? $properties['name'] : null; 334 | $name = $customName; 335 | 336 | if ($name === null) { 337 | $buildInKeys = array_keys(self::BUILT_IN); 338 | $notDoneBuildIn = array_diff($buildInKeys, array_keys($this->taxonomies)); 339 | $name = $this->generator->randomElement($notDoneBuildIn ?: $buildInKeys); 340 | } 341 | 342 | $public = array_key_exists('public', $properties) 343 | ? (bool)filter_var($properties['public'], FILTER_VALIDATE_BOOLEAN) 344 | : null; 345 | 346 | /** @var string $name */ 347 | 348 | $loadedProperties = $this->maybeLoadProperties($name, $properties); 349 | if (is_array($loadedProperties)) { 350 | return $this->createTaxonomy($loadedProperties, [], $name); 351 | } 352 | 353 | $builtIn = array_key_exists($name, self::BUILT_IN); 354 | $baseName = $builtIn ? $name : array_rand(self::BUILT_IN); 355 | 356 | if (!$builtIn && ($customName || (is_bool($public) && $public))) { 357 | $hierarchical = !empty($properties['hierarchical']); 358 | $baseName = $hierarchical ? 'category' : 'post_tag'; 359 | } 360 | 361 | $defaults = $builtIn ? self::BUILT_IN[$name] : self::BUILT_IN[$baseName]; 362 | $properties['_builtin'] = $builtIn; 363 | 364 | $taxonomy = $this->createTaxonomy($defaults, $properties, $name, $public); 365 | $this->mockFunctions(); 366 | 367 | return $taxonomy; 368 | } 369 | 370 | /** 371 | * @param string $name 372 | * @param array $properties 373 | * @return array|null 374 | */ 375 | private function maybeLoadProperties(string $name, array $properties): ?array 376 | { 377 | if (!isset($this->taxonomies[$name])) { 378 | return null; 379 | } 380 | 381 | $diffKeys = ['labels' => '', 'cap' => '', 'rewrite' => '', 'name' => '']; 382 | 383 | $savedProperties = $this->taxonomies[$name]; 384 | $savedScalars = array_diff_key($savedProperties, $diffKeys); 385 | $savedLabels = $savedProperties['labels'] ?? []; 386 | $savedCaps = $savedProperties['cap'] ?? []; 387 | $savedRewrite = $savedProperties['rewrite'] ?? false; 388 | $savedRewrite and $savedRewrite = (array)$savedRewrite; 389 | 390 | $newScalars = $properties ? array_diff_key($properties, $diffKeys) : []; 391 | $newLabels = (array)($properties['labels'] ?? []); 392 | $newCaps = (array)($properties['cap'] ?? []); 393 | $newRewrite = $properties['rewrite'] ?? false; 394 | $newRewrite and $newRewrite = (array)$newRewrite; 395 | 396 | $savedScalars and ksort($savedScalars); 397 | $savedLabels and ksort($savedLabels); 398 | $savedCaps and ksort($savedCaps); 399 | $savedRewrite and ksort($savedRewrite); 400 | 401 | $newScalars and ksort($newScalars); 402 | $newLabels and ksort($newLabels); 403 | $newCaps and ksort($newCaps); 404 | $newRewrite and ksort($newRewrite); 405 | 406 | if (($newScalars && $newScalars !== $savedScalars) 407 | || ($newLabels && $newLabels !== $savedLabels) 408 | || ($newCaps && $newCaps !== $savedCaps) 409 | || ($newRewrite && $newRewrite !== $savedRewrite) 410 | ) { 411 | throw new \Error("Taxonomy {$name} was already faked with different properties."); 412 | } 413 | 414 | return $savedProperties; 415 | } 416 | 417 | /** 418 | * @param array $defaults 419 | * @param array $properties 420 | * @param string $name 421 | * @param bool|null $public 422 | * @return \WP_Taxonomy 423 | */ 424 | private function createTaxonomy( 425 | array $defaults, 426 | array $properties, 427 | string $name, 428 | ?bool $public = null 429 | ): \WP_Taxonomy { 430 | 431 | $showUi = $properties['show_ui'] ?? $public; 432 | 433 | $publicKeys = [ 434 | 'publicly_queryable', 435 | 'show_in_rest', 436 | ]; 437 | 438 | $uiKeys = [ 439 | 'show_ui', 440 | 'show_in_menu', 441 | 'show_in_nav_menus', 442 | 'show_tagcloud', 443 | 'show_in_quick_edit', 444 | 'show_in_rest', 445 | ]; 446 | 447 | $reloading = isset($this->taxonomies[$name]); 448 | $data = []; 449 | 450 | $taxonomy = \Mockery::mock(\WP_Taxonomy::class); 451 | 452 | foreach ($defaults as $key => $value) { 453 | $custom = array_key_exists($key, $properties); 454 | $field = $custom ? $properties[$key] : $value; 455 | if (!$custom && is_bool($public) && in_array($key, $publicKeys, true)) { 456 | $field = $public; 457 | } 458 | 459 | if (!$custom && is_bool($showUi) && in_array($key, $uiKeys, true)) { 460 | $field = $showUi; 461 | } 462 | 463 | if (!in_array($key, ['labels', 'cap'], true)) { 464 | $taxonomy->{$key} = $field; 465 | $reloading or $data[$key] = $field; 466 | } 467 | } 468 | 469 | $labels = (array)($properties['labels'] ?? $defaults['labels'] ?? []); 470 | $caps = (array)($properties['cap'] ?? $defaults['cap'] ?? []); 471 | $buildIn = array_key_exists($name, self::BUILT_IN); 472 | 473 | $baseType = $taxonomy->hierarchical ? 'category' : 'post_tag'; 474 | $baseData = $buildIn ? self::BUILT_IN[$name] : self::BUILT_IN[$baseType]; 475 | 476 | $data['labels'] = array_merge($baseData['labels'], $labels); 477 | $taxonomy->labels = (object)$data['labels']; 478 | 479 | $data['cap'] = array_merge($baseData['cap'], $caps); 480 | $taxonomy->cap = (object)$data['cap']; 481 | 482 | if (empty($properties['label']) && !empty($properties['labels']['name'])) { 483 | $taxonomy->label = $taxonomy->labels->name; 484 | $reloading or $data['label'] = $taxonomy->labels->name; 485 | } 486 | 487 | if (empty($properties['labels']['name']) && !empty($properties['label'])) { 488 | $taxonomy->labels->name = $taxonomy->label; 489 | $reloading or $data['labels']['name'] = $taxonomy->label; 490 | } 491 | 492 | $reloading or $this->taxonomies[$name] = $data; 493 | 494 | return $taxonomy; 495 | } 496 | 497 | /** 498 | * @return void 499 | */ 500 | private function mockFunctions(): void 501 | { 502 | if (!$this->canMockFunctions()) { 503 | return; 504 | } 505 | 506 | $this->functionExpectations->mock('get_taxonomy') 507 | ->zeroOrMoreTimes() 508 | ->andReturnUsing( 509 | function ($name) { // phpcs:ignore 510 | if (!is_scalar($name) || !isset($this->taxonomies[$name])) { 511 | return null; 512 | } 513 | 514 | return $this->__invoke($this->taxonomies[$name]); 515 | } 516 | ); 517 | 518 | $this->functionExpectations->mock('taxonomy_exists') 519 | ->zeroOrMoreTimes() 520 | ->zeroOrMoreTimes() 521 | ->andReturnUsing( 522 | function ($name) { // phpcs:ignore 523 | return is_scalar($name) && array_key_exists($name, $this->taxonomies); 524 | } 525 | ); 526 | 527 | $this->stopMockingFunctions(); 528 | } 529 | } 530 | -------------------------------------------------------------------------------- /src/Provider/Term.php: -------------------------------------------------------------------------------- 1 | term_id === (int)$term->term_id; 29 | }; 30 | } 31 | 32 | /** 33 | * @return void 34 | */ 35 | public function reset(): void 36 | { 37 | $this->terms = []; 38 | parent::reset(); 39 | } 40 | 41 | /** 42 | * @param array $properties 43 | * @return \WP_Term 44 | */ 45 | public function __invoke(array $properties = []): \WP_Term 46 | { 47 | $id = $properties['term_id'] ?? $properties['id'] ?? null; 48 | $ttId = $properties['term_taxonomy_id'] ?? $properties['tt_id'] ?? null; 49 | ($id && !$ttId) and $ttId = $id; 50 | ($ttId && !$id) and $id = $ttId; 51 | 52 | $id or $id = $this->uniqueGenerator->numberBetween(1, 99999999); 53 | $ttId or $ttId = $id; 54 | 55 | $slug = strtolower($this->uniqueGenerator->slug($this->generator->numberBetween(1, 2))); 56 | 57 | $defaults = [ 58 | 'name' => ucwords(str_replace('-', ' ', $slug)), 59 | 'slug' => $slug, 60 | 'term_group' => '', 61 | 'taxonomy' => array_rand(Taxonomy::BUILT_IN), 62 | 'description' => $this->generator->sentence, 63 | 'parent' => $this->generator->numberBetween(0, 99999), 64 | 'count' => $this->generator->numberBetween(0, 99999), 65 | 'filter' => $this->generator->randomElement(['raw', null]), 66 | ]; 67 | 68 | $term = \Mockery::mock(\WP_Term::class); 69 | $term->term_id = (int)$id; 70 | $term->term_taxonomy_id = (int)$ttId; 71 | 72 | $toArray = ['term_id' => (int)$id, 'term_taxonomy_id' => (int)$ttId]; 73 | foreach ($defaults as $key => $value) { 74 | $field = array_key_exists($key, $properties) ? $properties[$key] : $value; 75 | $toArray[$key] = $field; 76 | $term->{$key} = $field; 77 | } 78 | 79 | /** @var \Mockery\MockInterface $term */ 80 | $term->data = (object)$toArray; 81 | $term->shouldReceive('to_array')->andReturn($toArray)->byDefault(); 82 | 83 | $this->terms[$term->term_id] = $toArray; 84 | $this->mockFunctions(); 85 | 86 | return $term; 87 | } 88 | 89 | /** 90 | * @return void 91 | */ 92 | private function mockFunctions(): void 93 | { 94 | if (!$this->canMockFunctions()) { 95 | return; 96 | } 97 | 98 | $this->functionExpectations->mock('get_term') 99 | ->zeroOrMoreTimes() 100 | ->andReturnUsing( 101 | function (...$args) { //phpcs:ignore 102 | return $this->mockedGetTerm(...$args); 103 | } 104 | ); 105 | 106 | $this->functionExpectations->mock('get_term_by') 107 | ->zeroOrMoreTimes() 108 | ->andReturnUsing( 109 | function (...$args) { //phpcs:ignore 110 | return $this->mockedGetTermBy(...$args); 111 | } 112 | ); 113 | 114 | $this->stopMockingFunctions(); 115 | } 116 | 117 | /** 118 | * @param $term 119 | * @param string $taxonomy 120 | * @param string $output 121 | * @return array|\WP_Term|null 122 | * 123 | * phpcs:disable Inpsyde.CodeQuality.ArgumentTypeDeclaration 124 | * phpcs:disable Inpsyde.CodeQuality.ReturnTypeDeclaration 125 | */ 126 | private function mockedGetTerm($term, $taxonomy = '', $output = 'OBJECT') 127 | { 128 | // phpcs:enable Inpsyde.CodeQuality.ArgumentTypeDeclaration 129 | // phpcs:enable Inpsyde.CodeQuality.ReturnTypeDeclaration 130 | 131 | $termId = is_object($term) ? $term->term_id ?? null : $term; 132 | if (!$termId || !is_numeric($termId)) { 133 | return null; 134 | } 135 | 136 | $data = $this->terms[(int)$termId] ?? null; 137 | if (!$data || ($taxonomy && $taxonomy !== $data['taxonomy'])) { 138 | return null; 139 | } 140 | 141 | $termObj = $this->__invoke($data); 142 | 143 | if ($output === 'ARRAY_A') { 144 | return $termObj->to_array(); 145 | } elseif ($output === 'ARRAY_N') { 146 | return array_values($termObj->to_array()); 147 | } 148 | 149 | return $termObj; 150 | } 151 | 152 | /** 153 | * @param $field 154 | * @param $value 155 | * @param string $taxonomy 156 | * @param string $output 157 | * @return array|\WP_Term|bool 158 | * 159 | * phpcs:disable Inpsyde.CodeQuality.ArgumentTypeDeclaration 160 | * phpcs:disable Inpsyde.CodeQuality.ReturnTypeDeclaration 161 | */ 162 | private function mockedGetTermBy($field, $value, $taxonomy = '', $output = 'OBJECT') 163 | { 164 | // phpcs:enable Inpsyde.CodeQuality.ArgumentTypeDeclaration 165 | // phpcs:enable Inpsyde.CodeQuality.ReturnTypeDeclaration 166 | 167 | if (!in_array($field, ['id', 'term_taxonomy_id', 'slug', 'name'], true)) { 168 | return false; 169 | } 170 | 171 | $id = $field === 'id' ? $value : null; 172 | if ($id === null) { 173 | $isTtId = $field === 'term_taxonomy_id'; 174 | if (($isTtId && !is_numeric($value)) 175 | || (!$isTtId && (!is_string($value) || !$taxonomy)) 176 | ) { 177 | return false; 178 | } 179 | 180 | $isTtId and $value = (int)$value; 181 | if ($field === 'slug') { 182 | $value = preg_replace('/[^a-z0-9_-]/i', '-', strtolower($value)); 183 | } 184 | 185 | $values = array_column($this->terms, $field, 'term_id'); 186 | $id = array_search($value, $values, true); 187 | } 188 | 189 | return $id && is_numeric($id) 190 | ? ($this->mockedGetTerm($id, $taxonomy, $output) ?: false) 191 | : false; 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /src/Provider/User.php: -------------------------------------------------------------------------------- 1 | [ 21 | 'switch_themes', 22 | 'edit_themes', 23 | 'activate_plugins', 24 | 'edit_plugins', 25 | 'edit_users', 26 | 'edit_files', 27 | 'manage_options', 28 | 'moderate_comments', 29 | 'manage_categories', 30 | 'manage_links', 31 | 'upload_files', 32 | 'import', 33 | 'unfiltered_html', 34 | 'edit_posts', 35 | 'edit_others_posts', 36 | 'edit_published_posts', 37 | 'publish_posts', 38 | 'edit_pages', 39 | 'read', 40 | 'level_10', 41 | 'level_9', 42 | 'level_8', 43 | 'level_7', 44 | 'level_6', 45 | 'level_5', 46 | 'level_4', 47 | 'level_3', 48 | 'level_2', 49 | 'level_1', 50 | 'level_0', 51 | 'edit_others_pages', 52 | 'edit_published_pages', 53 | 'publish_pages', 54 | 'delete_pages', 55 | 'delete_others_pages', 56 | 'delete_published_pages', 57 | 'delete_posts', 58 | 'delete_others_posts', 59 | 'delete_published_posts', 60 | 'delete_private_posts', 61 | 'edit_private_posts', 62 | 'read_private_posts', 63 | 'delete_private_pages', 64 | 'edit_private_pages', 65 | 'read_private_pages', 66 | 'delete_users', 67 | 'create_users', 68 | 'unfiltered_upload', 69 | 'edit_dashboard', 70 | 'update_plugins', 71 | 'delete_plugins', 72 | 'install_plugins', 73 | 'update_themes', 74 | 'install_themes', 75 | 'update_core', 76 | 'list_users', 77 | 'remove_users', 78 | 'promote_users', 79 | 'edit_theme_options', 80 | 'delete_themes', 81 | 'export', 82 | 'manage_woocommerce', 83 | 'view_woocommerce_reports', 84 | 'edit_product', 85 | 'read_product', 86 | 'delete_product', 87 | 'edit_products', 88 | 'edit_others_products', 89 | 'publish_products', 90 | 'read_private_products', 91 | 'delete_products', 92 | 'delete_private_products', 93 | 'delete_published_products', 94 | 'delete_others_products', 95 | 'edit_private_products', 96 | 'edit_published_products', 97 | 'manage_product_terms', 98 | 'edit_product_terms', 99 | 'delete_product_terms', 100 | 'assign_product_terms', 101 | 'edit_shop_order', 102 | 'read_shop_order', 103 | 'delete_shop_order', 104 | 'edit_shop_orders', 105 | 'edit_others_shop_orders', 106 | 'publish_shop_orders', 107 | 'read_private_shop_orders', 108 | 'delete_shop_orders', 109 | 'delete_private_shop_orders', 110 | 'delete_published_shop_orders', 111 | 'delete_others_shop_orders', 112 | 'edit_private_shop_orders', 113 | 'edit_published_shop_orders', 114 | 'manage_shop_order_terms', 115 | 'edit_shop_order_terms', 116 | 'delete_shop_order_terms', 117 | 'assign_shop_order_terms', 118 | 'edit_shop_coupon', 119 | 'read_shop_coupon', 120 | 'delete_shop_coupon', 121 | 'edit_shop_coupons', 122 | 'edit_others_shop_coupons', 123 | 'publish_shop_coupons', 124 | 'read_private_shop_coupons', 125 | 'delete_shop_coupons', 126 | 'delete_private_shop_coupons', 127 | 'delete_published_shop_coupons', 128 | 'delete_others_shop_coupons', 129 | 'edit_private_shop_coupons', 130 | 'edit_published_shop_coupons', 131 | 'manage_shop_coupon_terms', 132 | 'edit_shop_coupon_terms', 133 | 'delete_shop_coupon_terms', 134 | 'assign_shop_coupon_terms', 135 | ], 136 | 'editor' => [ 137 | 'moderate_comments', 138 | 'manage_categories', 139 | 'manage_links', 140 | 'upload_files', 141 | 'unfiltered_html', 142 | 'edit_posts', 143 | 'edit_others_posts', 144 | 'edit_published_posts', 145 | 'publish_posts', 146 | 'edit_pages', 147 | 'read', 148 | 'level_7', 149 | 'level_6', 150 | 'level_5', 151 | 'level_4', 152 | 'level_3', 153 | 'level_2', 154 | 'level_1', 155 | 'level_0', 156 | 'edit_others_pages', 157 | 'edit_published_pages', 158 | 'publish_pages', 159 | 'delete_pages', 160 | 'delete_others_pages', 161 | 'delete_published_pages', 162 | 'delete_posts', 163 | 'delete_others_posts', 164 | 'delete_published_posts', 165 | 'delete_private_posts', 166 | 'edit_private_posts', 167 | 'read_private_posts', 168 | 'delete_private_pages', 169 | 'edit_private_pages', 170 | 'read_private_pages', 171 | ], 172 | 'author' => [ 173 | 'upload_files', 174 | 'edit_posts', 175 | 'edit_published_posts', 176 | 'publish_posts', 177 | 'read', 178 | 'level_2', 179 | 'level_1', 180 | 'level_0', 181 | 'delete_posts', 182 | 'delete_published_posts', 183 | ], 184 | 'contributor' => [ 185 | 'edit_posts', 186 | 'read', 187 | 'level_1', 188 | 'level_0', 189 | 'delete_posts', 190 | ], 191 | 'subscriber' => [ 192 | 'read', 193 | 'level_0', 194 | ], 195 | ]; 196 | 197 | const LEVELS = [ 198 | 'administrator' => 10, 199 | 'editor' => 7, 200 | 'author' => 2, 201 | 'contributor' => 1, 202 | 'subscriber' => 0, 203 | ]; 204 | 205 | /** 206 | * @var array[] 207 | */ 208 | private $users = []; 209 | 210 | /** 211 | * @var bool 212 | */ 213 | private $currentUserSet = false; 214 | 215 | /** 216 | * @param \WP_User $user 217 | * @return callable 218 | */ 219 | public static function withSame(\WP_User $user): callable 220 | { 221 | return function (\WP_User $theUser) use ($user): bool { 222 | return (int)$theUser->ID === (int)$user->ID; 223 | }; 224 | } 225 | 226 | /** 227 | * @return void 228 | */ 229 | public function reset(): void 230 | { 231 | $this->users = []; 232 | $this->currentUserSet = false; 233 | parent::reset(); 234 | } 235 | 236 | /** 237 | * @param array $properties 238 | * @return \WP_User|MonkeyWpUser 239 | * 240 | * phpcs:disable Inpsyde.CodeQuality.FunctionLength.TooLong 241 | */ 242 | public function __invoke(array $properties = []): \WP_User 243 | { 244 | // phpcs:enable Inpsyde.CodeQuality.FunctionLength.TooLong 245 | 246 | $properties = array_change_key_case($properties, CASE_LOWER); 247 | 248 | $user = $this->createBaseUser($properties); 249 | 250 | $noPrefixKeys = [ 251 | 'user_firstname' => 'first_name', 252 | 'user_lastname' => 'last_name', 253 | 'user_description' => 'description', 254 | ]; 255 | 256 | $login = $this->uniqueGenerator->userName; 257 | 258 | $defaults = [ 259 | 'user_login' => $login, 260 | 'user_pass' => $this->generator->password, 261 | 'user_nicename' => preg_replace('/[^a-z0-9-]/', '-', strtolower($login)), 262 | 'user_email' => $this->uniqueGenerator->email, 263 | 'user_url' => 'https://' . $this->generator->domainName, 264 | 'user_registered' => $this->generator->date('Y-m-d H:i:s'), 265 | 'user_activation_key' => rand(1, 100) > 80 ? $this->generator->password(32) : '', 266 | 'user_status' => '0', 267 | 'display_name' => $this->generator->name, 268 | 'user_description' => $this->generator->sentence, 269 | 'user_firstname' => $this->generator->firstName, 270 | 'user_lastname' => $this->generator->lastName, 271 | 'nickname' => $login, 272 | 'spam' => '', 273 | 'deleted' => '', 274 | 'locale' => $this->generator->locale, 275 | 'rich_editing' => $this->generator->randomElement(['true', 'false']), 276 | 'syntax_highlighting' => $this->generator->randomElement(['true', 'false']), 277 | 'cap_key' => 'wp_capabilities', 278 | 'filter' => $this->generator->randomElement(['raw', null]), 279 | ]; 280 | 281 | foreach ($defaults as $key => $value) { 282 | $hasKey = array_key_exists($key, $properties); 283 | $noPrefixKey = ''; 284 | if (strpos($key, 'user_') === 0) { 285 | $noPrefixKey = $noPrefixKeys[$key] ?? substr($key, 5); 286 | } 287 | 288 | if (!$hasKey && $noPrefixKey) { 289 | $hasKey = array_key_exists($noPrefixKey, $properties); 290 | $hasKey and $properties[$key] = $properties[$noPrefixKey]; 291 | } 292 | 293 | if (!$hasKey && $key === 'user_pass' && !empty($properties['password'])) { 294 | $properties[$key] = $properties['password']; 295 | $hasKey = true; 296 | } 297 | 298 | $field = $hasKey ? $properties[$key] : $value; 299 | $user->{$key} = $field; 300 | $get[$key] = $field; 301 | 302 | if ($noPrefixKey && in_array($noPrefixKey, $noPrefixKeys, true)) { 303 | $user->{$noPrefixKey} = $field; 304 | } 305 | } 306 | 307 | $toArray = [ 308 | 'ID' => $user->ID, 309 | 'user_login' => $user->user_login, 310 | 'user_pass' => $user->user_pass, 311 | 'user_nicename' => $user->user_nicename, 312 | 'user_email' => $user->user_email, 313 | 'user_url' => $user->user_url, 314 | 'user_registered' => $user->user_registered, 315 | 'user_activation_key' => $user->user_activation_key, 316 | 'user_status' => $user->user_status, 317 | 'display_name' => $user->display_name, 318 | ]; 319 | 320 | $user->data = (object)$toArray; 321 | 322 | $user->shouldReceive('to_array')->andReturn($toArray)->byDefault(); 323 | 324 | $getKeys = [ 325 | 'user_description', 326 | 'description', 327 | 'user_firstname', 328 | 'first_name', 329 | 'user_lastname', 330 | 'last_name', 331 | 'user_level', 332 | 'nickname', 333 | 'spam', 334 | 'deleted', 335 | 'locale', 336 | 'rich_editing', 337 | 'syntax_highlighting', 338 | ]; 339 | 340 | $get = $toArray; 341 | foreach ($getKeys as $key) { 342 | $get[$key] = $user->{$key}; 343 | } 344 | 345 | $user->shouldReceive('get') 346 | ->andReturnUsing( 347 | function ($key) use ($get) { //phpcs:ignore 348 | if (strtoupper($key) === 'ID' && $key !== 'ID') { 349 | throw new \Error('Please use `WP_User::ID` instead of `WP_User::id`.'); 350 | } 351 | 352 | return array_key_exists($key, $get) ? (string)$get[$key] : false; 353 | } 354 | ) 355 | ->byDefault(); 356 | 357 | $siteId = $properties['site_id'] 358 | ?? $properties['blog_id'] 359 | ?? (rand(1, 100) > 80 ? $this->generator->numberBetween(1, 10) : 1); 360 | 361 | $user->shouldReceive('get_site_id')->andReturn($siteId)->byDefault(); 362 | 363 | $this->saveUser($get, (int)$siteId, $user); 364 | $this->mockFunctions(); 365 | 366 | $user->shouldReceive('__monkeyMakeCurrent') 367 | ->withNoArgs() 368 | ->andReturnUsing( 369 | function () use (&$user) { //phpcs:ignore 370 | $this->makeCurrent($user); 371 | 372 | return $user; 373 | } 374 | ); 375 | 376 | return $user; 377 | } 378 | 379 | /** 380 | * @param array $properties 381 | * @return \Mockery\MockInterface|\WP_User 382 | */ 383 | private function createBaseUser(array $properties): \Mockery\MockInterface 384 | { 385 | [$userRoles, $allCaps, $userCaps, $level] = $this->extractRolesAndCapabilities($properties); 386 | 387 | $id = array_key_exists('id', $properties) 388 | ? $properties['id'] 389 | : $this->uniqueGenerator->numberBetween(1, 99999999); 390 | 391 | $user = \Mockery::mock(\WP_User::class); 392 | $user->ID = (int)$id; 393 | $user->roles = $userRoles; 394 | $user->caps = $userCaps; 395 | $user->allcaps = $allCaps; 396 | $user->user_level = is_int($level) ? $level : ''; 397 | 398 | $user->shouldReceive('exists')->andReturn($id > 0)->byDefault(); 399 | 400 | $user->shouldReceive('has_cap') 401 | ->with(\Mockery::type('string')) 402 | ->andReturnUsing( 403 | function ($cap) use ($allCaps) { //phpcs:ignore 404 | return !empty($allCaps[$cap]); 405 | } 406 | ) 407 | ->byDefault(); 408 | 409 | return $user; 410 | } 411 | 412 | /** 413 | * @param array $properties 414 | * @param int $siteId 415 | * @param \WP_User $user 416 | */ 417 | private function saveUser(array $properties, int $siteId, \WP_User $user) 418 | { 419 | $updatedProps = $properties; 420 | $updatedProps['site_id'] = $siteId; 421 | $updatedProps['allcaps'] = $user->allcaps; 422 | $updatedProps['roles'] = $user->roles; 423 | $updatedProps['user_level'] = $user->user_level; 424 | 425 | $this->users[$user->ID] = $updatedProps; 426 | } 427 | 428 | /** 429 | * @return void 430 | * 431 | * phpcs:disable Inpsyde.CodeQuality.FunctionLength.TooLong 432 | * phpcs:disable Generic.Metrics.NestingLevel 433 | */ 434 | private function mockFunctions(): void 435 | { 436 | // phpcs:enable Inpsyde.CodeQuality.FunctionLength.TooLong 437 | // phpcs:enable Generic.Metrics.NestingLevel 438 | 439 | if (!$this->canMockFunctions()) { 440 | return; 441 | } 442 | 443 | $this->functionExpectations->mock('get_userdata') 444 | ->zeroOrMoreTimes() 445 | ->andReturnUsing( 446 | function ($userId = null) { //phpcs:ignore 447 | if (!is_numeric($userId) || !array_key_exists((int)$userId, $this->users)) { 448 | return false; 449 | } 450 | 451 | return $this->__invoke($this->users[(int)$userId]); 452 | } 453 | ); 454 | 455 | $this->functionExpectations->mock('get_user_by') 456 | ->zeroOrMoreTimes() 457 | ->with(\Mockery::any(), \Mockery::any()) 458 | ->andReturnUsing( 459 | function ($field, $value) { //phpcs:ignore 460 | if (!in_array($field, ['id', 'ID', 'slug', 'email', 'login'], true)) { 461 | return false; 462 | } 463 | 464 | $byId = $field === 'id' || $field === 'ID'; 465 | $id = $byId ? $value : null; 466 | if (!$byId) { 467 | if (!is_string($value)) { 468 | return false; 469 | } 470 | 471 | $fieldName = $field === 'slug' ? 'nicename' : $field; 472 | $fields = array_column($this->users, "user_{$fieldName}", 'ID'); 473 | $id = array_search($value, $fields, true); 474 | } 475 | 476 | if (!is_numeric($id) || !array_key_exists((int)$id, $this->users)) { 477 | return false; 478 | } 479 | 480 | return $this->__invoke($this->users[(int)$id]); 481 | } 482 | ); 483 | 484 | $this->functionExpectations->mock('user_can') 485 | ->zeroOrMoreTimes() 486 | ->with(\Mockery::any(), \Mockery::any()) 487 | ->andReturnUsing( 488 | function ($user = null, $cap = null) { // phpcs:ignore 489 | $userId = is_object($user) ? $user->ID : $user; 490 | if (!is_numeric($userId) 491 | || !is_scalar($cap) 492 | || !array_key_exists((int)$userId, $this->users) 493 | ) { 494 | return false; 495 | } 496 | 497 | $caps = $this->users[(int)$userId]['allcaps']; 498 | 499 | return !empty($caps[$cap]); 500 | } 501 | ); 502 | 503 | $this->stopMockingFunctions(); 504 | } 505 | 506 | /** 507 | * @param \WP_User $user 508 | * @return void 509 | */ 510 | private function makeCurrent(\WP_User $user): void 511 | { 512 | if ($this->currentUserSet) { 513 | throw new \Error('Only one user can be made the current one.'); 514 | } 515 | 516 | $this->currentUserSet = true; 517 | 518 | Monkey\Functions\when('get_current_user_id')->justReturn($user->ID); 519 | Monkey\Functions\when('wp_get_current_user')->justReturn($user); 520 | Monkey\Functions\when('current_user_can')->alias([$user, 'has_cap']); 521 | } 522 | 523 | /** 524 | * @param array $properties 525 | * @return array 526 | */ 527 | private function extractRolesAndCapabilities(array $properties): array 528 | { 529 | $role = $properties['role'] ?? null; 530 | $roles = $properties['roles'] ?? null; 531 | $level = $properties['user_level'] ?? $properties['level'] ?? null; 532 | $allCaps = $properties['allcaps'] 533 | ?? $properties['caps'] 534 | ?? $properties['capabilities'] 535 | ?? null; 536 | 537 | $rawRoles = is_array($roles) ? $roles : []; 538 | ($role && is_string($role)) and $rawRoles[] = $role; 539 | $userRoles = array_values(array_filter($rawRoles, 'is_string')); 540 | is_numeric($level) and $level = (int)$level; 541 | 542 | if (is_int($level) && !$userRoles) { 543 | $foundLevels = []; 544 | $level = min(max((int)$level, 0), 10); 545 | foreach (self::LEVELS as $levelRole => $roleLevel) { 546 | ($level >= $roleLevel) and $foundLevels[$roleLevel] = $levelRole; 547 | } 548 | if ($foundLevels) { 549 | ksort($foundLevels, SORT_NUMERIC); 550 | $userRoles[] = end($foundLevels); 551 | } 552 | } 553 | 554 | if (!$userRoles && $roles !== []) { 555 | $userRoles = $this->generator->randomElements(array_keys(self::CAPS), 3); 556 | } 557 | 558 | $hasCaps = is_array($allCaps); 559 | [$allCaps, $userCaps] = $this->prepareCapabilities( 560 | $userRoles, 561 | $hasCaps ? $allCaps : [], 562 | $hasCaps 563 | ); 564 | 565 | if (!is_int($level)) { 566 | $level = null; 567 | foreach ($userRoles as $role) { 568 | $level = $level === null 569 | ? (int)(self::LEVELS[$role] ?? 0) 570 | : (int)max($level, self::LEVELS[$role] ?? 0); 571 | } 572 | 573 | if ($level === null && $roles !== []) { 574 | $level = $this->generator->numberBetween(0, 10); 575 | } 576 | } 577 | 578 | if (is_int($level)) { 579 | $level = min(max((int)$level, 0), 10); 580 | for ($i = 0; $i <= $level; $i++) { 581 | array_key_exists("level_{$i}", $allCaps) or $allCaps["level_{$i}"] = true; 582 | } 583 | } 584 | 585 | return [array_unique(array_filter($userRoles)), $allCaps, $userCaps, $level]; 586 | } 587 | 588 | /** 589 | * @param array $roles 590 | * @param array $allcaps 591 | * @param bool $hasCaps 592 | * @return array 593 | */ 594 | private function prepareCapabilities( 595 | array $roles, 596 | array $allcaps, 597 | bool $hasCaps 598 | ): array { 599 | 600 | $userCaps = []; 601 | 602 | foreach ($roles as $role) { 603 | $userCaps[$role] = true; 604 | if (!$hasCaps && array_key_exists($role, self::CAPS)) { 605 | $roleCaps = array_fill_keys(self::CAPS[$role], true); 606 | $allcaps = array_merge($allcaps, $roleCaps); 607 | } 608 | } 609 | 610 | return [$hasCaps ? $allcaps : array_merge($allcaps, $userCaps), $userCaps]; 611 | } 612 | } 613 | -------------------------------------------------------------------------------- /src/Providers.php: -------------------------------------------------------------------------------- 1 | > 226 | */ 227 | private $methods = [ 228 | self::ONE => [], 229 | self::MANY => [], 230 | ]; 231 | 232 | /** 233 | * @var array 234 | */ 235 | private $providers = []; 236 | 237 | /** 238 | * @var FunctionExpectations 239 | */ 240 | private $functionExpectations; 241 | 242 | /** 243 | * @param Generator $generator 244 | */ 245 | public function __construct(Generator $generator) 246 | { 247 | $this->generator = $generator; 248 | $this->functionExpectations = new FunctionExpectations(); 249 | } 250 | 251 | /** 252 | * @param string $method 253 | * @param array $args 254 | * @return object|object[] 255 | * 256 | * phpcs:disable Inpsyde.CodeQuality.ReturnTypeDeclaration 257 | */ 258 | public function __call(string $method, array $args = []) 259 | { 260 | //phpcs:enable Inpsyde.CodeQuality.ReturnTypeDeclaration 261 | 262 | $this->bailWhenNotInitialized(sprintf('$faker->wp()->%s', $method)); 263 | 264 | /** 265 | * @var bool $forceMany 266 | * @var string $method 267 | * @var bool|null $atLeast 268 | * @var bool|null $atMost 269 | */ 270 | [$forceMany, $method, $atLeast, $atMost] = $this->extractAtLeastArMost($method); 271 | 272 | /** 273 | * @var callable $object 274 | * @var bool $isMany 275 | */ 276 | [$object, $isMany] = $this->factoryProvider($method); 277 | 278 | if (!$isMany && !$forceMany) { 279 | return $object(...$args); 280 | } 281 | 282 | $min = $atLeast ?? 0; 283 | $max = $atMost ?? ($min + $this->generator->numberBetween(1, 10)); 284 | 285 | $num = null; 286 | if ($atLeast === null && $atMost === null) { 287 | $num = $args ? array_shift($args) : $this->generator->numberBetween(0, 10); 288 | is_numeric($num) or $this->bailForCallManyWithNoNumber($method); 289 | } 290 | 291 | $params = $args ? reset($args) : []; 292 | if (!is_array($params)) { 293 | $this->bailForCallManyWithBadParams($method); 294 | } 295 | 296 | if ($num === null) { 297 | $num = $min === $max ? $min : $this->generator->numberBetween($min, $max); 298 | } 299 | 300 | $objects = []; 301 | for ($i = 0; $i < $num; $i++) { 302 | $objects[] = $object(...$args); 303 | } 304 | 305 | return $objects; 306 | } 307 | 308 | /** 309 | * @param string $method 310 | * @return mixed 311 | * 312 | * phpcs:disable Inpsyde.CodeQuality.ReturnTypeDeclaration 313 | */ 314 | public function __get(string $method) 315 | { 316 | // phpcs:enable Inpsyde.CodeQuality.ReturnTypeDeclaration 317 | 318 | return $this->__call($method); 319 | } 320 | 321 | /** 322 | * @param string $providerClass 323 | * @param string $methodOne 324 | * @param string $methodMany 325 | * @return Providers 326 | */ 327 | public function __addProviderClass( // phpcs:ignore 328 | string $providerClass, 329 | string $methodOne, 330 | string $methodMany 331 | ): Providers { 332 | 333 | $reflection = null; 334 | 335 | if (!is_subclass_of($providerClass, Provider::class)) { 336 | $this->bailForInvalidClass($providerClass); 337 | } 338 | 339 | try { 340 | $reflection = new \ReflectionClass($providerClass); 341 | } catch (\Throwable $throwable) { 342 | $this->bailForInvalidClass($providerClass, $throwable); 343 | } 344 | 345 | /** \ReflectionClass $reflection */ 346 | if (!$reflection->isInstantiable()) { 347 | $this->bailForInvalidClass($providerClass); 348 | } 349 | 350 | if ($methodOne === 'wp' || substr($methodOne, 0, 2) === '__') { 351 | $this->bailForReservedMethod($methodOne); 352 | } 353 | 354 | if ($methodMany === 'wp' || substr($methodMany, 0, 2) === '__') { 355 | $this->bailForReservedMethod($methodOne); 356 | } 357 | 358 | $this->methods[self::ONE][$methodOne] = $providerClass; 359 | $this->methods[self::MANY][$methodMany] = $providerClass; 360 | unset($this->providers[$providerClass]); 361 | 362 | return $this; 363 | } 364 | 365 | /** 366 | * @return Providers 367 | */ 368 | public function __resetUnique(): Providers // phpcs:ignore 369 | { 370 | /** @var Provider $provider */ 371 | foreach ($this->providers as $provider) { 372 | $provider->resetUnique(); 373 | } 374 | 375 | return $this; 376 | } 377 | 378 | /** 379 | * @param string $function 380 | * @return Expectation 381 | */ 382 | public function __monkeyFunction(string $function): Expectation // phpcs:ignore 383 | { 384 | return $this->functionExpectations->replace($function); 385 | } 386 | 387 | /** 388 | * @return Providers 389 | */ 390 | public function __reset(): Providers // phpcs:ignore 391 | { 392 | $this->functionExpectations->reset(); 393 | 394 | /** @var Provider $provider */ 395 | foreach ($this->providers as $provider) { 396 | $provider->reset(); 397 | } 398 | 399 | return $this; 400 | } 401 | 402 | /** 403 | * @return Providers 404 | */ 405 | public function wp(): Providers 406 | { 407 | $this->bailWhenNotInitialized(__METHOD__); 408 | 409 | return $this; 410 | } 411 | 412 | /** 413 | * @param string $method 414 | * @return array 415 | */ 416 | private function extractAtLeastArMost(string $method): array 417 | { 418 | $numWords = [ 419 | 'One' => 1, 420 | 'Two' => 2, 421 | 'Three' => 3, 422 | 'Four' => 4, 423 | 'Five' => 5, 424 | ]; 425 | 426 | $prefixExp = 'atLeast|atMost|between'; 427 | $numExp = '(?:[0-9]+)|(?:' . implode('|', array_keys($numWords)) . ')'; 428 | $dynamicMethod = preg_match( 429 | "/^(?P{$prefixExp})(?{$numExp})(?:And(?P{$numExp}))?(?P.+)\$/", 430 | $method, 431 | $matches 432 | ); 433 | 434 | if (!$dynamicMethod) { 435 | return [false, $method, null, null]; 436 | } 437 | 438 | $minOrMax = $matches['prefix'] !== 'between' && empty($matches['n2']); 439 | $between = $matches['prefix'] === 'between' && !empty($matches['n2']); 440 | 441 | if (!$minOrMax && !$between) { 442 | return [false, $method, null, null]; 443 | } 444 | 445 | if ($minOrMax) { 446 | $isNum = is_numeric($matches['n1']); 447 | $atNum = $isNum ? (int)$matches['n1'] : $numWords[$matches['n1']]; 448 | $maybeMethod = lcfirst($matches['method']); 449 | if (($atNum === 1 && array_key_exists($maybeMethod, $this->methods[self::ONE])) 450 | || array_key_exists($maybeMethod, $this->methods[self::MANY]) 451 | ) { 452 | $atLeast = $matches['prefix'] === 'atLeast' ? $atNum : null; 453 | $atMost = $matches['prefix'] === 'atLeast' ? null : $atNum; 454 | 455 | return [true, $maybeMethod, $atLeast, $atMost]; 456 | } 457 | 458 | return [false, $method, null, null]; 459 | } 460 | 461 | $isNumMin = is_numeric($matches['n1']); 462 | $isNumMax = is_numeric($matches['n2']); 463 | $maybeMethod = lcfirst($matches['method']); 464 | 465 | if (array_key_exists($maybeMethod, $this->methods[self::MANY])) { 466 | $min = $isNumMin ? (int)$matches['n1'] : $numWords[$matches['n1']]; 467 | $max = $isNumMax ? (int)$matches['n2'] : $numWords[$matches['n2']]; 468 | $atLeast = min($min, $max); 469 | $atMost = max($min, $max); 470 | 471 | return [true, $maybeMethod, $atLeast, $atMost]; 472 | } 473 | 474 | return [false, $method, null, null]; 475 | } 476 | 477 | /** 478 | * @param string $method 479 | * @return array 480 | */ 481 | private function factoryProvider(string $method): array 482 | { 483 | $isOne = array_key_exists($method, $this->methods[self::ONE]); 484 | $isMany = array_key_exists($method, $this->methods[self::MANY]); 485 | 486 | if (!$isOne && !$isMany) { 487 | $this->bailForInvalidMethod($method); 488 | } 489 | 490 | if ($isOne && $isMany) { 491 | $this->bailForNotUniquelyIdentifiedClass($method); 492 | } 493 | 494 | $class = $isMany ? $this->methods[self::MANY][$method] : $this->methods[self::ONE][$method]; 495 | 496 | if (empty($this->providers[$class])) { 497 | $isFunctionMocker = is_subclass_of($class, FunctionMockerProvider::class, true); 498 | /** @var Provider|FunctionMockerProvider $provider */ 499 | $provider = $isFunctionMocker 500 | ? new $class($this->generator, $this->functionExpectations) 501 | : new $class($this->generator); 502 | 503 | $this->providers[$class] = $provider; 504 | } 505 | 506 | return [$this->providers[$class], $isMany]; 507 | } 508 | 509 | /** 510 | * @param string $method 511 | */ 512 | private function bailWhenNotInitialized(string $method): void 513 | { 514 | if (!empty($this->methods[self::ONE])) { 515 | return; 516 | } 517 | 518 | throw new \Error( 519 | sprintf( 520 | "Can't call '%s()' before %s has been initialized.", 521 | $method, 522 | __CLASS__ 523 | ) 524 | ); 525 | } 526 | 527 | /** 528 | * @param string $class 529 | * @param \Throwable|null $previous 530 | */ 531 | private function bailForInvalidClass(string $class, \Throwable $previous = null): void 532 | { 533 | throw new \Error("{$class} is not a valid provider class.", 0, $previous); 534 | } 535 | 536 | /** 537 | * @param string $method 538 | */ 539 | private function bailForInvalidMethod(string $method): void 540 | { 541 | throw new \Error(sprintf('Call to undefined method %s::%s()', __CLASS__, $method)); 542 | } 543 | 544 | /** 545 | * @param string $method 546 | */ 547 | private function bailForNotUniquelyIdentifiedClass(string $method): void 548 | { 549 | throw new \Error( 550 | "Impossible to uniquely identify provider class for \$faker->wp()->{$method}()." 551 | ); 552 | } 553 | 554 | /** 555 | * @param string $method 556 | */ 557 | private function bailForReservedMethod(string $method): void 558 | { 559 | throw new \Error( 560 | "'{$method}' is a reserved method name, can't be used for \$faker->wp()->{$method}()." 561 | ); 562 | } 563 | 564 | /** 565 | * @param string $method 566 | */ 567 | private function bailForCallManyWithNoNumber(string $method): void 568 | { 569 | throw new \Error("\$faker->wp()->{$method}() first argument must be a number."); 570 | } 571 | 572 | /** 573 | * @param string $method 574 | */ 575 | private function bailForCallManyWithBadParams(string $method): void 576 | { 577 | throw new \Error("\$faker->wp()->{$method}() requires options provided as array."); 578 | } 579 | } 580 | -------------------------------------------------------------------------------- /tests/cases/functional/BrainFakerTest.php: -------------------------------------------------------------------------------- 1 | wpFaker->post(['id' => 123, 'author' => 456]); 25 | $this->wpFaker->user(['id' => 456, 'role' => 'editor'])->__monkeyMakeCurrent(); 26 | 27 | static::assertSame('It works!', $this->productionCode(123)); 28 | } 29 | 30 | public function testPostAndUserNoRole() 31 | { 32 | $this->wpFaker->post(['id' => 123, 'author' => 456]); 33 | $this->wpFaker->user(['id' => 456, 'role' => 'subscriber'])->__monkeyMakeCurrent(); 34 | 35 | static::assertSame('Can\'t edit pages', $this->productionCode(123)); 36 | } 37 | 38 | public function testPostAndUserNoPost() 39 | { 40 | $this->wpFaker->post(['id' => 123, 'author' => 456]); 41 | $this->wpFaker->user(['id' => 456, 'role' => 'subscriber'])->__monkeyMakeCurrent(); 42 | 43 | static::assertSame('No post', $this->productionCode(124)); 44 | } 45 | 46 | public function testPostAndUserNoCurrentUser() 47 | { 48 | $this->wpFaker->post(['id' => 123, 'author' => 457]); 49 | $this->wpFaker->user(['id' => 456, 'role' => 'subscriber'])->__monkeyMakeCurrent(); 50 | 51 | static::assertSame('No user ID', $this->productionCode(123)); 52 | } 53 | 54 | public function testPostAndUserNotExists() 55 | { 56 | $this->wpFaker->post(['id' => 123, 'author' => 456]); 57 | 58 | $user = $this->wpFaker->user(['id' => 456, 'role' => 'subscriber']) 59 | ->__monkeyMakeCurrent(); 60 | 61 | $this->wpFaker->__monkeyFunction('get_userdata') 62 | ->once() 63 | ->with($user->ID) 64 | ->andReturn($user); 65 | 66 | $user->shouldReceive('exists') 67 | ->once() 68 | ->andReturn(false); 69 | 70 | static::assertSame('Bad user', $this->productionCode(123)); 71 | } 72 | 73 | public function testWithoutBrainFaker() 74 | { 75 | $post = \Mockery::mock(\WP_Post::class); 76 | $post->post_author = 456; 77 | 78 | $user = \Mockery::mock(\WP_User::class); 79 | $user->shouldReceive('exists')->andReturn(true); 80 | $user->shouldReceive('has_cap')->with('edit_others_pages')->andReturn(true); 81 | $user->ID = 456; 82 | $user->user_email = 'me@example.com'; 83 | $user->user_url = 'http://example.com'; 84 | 85 | Monkey\Functions\expect('get_post')->with(123)->andReturn($post); 86 | Monkey\Functions\expect('user_can')->with($user, 'upload_files')->andReturn(true); 87 | Monkey\Functions\expect('get_current_user_id')->andReturn(456); 88 | Monkey\Functions\expect('get_userdata')->with(456)->andReturn($user); 89 | Monkey\Functions\expect('wp_get_current_user')->andReturn($user); 90 | 91 | static::assertSame('It works!', $this->productionCode(123)); 92 | } 93 | 94 | /** 95 | * @param int $postId 96 | * @return string 97 | */ 98 | private function productionCode(int $postId): string 99 | { 100 | $post = get_post($postId); 101 | 102 | if (!$post instanceof \WP_Post) { 103 | return 'No post'; 104 | } 105 | 106 | $callback = static function (\WP_User $user) { // phpcs:ignore 107 | if (wp_get_current_user()->ID !== $user->ID) { 108 | return "No current user"; 109 | } 110 | 111 | if (!user_can($user, 'upload_files')) { 112 | return "Can't upload"; 113 | } 114 | 115 | if (filter_var($user->user_email, FILTER_VALIDATE_EMAIL) 116 | && filter_var($user->user_url, FILTER_VALIDATE_URL) 117 | ) { 118 | return 'It works!'; 119 | } 120 | 121 | return 'Bad email or URL'; 122 | }; 123 | 124 | $authorId = (int)$post->post_author; 125 | 126 | if ($authorId === get_current_user_id()) { 127 | $user = get_userdata($authorId); 128 | if (!$user || !$user->exists()) { 129 | return 'Bad user'; 130 | } 131 | 132 | if ($user->has_cap('edit_others_pages')) { 133 | return $callback($user); 134 | } 135 | 136 | return "Can't edit pages"; 137 | } 138 | 139 | return 'No user ID'; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /tests/cases/unit/GeneratorsTest.php: -------------------------------------------------------------------------------- 1 | wp()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tests/cases/unit/Provider/CommentTest.php: -------------------------------------------------------------------------------- 1 | factoryProvider(Provider\Comment::class); 22 | $comment = $factory(); 23 | 24 | static::assertInstanceOf(\WP_Comment::class, $comment); 25 | 26 | static::assertIsInt($comment->comment_ID); 27 | static::assertGreaterThanOrEqual(1, $comment->comment_ID); 28 | static::assertIsInt($comment->comment_post_ID); 29 | static::assertGreaterThanOrEqual(1, $comment->comment_post_ID); 30 | static::assertIsString($comment->comment_author); 31 | static::assertTrue((bool)filter_var($comment->comment_author_email, FILTER_VALIDATE_EMAIL)); 32 | static::assertIsString($comment->comment_author_url); 33 | static::assertIsString($comment->comment_author_url); 34 | static::assertIsString($comment->comment_author_IP); 35 | static::assertTrue((bool)filter_var($comment->comment_author_IP, FILTER_VALIDATE_IP)); 36 | static::assertInstanceOf(\DateTime::class, $this->dateByMySql($comment->comment_date)); 37 | static::assertInstanceOf(\DateTime::class, $this->dateByMySql($comment->comment_date_gmt)); 38 | static::assertIsString($comment->comment_content); 39 | static::assertSame(0, $comment->comment_karma); 40 | static::assertContains($comment->comment_approved, Provider\Comment::STATUSES); 41 | static::assertIsString($comment->comment_agent); 42 | static::assertIsString($comment->comment_type); 43 | static::assertIsInt($comment->comment_parent); 44 | static::assertGreaterThanOrEqual(0, $comment->comment_parent); 45 | static::assertIsInt($comment->user_id); 46 | static::assertGreaterThanOrEqual(0, $comment->user_id); 47 | 48 | $expectedArray = [ 49 | 'comment_ID' => $comment->comment_ID, 50 | 'comment_post_ID' => $comment->comment_post_ID, 51 | 'comment_author' => $comment->comment_author, 52 | 'comment_author_email' => $comment->comment_author_email, 53 | 'comment_author_url' => $comment->comment_author_url, 54 | 'comment_author_IP' => $comment->comment_author_IP, 55 | 'comment_date' => $comment->comment_date, 56 | 'comment_date_gmt' => $comment->comment_date_gmt, 57 | 'comment_content' => $comment->comment_content, 58 | 'comment_karma' => $comment->comment_karma, 59 | 'comment_approved' => $comment->comment_approved, 60 | 'comment_agent' => $comment->comment_agent, 61 | 'comment_type' => $comment->comment_type, 62 | 'comment_parent' => $comment->comment_parent, 63 | 'user_id' => $comment->user_id, 64 | ]; 65 | 66 | $actualArray = $comment->to_array(); 67 | 68 | ksort($expectedArray); 69 | ksort($actualArray); 70 | 71 | static::assertSame($expectedArray, $actualArray); 72 | } 73 | 74 | public function testCreateWithFixedId() 75 | { 76 | $factory = $this->factoryProvider(Provider\Comment::class); 77 | $comment = $factory(['id' => 123]); 78 | 79 | static::assertInstanceOf(\WP_Comment::class, $comment); 80 | static::assertSame(123, $comment->comment_ID); 81 | } 82 | 83 | public function testCreateWithFixedType() 84 | { 85 | $factory = $factory = $this->factoryProvider(Provider\Comment::class); 86 | $comment = $factory(['type' => 'pingback']); 87 | 88 | static::assertInstanceOf(\WP_Comment::class, $comment); 89 | static::assertSame('pingback', $comment->comment_type); 90 | } 91 | 92 | public function testIdUniqueness() 93 | { 94 | $factory = $this->factoryProvider(Provider\Comment::class); 95 | 96 | $ids = []; 97 | for ($i = 0; $i < 200; $i++) { 98 | $ids[] = $factory()->comment_ID; 99 | } 100 | 101 | static::assertSame($ids, array_unique($ids)); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /tests/cases/unit/Provider/ErrorTest.php: -------------------------------------------------------------------------------- 1 | factoryProvider(Provider\Error::class); 22 | $error = $factory(); 23 | 24 | static::assertInstanceOf(\WP_Error::class, $error); 25 | static::assertTrue(is_wp_error($error)); 26 | static::assertSame([], $error->errors); 27 | static::assertSame([], $error->error_data); 28 | static::assertFalse($error->has_errors()); 29 | static::assertSame('', $error->get_error_code()); 30 | static::assertSame([], $error->get_error_codes()); 31 | static::assertNull($error->get_error_data()); 32 | static::assertSame('', $error->get_error_message()); 33 | static::assertSame([], $error->get_error_messages()); 34 | } 35 | 36 | public function testWithError() 37 | { 38 | $factory = $this->factoryProvider(Provider\Error::class); 39 | $error = $factory(['error' => 'Ahi!']); 40 | 41 | $code = $error->get_error_code(); 42 | 43 | static::assertInstanceOf(\WP_Error::class, $error); 44 | 45 | static::assertIsString($code); 46 | static::assertSame('Ahi!', $error->get_error_message()); 47 | static::assertSame('Ahi!', $error->get_error_message($code)); 48 | static::assertSame(['Ahi!'], $error->get_error_messages()); 49 | static::assertSame(['Ahi!'], $error->get_error_messages($code)); 50 | static::assertTrue($error->has_errors()); 51 | } 52 | 53 | public function testWithErrorAndCode() 54 | { 55 | $factory = $this->factoryProvider(Provider\Error::class); 56 | $error = $factory(['error' => 'Ahi!', 'code' => 'foo']); 57 | 58 | static::assertInstanceOf(\WP_Error::class, $error); 59 | 60 | static::assertSame('foo', $error->get_error_code()); 61 | static::assertSame('Ahi!', $error->get_error_message()); 62 | static::assertSame('Ahi!', $error->get_error_message('foo')); 63 | static::assertSame(['Ahi!'], $error->get_error_messages()); 64 | static::assertSame(['Ahi!'], $error->get_error_messages('foo')); 65 | static::assertTrue($error->has_errors()); 66 | } 67 | 68 | public function testAddError() 69 | { 70 | $factory = $this->factoryProvider(Provider\Error::class); 71 | $error = $factory(); 72 | 73 | static::assertInstanceOf(\WP_Error::class, $error); 74 | 75 | static::assertFalse($error->has_errors()); 76 | 77 | $error->add('foo', 'One'); 78 | $error->add('foo', 'Two'); 79 | $error->add('bar', 'Three', 'some data'); 80 | 81 | static::assertSame('foo', $error->get_error_code()); 82 | static::assertSame(['foo', 'bar'], $error->get_error_codes()); 83 | static::assertSame('One', $error->get_error_message()); 84 | static::assertSame('One', $error->get_error_message('foo')); 85 | static::assertSame('Three', $error->get_error_message('bar')); 86 | static::assertSame(['One', 'Two', 'Three'], $error->get_error_messages()); 87 | static::assertSame(['One', 'Two'], $error->get_error_messages('foo')); 88 | static::assertSame(['Three'], $error->get_error_messages('bar')); 89 | static::assertNull($error->get_error_data('foo')); 90 | static::assertNull($error->get_error_data()); 91 | static::assertSame('some data', $error->get_error_data('bar')); 92 | 93 | static::assertTrue($error->has_errors()); 94 | } 95 | 96 | public function testAddErrorData() 97 | { 98 | /** @var Provider\Error $factory */ 99 | $factory = $this->factoryProvider(Provider\Error::class); 100 | $error = $factory(); 101 | 102 | $error->add_data('Data before'); 103 | 104 | static::assertSame('Data before', $error->get_error_data()); 105 | static::assertSame('Data before', $error->get_error_data('')); 106 | 107 | $error->add('x', 'One'); 108 | 109 | $error->add_data('Data after'); 110 | $error->add_data('Data foo', 'foo'); 111 | 112 | static::assertSame('Data after', $error->get_error_data()); 113 | static::assertSame('Data after', $error->get_error_data('')); 114 | static::assertSame('Data after', $error->get_error_data('x')); 115 | static::assertSame('Data foo', $error->get_error_data('foo')); 116 | } 117 | 118 | public function testRemove() 119 | { 120 | /** @var Provider\Error $factory */ 121 | $factory = $this->factoryProvider(Provider\Error::class); 122 | $error = $factory(); 123 | 124 | $error->add('code', 'msg', 'data'); 125 | 126 | static::assertSame('code', $error->get_error_code()); 127 | static::assertSame('msg', $error->get_error_message()); 128 | static::assertSame('data', $error->get_error_data()); 129 | 130 | $error->remove('code'); 131 | 132 | static::assertSame('', $error->get_error_code()); 133 | static::assertSame('', $error->get_error_message()); 134 | static::assertNull($error->get_error_data()); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /tests/cases/unit/Provider/PostTest.php: -------------------------------------------------------------------------------- 1 | factoryProvider(Provider\Post::class); 25 | $post = $factory(); 26 | 27 | static::assertInstanceOf(\WP_Post::class, $post); 28 | 29 | static::assertIsInt($post->ID); 30 | static::assertGreaterThanOrEqual(1, $post->ID); 31 | static::assertIsNumeric($post->post_author); 32 | static::assertGreaterThanOrEqual(1, (int)$post->post_author); 33 | static::assertInstanceOf(\DateTime::class, $this->dateByMySql($post->post_date)); 34 | static::assertInstanceOf(\DateTime::class, $this->dateByMySql($post->post_date_gmt)); 35 | static::assertIsString($post->post_content); 36 | static::assertIsString($post->post_title); 37 | static::assertIsString($post->post_excerpt); 38 | static::assertContains($post->post_status, ['publish', 'draft', 'future']); 39 | static::assertContains($post->comment_status, ['open', 'closed']); 40 | static::assertContains($post->ping_status, ['open', 'closed']); 41 | static::assertIsString($post->post_password); 42 | static::assertIsString($post->post_name); 43 | static::assertSame('', $post->to_ping); 44 | static::assertSame('', $post->pinged); 45 | static::assertInstanceOf(\DateTime::class, $this->dateByMySql($post->post_modified)); 46 | static::assertInstanceOf(\DateTime::class, $this->dateByMySql($post->post_modified_gmt)); 47 | static::assertIsString($post->post_content_filtered); 48 | static::assertSame($post->post_content_filtered, trim(strip_tags($post->post_content))); 49 | static::assertIsInt($post->post_parent); 50 | static::assertGreaterThanOrEqual(0, $post->post_parent); 51 | static::assertTrue((bool)filter_var($post->guid, FILTER_VALIDATE_URL)); 52 | static::assertIsInt($post->menu_order); 53 | static::assertGreaterThanOrEqual(0, $post->menu_order); 54 | static::assertContains($post->post_type, ['post', 'page']); 55 | static::assertSame('', $post->post_mime_type); 56 | static::assertSame('0', $post->comment_count); 57 | static::assertContains($post->filter, ['raw', null]); 58 | static::assertSame([], $post->ancestors); 59 | static::assertSame('', $post->page_template); 60 | static::assertSame([], $post->post_category); 61 | static::assertSame([], $post->tags_input); 62 | 63 | $expectedArray = [ 64 | 'ID' => $post->ID, 65 | 'post_author' => $post->post_author, 66 | 'post_date' => $post->post_date, 67 | 'post_date_gmt' => $post->post_date_gmt, 68 | 'post_content' => $post->post_content, 69 | 'post_title' => $post->post_title, 70 | 'post_excerpt' => $post->post_excerpt, 71 | 'post_status' => $post->post_status, 72 | 'comment_status' => $post->comment_status, 73 | 'ping_status' => $post->ping_status, 74 | 'post_password' => $post->post_password, 75 | 'post_name' => $post->post_name, 76 | 'to_ping' => $post->to_ping, 77 | 'pinged' => $post->pinged, 78 | 'post_modified' => $post->post_modified, 79 | 'post_modified_gmt' => $post->post_modified_gmt, 80 | 'post_content_filtered' => $post->post_content_filtered, 81 | 'post_parent' => $post->post_parent, 82 | 'guid' => $post->guid, 83 | 'menu_order' => $post->menu_order, 84 | 'post_type' => $post->post_type, 85 | 'post_mime_type' => $post->post_mime_type, 86 | 'comment_count' => $post->comment_count, 87 | 'filter' => $post->filter, 88 | 'ancestors' => $post->ancestors, 89 | 'page_template' => $post->page_template, 90 | 'post_category' => $post->post_category, 91 | 'tags_input' => $post->tags_input, 92 | ]; 93 | 94 | $actualArray = $post->to_array(); 95 | 96 | ksort($expectedArray); 97 | ksort($actualArray); 98 | 99 | static::assertSame($expectedArray, $actualArray); 100 | 101 | static::assertSame($post->to_array(), get_post($post->ID)->to_array()); 102 | static::assertSame($post->to_array(), get_post((string)$post->ID)->to_array()); 103 | static::assertSame($post->to_array(), get_post($post)->to_array()); 104 | 105 | foreach ($expectedArray as $key => $value) { 106 | static::assertSame($value ?? '', get_post_field($key, $post->ID)); 107 | static::assertSame($value ?? '', get_post_field($key, $post)); 108 | } 109 | } 110 | 111 | public function testCreateWithId() 112 | { 113 | $factory = $this->factoryProvider(Provider\Post::class); 114 | $post = $factory(['id' => 123]); 115 | 116 | static::assertSame(123, $post->ID); 117 | } 118 | 119 | public function testCreateWithAttachmentType() 120 | { 121 | $factory = $this->factoryProvider(Provider\Post::class); 122 | $post = $factory(['type' => 'attachment']); 123 | 124 | static::assertSame('attachment', $post->post_type); 125 | static::assertTrue(in_array($post->post_mime_type, Provider\Post::MIME_TYPES, true)); 126 | } 127 | 128 | public function testFuturePost() 129 | { 130 | $factory = $this->factoryProvider(Provider\Post::class); 131 | $post = $factory(['date' => '+1 year']); 132 | 133 | $gmt = new \DateTimeZone('GMT'); 134 | $postDateGmt = \DateTime::createFromFormat('Y-m-d H:i:s', $post->post_date_gmt, $gmt); 135 | 136 | $compare = (new \DateTime('now', $gmt))->modify('+1 year')->modify('-1 day'); 137 | 138 | static::assertSame('future', $post->post_status); 139 | static::assertGreaterThan($compare->getTimestamp(), $postDateGmt->getTimestamp()); 140 | } 141 | 142 | public function testWithFixedDateTime() 143 | { 144 | $factory = $this->factoryProvider(Provider\Post::class); 145 | 146 | $gmt = new \DateTimeZone('GMT'); 147 | $now = new \DateTime('now', $gmt); 148 | 149 | $post = $factory(['date_gmt' => $now]); 150 | 151 | $postDateGmt = \DateTime::createFromFormat('Y-m-d H:i:s', $post->post_date_gmt, $gmt); 152 | 153 | static::assertSame($now->getTimestamp(), $postDateGmt->getTimestamp()); 154 | } 155 | 156 | public function testTrashedPost() 157 | { 158 | $factory = $this->factoryProvider(Provider\Post::class); 159 | 160 | $post = $factory(['status' => 'trash']); 161 | 162 | static::assertSame('trash', $post->post_status); 163 | } 164 | 165 | public function testFunctionWithMultipleObjects() 166 | { 167 | /** @var Provider\Post $factory */ 168 | $factory = $this->factoryProvider(Provider\Post::class); 169 | 170 | $posts = []; 171 | for ($i = 0; $i < 100; $i++) { 172 | $posts[] = $factory(); 173 | } 174 | 175 | /** @var \WP_Post $post */ 176 | foreach ($posts as $post) { 177 | /** @var \WP_Post $compare */ 178 | $compare = get_post($post->ID); 179 | static::assertSame($post->to_array(), $compare->to_array()); 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /tests/cases/unit/Provider/PostTypeTest.php: -------------------------------------------------------------------------------- 1 | factoryProvider(Provider\PostType::class); 23 | $type = $factory(); 24 | 25 | static::assertInstanceOf(\WP_Post_Type::class, $type); 26 | 27 | static::assertArrayHasKey($type->name, Provider\PostType::BUILT_IN); 28 | static::assertIsString($type->label); 29 | static::assertTrue((bool)$type->label); 30 | static::assertInstanceOf(\stdClass::class, $type->labels); 31 | static::assertIsString($type->labels->name); 32 | static::assertIsString($type->description); 33 | static::assertIsBool($type->public); 34 | static::assertIsBool($type->hierarchical); 35 | static::assertIsBool($type->exclude_from_search); 36 | static::assertIsBool($type->publicly_queryable); 37 | static::assertIsBool($type->show_ui); 38 | static::assertIsBool($type->show_in_menu); 39 | static::assertIsBool($type->show_in_nav_menus); 40 | static::assertIsBool($type->show_in_admin_bar); 41 | static::assertTrue(is_int($type->menu_position) || is_null($type->menu_position)); 42 | static::assertNull($type->menu_icon); 43 | static::assertIsString($type->capability_type); 44 | static::assertIsBool($type->map_meta_cap); 45 | static::assertNull($type->register_meta_box_cb); 46 | static::assertIsArray($type->taxonomies); 47 | static::assertIsBool($type->has_archive); 48 | static::assertTrue(is_bool($type->query_var) || is_string($type->query_var)); 49 | static::assertIsBool($type->can_export); 50 | static::assertTrue(is_bool($type->delete_with_user) || is_null($type->delete_with_user)); 51 | static::assertIsString($type->_edit_link); 52 | static::assertInstanceOf(\stdClass::class, $type->cap); 53 | static::assertTrue(is_array($type->rewrite) || ($type->rewrite === false)); 54 | static::assertIsBool($type->show_in_rest); 55 | static::assertTrue(is_string($type->rest_base) || is_bool($type->rest_base)); 56 | static::assertTrue( 57 | is_string($type->rest_controller_class) 58 | || is_bool($type->rest_controller_class) 59 | ); 60 | 61 | static::assertTrue($type->_builtin); 62 | 63 | static::assertInstanceOf(\WP_Post_Type::class, get_post_type_object($type->name)); 64 | static::assertSame($type->name, get_post_type_object($type->name)->name); 65 | static::assertEquals($type->labels, get_post_type_object($type->name)->labels); 66 | static::assertEquals($type->cap, get_post_type_object($type->name)->cap); 67 | 68 | static::assertTrue(post_type_exists($type->name)); 69 | } 70 | 71 | public function testWithBuiltInType() 72 | { 73 | $factory = $this->factoryProvider(Provider\PostType::class); 74 | $type = $factory(['name' => 'page']); 75 | 76 | static::assertSame('Pages', $type->label); 77 | static::assertSame('Page', $type->labels->singular_name); 78 | static::assertTrue($type->hierarchical); 79 | static::assertTrue($type->_builtin); 80 | } 81 | 82 | public function testWithNameAndHierarchical() 83 | { 84 | $factory = $this->factoryProvider(Provider\PostType::class); 85 | $type = $factory(['name' => 'foo', 'hierarchical' => true, 'labels' => ['name' => 'Foo']]); 86 | 87 | static::assertSame('Foo', $type->label); 88 | static::assertSame('Foo', $type->labels->name); 89 | static::assertSame('Page', $type->labels->singular_name); 90 | static::assertTrue($type->hierarchical); 91 | static::assertFalse($type->_builtin); 92 | } 93 | 94 | public function testWithNameAndLabel() 95 | { 96 | $factory = $this->factoryProvider(Provider\PostType::class); 97 | $type = $factory(['name' => 'bar', 'label' => 'Bar']); 98 | 99 | static::assertSame('Bar', $type->label); 100 | static::assertSame('Bar', $type->labels->name); 101 | static::assertSame('Post', $type->labels->singular_name); 102 | static::assertFalse($type->hierarchical); 103 | static::assertFalse($type->_builtin); 104 | } 105 | 106 | public function testWithNameAndLabelAndCap() 107 | { 108 | $factory = $this->factoryProvider(Provider\PostType::class); 109 | $type = $factory( 110 | [ 111 | 'name' => 'product', 112 | 'label' => 'Product', 113 | 'cap' => ['edit_post' => 'edit_product'], 114 | ] 115 | ); 116 | 117 | static::assertSame('Product', $type->label); 118 | static::assertSame('Product', $type->labels->name); 119 | static::assertSame('Post', $type->labels->singular_name); 120 | static::assertSame('edit_product', $type->cap->edit_post); 121 | static::assertSame('publish_posts', $type->cap->publish_posts); 122 | static::assertSame('edit_posts', $type->cap->create_posts); 123 | static::assertFalse($type->hierarchical); 124 | static::assertFalse($type->_builtin); 125 | } 126 | 127 | public function testWithPublic() 128 | { 129 | $factory = $this->factoryProvider(Provider\PostType::class); 130 | $type = $factory(['name' => 'product', 'label' => 'Product', 'public' => true]); 131 | 132 | static::assertSame('Product', $type->label); 133 | static::assertSame('Product', $type->labels->name); 134 | static::assertFalse($type->exclude_from_search); 135 | static::assertTrue($type->show_in_admin_bar); 136 | static::assertTrue($type->show_in_menu); 137 | static::assertTrue($type->show_in_nav_menus); 138 | static::assertTrue($type->show_in_rest); 139 | static::assertTrue($type->show_ui); 140 | static::assertFalse($type->_builtin); 141 | } 142 | 143 | public function testFunctionWithMultipleObjects() 144 | { 145 | $factory = $this->factoryProvider(Provider\PostType::class); 146 | 147 | $types = []; 148 | for ($i = 0; $i < 100; $i++) { 149 | $types[] = $factory(); 150 | } 151 | 152 | /** @var \WP_Post_Type $type */ 153 | foreach ($types as $type) { 154 | /** @var \WP_Post_Type $compare */ 155 | $compare = get_post_type_object($type->name); 156 | static::assertInstanceOf(\WP_Post_Type::class, $compare); 157 | static::assertSame($type->name, $compare->name); 158 | static::assertSame($type->label, $compare->label); 159 | static::assertSame($type->query_var, $compare->query_var); 160 | static::assertEquals($type->labels, $compare->labels); 161 | static::assertEquals($type->cap, $compare->cap); 162 | } 163 | } 164 | 165 | public function testUnicityUpToPossible() 166 | { 167 | $factory = $this->factoryProvider(Provider\PostType::class); 168 | 169 | $names = []; 170 | for ($i = 0; $i < count(Provider\PostType::BUILT_IN); $i++) { 171 | $names[] = $factory()->name; 172 | } 173 | 174 | static::assertSame($names, array_unique($names)); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /tests/cases/unit/Provider/SiteTest.php: -------------------------------------------------------------------------------- 1 | factoryProvider(Provider\Site::class); 27 | $site = $factory(); 28 | 29 | static::assertInstanceOf(\WP_Site::class, $site); 30 | 31 | static::assertIsString($site->blog_id); 32 | static::assertIsNumeric($site->blog_id); 33 | static::assertGreaterThanOrEqual(1, (int)$site->blog_id); 34 | static::assertIsString($site->domain); 35 | static::assertIsString($site->path); 36 | static::assertIsString($site->site_id); 37 | static::assertIsNumeric($site->site_id); 38 | static::assertGreaterThanOrEqual(1, (int)$site->site_id); 39 | static::assertInstanceOf(\DateTime::class, $this->dateByMySql($site->registered)); 40 | static::assertInstanceOf(\DateTime::class, $this->dateByMySql($site->last_updated)); 41 | static::assertIsString($site->public); 42 | static::assertIsNumeric($site->public); 43 | static::assertContains($site->public, ['0', '1']); 44 | static::assertIsString($site->archived); 45 | static::assertIsNumeric($site->archived); 46 | static::assertContains($site->archived, ['0', '1']); 47 | static::assertIsString($site->mature); 48 | static::assertIsNumeric($site->mature); 49 | static::assertContains($site->mature, ['0', '1']); 50 | static::assertIsString($site->spam); 51 | static::assertIsNumeric($site->spam); 52 | static::assertContains($site->spam, ['0', '1']); 53 | static::assertIsString($site->deleted); 54 | static::assertIsNumeric($site->deleted); 55 | static::assertContains($site->deleted, ['0', '1']); 56 | static::assertIsString($site->lang_id); 57 | static::assertIsNumeric($site->lang_id); 58 | static::assertContains($site->lang_id, ['0', '1']); 59 | 60 | $expectedArray = [ 61 | 'blog_id' => $site->blog_id, 62 | 'domain' => $site->domain, 63 | 'path' => $site->path, 64 | 'site_id' => $site->site_id, 65 | 'registered' => $site->registered, 66 | 'last_updated' => $site->last_updated, 67 | 'public' => $site->public, 68 | 'archived' => $site->archived, 69 | 'mature' => $site->mature, 70 | 'spam' => $site->spam, 71 | 'deleted' => $site->deleted, 72 | 'lang_id' => $site->lang_id, 73 | ]; 74 | 75 | $actualArray = $site->to_array(); 76 | 77 | ksort($expectedArray); 78 | ksort($actualArray); 79 | 80 | static::assertSame($expectedArray, $actualArray); 81 | static::assertSame($site->blog_id, $site->id); 82 | static::assertSame($site->site_id, $site->network_id); 83 | static::assertIsString($site->blogname); 84 | static::assertTrue((bool)filter_var($site->siteurl, FILTER_VALIDATE_URL)); 85 | static::assertTrue((bool)filter_var($site->home, FILTER_VALIDATE_URL)); 86 | static::assertIsInt($site->post_count); 87 | static::assertGreaterThan(0, $site->post_count); 88 | 89 | static::assertInstanceOf(\WP_Site::class, get_site($site->blog_id)); 90 | static::assertInstanceOf(\WP_Site::class, get_site((int)$site->blog_id)); 91 | static::assertSame($site->to_array(), get_site($site->blog_id)->to_array()); 92 | static::assertSame($site->to_array(), get_site((int)$site->blog_id)->to_array()); 93 | } 94 | 95 | public function testWithGivenId() 96 | { 97 | /** @var Provider\Site $factory */ 98 | $factory = $this->factoryProvider(Provider\Site::class); 99 | $site = $factory(['id' => 123]); 100 | 101 | static::assertSame('123', $site->blog_id); 102 | static::assertSame('123', $site->id); 103 | } 104 | 105 | public function testWithGivenUrl() 106 | { 107 | /** @var Provider\Site $factory */ 108 | $factory = $this->factoryProvider(Provider\Site::class); 109 | $site = $factory(['url' => 'example.com/blog/']); 110 | 111 | static::assertSame('http://example.com/blog/', $site->home); 112 | static::assertSame('http://example.com/blog/', $site->siteurl); 113 | static::assertSame('/blog', $site->path); 114 | } 115 | 116 | public function testFunctionsWithMultipleObjects() 117 | { 118 | /** @var Provider\Site $factory */ 119 | $factory = $this->factoryProvider(Provider\Site::class); 120 | 121 | $sites = []; 122 | for ($i = 0; $i < 100; $i++) { 123 | $sites[] = $factory(); 124 | } 125 | 126 | /** @var \WP_Site $site */ 127 | foreach ($sites as $site) { 128 | static::assertInstanceOf(\WP_Site::class, get_site($site->blog_id)); 129 | static::assertSame($site->to_array(), get_site($site->blog_id)->to_array()); 130 | } 131 | } 132 | 133 | /** 134 | * @runInSeparateProcess 135 | */ 136 | public function testDoesNotCreateOnSingleSite() 137 | { 138 | Monkey\Functions\when('is_multisite')->justReturn(false); 139 | 140 | $this->expectExceptionMessageRegExp('/multisite/'); 141 | $this->factoryProvider(Provider\Site::class)(); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /tests/cases/unit/Provider/TaxonomyTest.php: -------------------------------------------------------------------------------- 1 | factoryProvider(Provider\Taxonomy::class); 23 | $taxonomy = $factory(); 24 | 25 | static::assertInstanceOf(\WP_Taxonomy::class, $taxonomy); 26 | 27 | static::assertArrayHasKey($taxonomy->name, Provider\Taxonomy::BUILT_IN); 28 | static::assertIsString($taxonomy->label); 29 | static::assertInstanceOf(\stdClass::class, $taxonomy->labels); 30 | static::assertIsString($taxonomy->labels->name); 31 | static::assertIsString($taxonomy->description); 32 | static::assertIsBool($taxonomy->public); 33 | static::assertIsBool($taxonomy->publicly_queryable); 34 | static::assertIsBool($taxonomy->hierarchical); 35 | static::assertIsBool($taxonomy->show_ui); 36 | static::assertIsBool($taxonomy->show_in_menu); 37 | static::assertIsBool($taxonomy->show_in_nav_menus); 38 | static::assertIsBool($taxonomy->show_tagcloud); 39 | static::assertIsBool($taxonomy->show_in_quick_edit); 40 | static::assertIsBool($taxonomy->show_in_quick_edit); 41 | static::assertIsBool($taxonomy->show_admin_column); 42 | static::assertIsBool($taxonomy->show_in_rest); 43 | static::assertIsArray($taxonomy->object_type); 44 | static::assertInstanceOf(\stdClass::class, $taxonomy->cap); 45 | static::assertIsString($taxonomy->cap->manage_terms); 46 | static::assertTrue(is_array($taxonomy->rewrite) || $taxonomy->rewrite === false); 47 | static::assertTrue(is_string($taxonomy->query_var) || $taxonomy->query_var === false); 48 | static::assertTrue(is_string($taxonomy->rest_base) || is_bool($taxonomy->rest_base)); 49 | 50 | $controller = $taxonomy->rest_controller_class; 51 | static::assertTrue(is_string($controller) || is_bool($controller)); 52 | 53 | // phpcs:disable Inpsyde.CodeQuality.VariablesName.SnakeCaseVar 54 | $boxCb = $taxonomy->meta_box_cb; 55 | $cb_esc = $taxonomy->meta_box_sanitize_cb; 56 | $cb_upd = $taxonomy->update_count_callback; 57 | static::assertTrue(is_null($boxCb) || is_bool($boxCb) || is_callable($boxCb, true)); 58 | static::assertTrue(is_null($cb_esc) || is_callable($cb_esc, true)); 59 | static::assertTrue(is_null($cb_upd) || is_callable($cb_upd, true)); 60 | // phpcs:enable Inpsyde.CodeQuality.VariablesName.SnakeCaseVar 61 | 62 | static::assertTrue($taxonomy->_builtin); 63 | static::assertInstanceOf(\WP_Taxonomy::class, get_taxonomy($taxonomy->name)); 64 | static::assertSame($taxonomy->name, get_taxonomy($taxonomy->name)->name); 65 | static::assertEquals($taxonomy->labels, get_taxonomy($taxonomy->name)->labels); 66 | static::assertEquals($taxonomy->cap, get_taxonomy($taxonomy->name)->cap); 67 | static::assertSame($taxonomy->rewrite, get_taxonomy($taxonomy->name)->rewrite); 68 | static::assertTrue(taxonomy_exists($taxonomy->name)); 69 | } 70 | 71 | public function testWithBuiltInType() 72 | { 73 | $factory = $this->factoryProvider(Provider\Taxonomy::class); 74 | $type = $factory(['name' => 'category']); 75 | 76 | static::assertSame('Categories', $type->label); 77 | static::assertSame('Category', $type->labels->singular_name); 78 | static::assertTrue($type->hierarchical); 79 | static::assertTrue($type->_builtin); 80 | } 81 | 82 | public function testWithNameAndHierarchical() 83 | { 84 | $factory = $this->factoryProvider(Provider\Taxonomy::class); 85 | $tax = $factory(['name' => 'foo', 'hierarchical' => true, 'labels' => ['name' => 'Foo']]); 86 | 87 | static::assertSame('Foo', $tax->label); 88 | static::assertSame('Foo', $tax->labels->name); 89 | static::assertSame('Category', $tax->labels->singular_name); 90 | static::assertTrue($tax->hierarchical); 91 | static::assertFalse($tax->_builtin); 92 | } 93 | 94 | public function testWithNameAndLabel() 95 | { 96 | $factory = $this->factoryProvider(Provider\Taxonomy::class); 97 | $type = $factory(['name' => 'bar', 'label' => 'Bar']); 98 | 99 | static::assertSame('Bar', $type->label); 100 | static::assertSame('Bar', $type->labels->name); 101 | static::assertSame('Tag', $type->labels->singular_name); 102 | static::assertFalse($type->hierarchical); 103 | static::assertFalse($type->_builtin); 104 | } 105 | 106 | public function testWithNameAndLabelAndCap() 107 | { 108 | $factory = $this->factoryProvider(Provider\Taxonomy::class); 109 | $type = $factory( 110 | [ 111 | 'name' => 'product', 112 | 'label' => 'Product', 113 | 'cap' => ['manage_terms' => 'manage_products'], 114 | ] 115 | ); 116 | 117 | static::assertSame('Product', $type->label); 118 | static::assertSame('Product', $type->labels->name); 119 | static::assertSame('Tag', $type->labels->singular_name); 120 | static::assertSame('manage_products', $type->cap->manage_terms); 121 | static::assertSame('assign_post_tags', $type->cap->assign_terms); 122 | static::assertFalse($type->hierarchical); 123 | static::assertFalse($type->_builtin); 124 | } 125 | 126 | public function testWithPublic() 127 | { 128 | /** @var Provider\Taxonomy $factory */ 129 | $factory = $this->factoryProvider(Provider\Taxonomy::class); 130 | $tax = $factory(['name' => 'product', 'label' => 'Product', 'public' => true]); 131 | 132 | static::assertSame('Product', $tax->label); 133 | static::assertSame('Product', $tax->labels->name); 134 | static::assertTrue($tax->public); 135 | static::assertTrue($tax->publicly_queryable); 136 | static::assertTrue($tax->show_in_menu); 137 | static::assertTrue($tax->show_in_nav_menus); 138 | static::assertTrue($tax->show_in_rest); 139 | static::assertTrue($tax->show_ui); 140 | static::assertFalse($tax->_builtin); 141 | } 142 | 143 | public function testFunctionWithMultipleObjects() 144 | { 145 | /** @var Provider\Taxonomy $factory */ 146 | $factory = $this->factoryProvider(Provider\Taxonomy::class); 147 | 148 | $taxonomies = []; 149 | for ($i = 0; $i < 50; $i++) { 150 | $taxonomies[] = $factory(); 151 | } 152 | 153 | /** @var \WP_Taxonomy $taxonomy */ 154 | foreach ($taxonomies as $taxonomy) { 155 | /** @var \WP_Taxonomy $compare */ 156 | $compare = get_taxonomy($taxonomy->name); 157 | static::assertInstanceOf(\WP_Taxonomy::class, $compare); 158 | static::assertSame($taxonomy->name, $compare->name); 159 | static::assertSame($taxonomy->label, $compare->label); 160 | static::assertSame($taxonomy->rewrite, $compare->rewrite); 161 | static::assertSame($taxonomy->description, $compare->description); 162 | static::assertEquals($taxonomy->labels, $compare->labels); 163 | static::assertEquals($taxonomy->cap, $compare->cap); 164 | } 165 | } 166 | 167 | public function testUnicityUpToPossible() 168 | { 169 | /** @var Provider\Taxonomy $factory */ 170 | $factory = $this->factoryProvider(Provider\Taxonomy::class); 171 | 172 | $names = []; 173 | for ($i = 0; $i < count(Provider\Taxonomy::BUILT_IN); $i++) { 174 | $names[] = $factory()->name; 175 | } 176 | 177 | static::assertSame($names, array_unique($names)); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /tests/cases/unit/Provider/TermTest.php: -------------------------------------------------------------------------------- 1 | factoryProvider(Provider\Term::class); 26 | $term = $factory(); 27 | 28 | static::assertInstanceOf(\WP_Term::class, $term); 29 | 30 | static::assertIsInt($term->term_id); 31 | static::assertGreaterThanOrEqual(1, $term->term_id); 32 | static::assertIsInt($term->term_taxonomy_id); 33 | static::assertGreaterThanOrEqual(1, $term->term_taxonomy_id); 34 | static::assertIsString($term->name); 35 | static::assertIsString($term->slug); 36 | static::assertSame('', $term->term_group); 37 | static::assertArrayHasKey($term->taxonomy, Provider\Taxonomy::BUILT_IN); 38 | static::assertIsString($term->description); 39 | static::assertIsInt($term->parent); 40 | static::assertGreaterThanOrEqual(0, $term->parent); 41 | static::assertIsInt($term->count); 42 | static::assertGreaterThanOrEqual(0, $term->count); 43 | static::assertContains($term->filter, ['raw', null]); 44 | static::assertInstanceOf(\stdClass::class, $term->data); 45 | static::assertSame($term->term_id, $term->data->term_id); 46 | 47 | $expectedArray = [ 48 | 'term_id' => $term->term_id, 49 | 'term_taxonomy_id' => $term->term_taxonomy_id, 50 | 'name' => $term->name, 51 | 'slug' => $term->slug, 52 | 'term_group' => $term->term_group, 53 | 'taxonomy' => $term->taxonomy, 54 | 'description' => $term->description, 55 | 'parent' => $term->parent, 56 | 'count' => $term->count, 57 | 'filter' => $term->filter, 58 | ]; 59 | 60 | $actualArray = $term->to_array(); 61 | $arr = $actualArray; 62 | 63 | ksort($expectedArray); 64 | ksort($actualArray); 65 | 66 | static::assertSame($expectedArray, $actualArray); 67 | 68 | $id = $term->term_id; 69 | $ttId = $term->term_taxonomy_id; 70 | $tax = $term->taxonomy; 71 | 72 | static::assertSame($arr, get_term($id, $tax)->to_array()); 73 | static::assertSame($arr, get_term((string)$id, $tax)->to_array()); 74 | static::assertSame($arr, get_term_by('slug', $term->slug, $tax)->to_array()); 75 | static::assertSame($arr, get_term_by('slug', $term->name, $tax)->to_array()); 76 | static::assertSame($arr, get_term_by('name', $term->name, $tax)->to_array()); 77 | static::assertSame($arr, get_term_by('id', $id, $tax)->to_array()); 78 | static::assertSame($arr, get_term_by('term_taxonomy_id', $ttId, $tax)->to_array()); 79 | static::assertSame($arr, get_term_by('term_taxonomy_id', (string)$ttId, $tax)->to_array()); 80 | static::assertSame($arr, get_term_by('term_taxonomy_id', $ttId)->to_array()); 81 | static::assertSame($arr, get_term_by('term_taxonomy_id', (string)$ttId)->to_array()); 82 | static::assertFalse(get_term_by('slug', $term->slug)); 83 | static::assertFalse(get_term_by('name', $term->name)); 84 | static::assertFalse(get_term_by('name', $term->slug, $tax)); 85 | } 86 | 87 | public function testCreateWithFixedId() 88 | { 89 | /** @var Provider\Term $factory */ 90 | $factory = $this->factoryProvider(Provider\Term::class); 91 | $term = $factory(['id' => 123]); 92 | 93 | static::assertInstanceOf(\WP_Term::class, $term); 94 | static::assertSame(123, $term->term_id); 95 | static::assertSame(123, $term->term_taxonomy_id); 96 | } 97 | 98 | public function testCreateWithFixedIdAndTermTaxId() 99 | { 100 | /** @var Provider\Term $factory */ 101 | $factory = $this->factoryProvider(Provider\Term::class); 102 | $term = $factory(['id' => 123, 'tt_id' => 456]); 103 | 104 | static::assertInstanceOf(\WP_Term::class, $term); 105 | static::assertSame(123, $term->term_id); 106 | static::assertSame(456, $term->term_taxonomy_id); 107 | } 108 | 109 | public function testCreateWithFixedTaxonomy() 110 | { 111 | /** @var Provider\Term $factory */ 112 | $factory = $this->factoryProvider(Provider\Term::class); 113 | $term = $factory(['taxonomy' => 'category']); 114 | 115 | static::assertSame('category', $term->taxonomy); 116 | } 117 | 118 | public function testIdUniqueness() 119 | { 120 | /** @var Provider\Term $factory */ 121 | $factory = $this->factoryProvider(Provider\Term::class); 122 | 123 | $ids = []; 124 | for ($i = 0; $i < 200; $i++) { 125 | $ids[] = $factory()->term_id; 126 | } 127 | 128 | static::assertSame($ids, array_unique($ids)); 129 | } 130 | 131 | public function testFunctionsForManyTerms() 132 | { 133 | /** @var Provider\Term $factory */ 134 | $factory = $this->factoryProvider(Provider\Term::class); 135 | 136 | $terms = []; 137 | for ($i = 0; $i < 1000; $i++) { 138 | $terms[] = $factory(); 139 | } 140 | 141 | $ids = []; 142 | /** @var \WP_Term $term */ 143 | foreach ($terms as $term) { 144 | $ids[] = $term->term_id; 145 | $arr = $term->to_array(); 146 | $tax = $term->taxonomy; 147 | $ttId = $term->term_taxonomy_id; 148 | 149 | static::assertSame($arr, get_term($term->term_id, $tax)->to_array()); 150 | static::assertSame($arr, get_term_by('slug', $term->slug, $tax)->to_array()); 151 | static::assertSame($arr, get_term_by('name', $term->name, $tax)->to_array()); 152 | static::assertSame($arr, get_term_by('id', $term->term_id, $tax)->to_array()); 153 | static::assertSame($arr, get_term_by('term_taxonomy_id', $ttId, $tax)->to_array()); 154 | static::assertSame($arr, get_term_by('term_taxonomy_id', $ttId)->to_array()); 155 | } 156 | 157 | $badId = (int)(max($ids) + 1); 158 | static::assertNull(get_term($badId)); 159 | static::assertFalse(get_term_by('id', $badId)); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /tests/cases/unit/Provider/UserTest.php: -------------------------------------------------------------------------------- 1 | factoryProvider(Provider\User::class); 27 | $user = $factory(); 28 | 29 | static::assertInstanceOf(\WP_User::class, $user); 30 | 31 | static::assertIsInt($user->ID); 32 | static::assertGreaterThan(1, $user->ID); 33 | static::assertInstanceOf(\stdClass::class, $user->data); 34 | static::assertSame($user->ID, $user->data->ID); 35 | static::assertIsArray($user->caps); 36 | static::assertIsString($user->cap_key); 37 | static::assertIsArray($user->roles); 38 | static::assertIsArray($user->allcaps); 39 | static::assertContains($user->filter, ['raw', null]); 40 | 41 | static::assertIsString($user->user_login); 42 | static::assertIsString($user->user_pass); 43 | static::assertIsString($user->user_nicename); 44 | static::assertTrue((bool)filter_var($user->user_email, FILTER_VALIDATE_EMAIL)); 45 | static::assertTrue((bool)filter_var($user->user_url, FILTER_VALIDATE_URL)); 46 | static::assertInstanceOf(\DateTime::class, $this->dateByMySql($user->user_registered)); 47 | static::assertIsString($user->user_activation_key); 48 | static::assertIsString($user->user_status); 49 | static::assertIsNumeric($user->user_status); 50 | static::assertIsString($user->display_name); 51 | 52 | static::assertIsString($user->user_description); 53 | static::assertSame($user->user_description, $user->description); 54 | static::assertIsString($user->user_firstname); 55 | static::assertSame($user->user_firstname, $user->first_name); 56 | static::assertIsString($user->user_lastname); 57 | static::assertSame($user->user_lastname, $user->last_name); 58 | static::assertIsInt($user->user_level); 59 | static::assertIsString($user->nickname); 60 | static::assertIsString($user->spam); 61 | static::assertIsString($user->deleted); 62 | static::assertIsString($user->locale); 63 | static::assertContains($user->rich_editing, ['true', 'false']); 64 | static::assertContains($user->syntax_highlighting, ['true', 'false']); 65 | 66 | $expectedArray = [ 67 | 'ID' => $user->ID, 68 | 'user_login' => $user->user_login, 69 | 'user_pass' => $user->user_pass, 70 | 'user_nicename' => $user->user_nicename, 71 | 'user_email' => $user->user_email, 72 | 'user_url' => $user->user_url, 73 | 'user_registered' => $user->user_registered, 74 | 'user_activation_key' => $user->user_activation_key, 75 | 'user_status' => $user->user_status, 76 | 'display_name' => $user->display_name, 77 | ]; 78 | 79 | $actualArray = $user->to_array(); 80 | 81 | ksort($expectedArray); 82 | ksort($actualArray); 83 | 84 | static::assertSame($expectedArray, $actualArray); 85 | 86 | static::assertIsInt($user->get_site_id()); 87 | static::assertGreaterThanOrEqual(1, $user->get_site_id()); 88 | } 89 | 90 | public function testGet() 91 | { 92 | $keys = [ 93 | 'ID', 94 | 'user_login', 95 | 'user_pass', 96 | 'user_nicename', 97 | 'user_email', 98 | 'user_url', 99 | 'user_registered', 100 | 'user_activation_key', 101 | 'user_status', 102 | 'user_description', 103 | 'description', 104 | 'user_firstname', 105 | 'first_name', 106 | 'user_lastname', 107 | 'last_name', 108 | 'user_level', 109 | 'nickname', 110 | 'spam', 111 | 'deleted', 112 | 'locale', 113 | 'rich_editing', 114 | 'syntax_highlighting', 115 | ]; 116 | 117 | /** @var Provider\User $factory */ 118 | $factory = $this->factoryProvider(Provider\User::class); 119 | $user = $factory(); 120 | 121 | foreach ($keys as $key) { 122 | $value = $user->get($key); 123 | static::assertSame((string)$user->{$key}, $value); 124 | } 125 | } 126 | 127 | public function testFunctions() 128 | { 129 | /** @var Provider\User $factory */ 130 | $factory = $this->factoryProvider(Provider\User::class); 131 | $user = $factory(); 132 | 133 | $this->compareUser($user, get_userdata($user->ID)); 134 | $this->compareUser($user, get_user_by('ID', $user->ID)); 135 | $this->compareUser($user, get_user_by('id', $user->ID)); 136 | $this->compareUser($user, get_user_by('ID', (string)$user->ID)); 137 | $this->compareUser($user, get_user_by('id', (string)$user->ID)); 138 | $this->compareUser($user, get_user_by('slug', $user->user_nicename)); 139 | $this->compareUser($user, get_user_by('email', $user->user_email)); 140 | $this->compareUser($user, get_user_by('login', $user->user_login)); 141 | 142 | static::assertFalse(get_userdata($user->ID + 1)); 143 | static::assertFalse(get_user_by('id', $user->ID + 1)); 144 | static::assertFalse(get_user_by('slug', 'x' . $user->user_nicename)); 145 | static::assertFalse(get_user_by('email', $user->user_email . 'x')); 146 | static::assertFalse(get_user_by('login', 'x' . $user->user_login)); 147 | static::assertFalse(get_user_by('login ', $user->user_login)); 148 | } 149 | 150 | public function testFunctionsForManyUsers() 151 | { 152 | /** @var Provider\User $factory */ 153 | $factory = $this->factoryProvider(Provider\User::class); 154 | 155 | $users = []; 156 | for ($i = 0; $i < 1000; $i++) { 157 | $users[] = $factory(); 158 | } 159 | 160 | $ids = []; 161 | /** @var \WP_User $user */ 162 | foreach ($users as $user) { 163 | $ids[] = $user->ID; 164 | $this->compareUser($user, get_userdata($user->ID)); 165 | $this->compareUser($user, get_user_by('slug', $user->user_nicename)); 166 | } 167 | 168 | $badId = (int)(max($ids) + 1); 169 | static::assertFalse(get_userdata($badId)); 170 | } 171 | 172 | public function testFixedData() 173 | { 174 | /** @var Provider\User $factory */ 175 | $factory = $this->factoryProvider(Provider\User::class); 176 | $user = $factory( 177 | [ 178 | 'id' => 123, 179 | 'login' => 'itsme', 180 | 'email' => 'me@example.com', 181 | 'password' => 's3cr3t', 182 | ] 183 | ); 184 | 185 | static::assertSame(123, $user->ID); 186 | static::assertSame('itsme', $user->user_login); 187 | static::assertSame('me@example.com', $user->user_email); 188 | static::assertSame('s3cr3t', $user->user_pass); 189 | } 190 | 191 | public function testFixedRole() 192 | { 193 | /** @var Provider\User $factory */ 194 | $factory = $this->factoryProvider(Provider\User::class); 195 | $user = $factory(['role' => 'editor']); 196 | 197 | static::assertSame(['editor'], $user->roles); 198 | static::assertTrue($user->caps['editor']); 199 | static::assertSame(7, $user->user_level); 200 | static::assertTrue($user->allcaps['level_7']); 201 | static::assertTrue($user->allcaps['level_6']); 202 | static::assertTrue($user->allcaps['level_5']); 203 | static::assertTrue($user->allcaps['level_4']); 204 | static::assertTrue($user->allcaps['level_3']); 205 | static::assertTrue($user->allcaps['level_2']); 206 | static::assertTrue($user->allcaps['level_1']); 207 | static::assertTrue($user->allcaps['level_0']); 208 | static::assertTrue($user->allcaps['manage_categories']); 209 | 210 | static::assertTrue($user->has_cap('editor')); 211 | static::assertTrue($user->has_cap('level_7')); 212 | static::assertTrue($user->has_cap('level_6')); 213 | static::assertTrue($user->has_cap('level_5')); 214 | static::assertTrue($user->has_cap('level_4')); 215 | static::assertTrue($user->has_cap('level_3')); 216 | static::assertTrue($user->has_cap('level_2')); 217 | static::assertTrue($user->has_cap('level_1')); 218 | static::assertTrue($user->has_cap('level_0')); 219 | static::assertTrue($user->has_cap('manage_categories')); 220 | static::assertFalse($user->has_cap('level_8')); 221 | static::assertFalse($user->has_cap('level_9')); 222 | static::assertFalse($user->has_cap('level_10')); 223 | static::assertFalse($user->has_cap('administrator')); 224 | static::assertFalse($user->has_cap('switch_themes')); 225 | 226 | static::assertTrue(user_can($user, 'manage_categories')); 227 | static::assertTrue(user_can($user->ID, 'editor')); 228 | static::assertFalse(user_can($user, 'administrator')); 229 | static::assertFalse(user_can($user->ID, 'switch_themes')); 230 | } 231 | 232 | public function testRoleByLevel() 233 | { 234 | /** @var Provider\User $factory */ 235 | $factory = $this->factoryProvider(Provider\User::class); 236 | $user = $factory(['level' => 5]); 237 | 238 | static::assertSame(['author'], $user->roles); 239 | static::assertTrue($user->caps['author']); 240 | static::assertSame(5, $user->user_level); 241 | static::assertTrue($user->allcaps['level_5']); 242 | static::assertTrue($user->allcaps['level_4']); 243 | static::assertTrue($user->allcaps['level_3']); 244 | static::assertTrue($user->allcaps['level_2']); 245 | static::assertTrue($user->allcaps['level_1']); 246 | static::assertTrue($user->allcaps['level_0']); 247 | static::assertTrue($user->allcaps['upload_files']); 248 | 249 | static::assertTrue($user->has_cap('author')); 250 | static::assertTrue($user->has_cap('level_5')); 251 | static::assertTrue($user->has_cap('level_4')); 252 | static::assertTrue($user->has_cap('level_3')); 253 | static::assertTrue($user->has_cap('level_2')); 254 | static::assertTrue($user->has_cap('level_1')); 255 | static::assertTrue($user->has_cap('level_0')); 256 | static::assertTrue($user->has_cap('upload_files')); 257 | static::assertFalse($user->has_cap('level_6')); 258 | static::assertFalse($user->has_cap('level_7')); 259 | static::assertFalse($user->has_cap('level_8')); 260 | static::assertFalse($user->has_cap('level_9')); 261 | static::assertFalse($user->has_cap('level_10')); 262 | static::assertFalse($user->has_cap('editor')); 263 | static::assertFalse($user->has_cap('manage_categories')); 264 | 265 | static::assertTrue(user_can($user, 'upload_files')); 266 | static::assertTrue(user_can($user->ID, 'author')); 267 | static::assertTrue(user_can((string)$user->ID, 'level_5')); 268 | static::assertFalse(user_can($user, 'editor')); 269 | static::assertFalse(user_can($user->ID, 'manage_categories')); 270 | } 271 | 272 | public function testGetFailWithLowercaseId() 273 | { 274 | /** @var Provider\User $factory */ 275 | $factory = $this->factoryProvider(Provider\User::class); 276 | $user = $factory(); 277 | 278 | $this->expectExceptionMessageRegExp('/WP_User::ID/'); 279 | 280 | $user->get('id'); 281 | } 282 | 283 | public function testMakeUserCurrent() 284 | { 285 | Monkey\Functions\expect('wp_safe_redirect')->once(); 286 | 287 | /** @var Provider\User $factory */ 288 | $factory = $this->factoryProvider(Provider\User::class); 289 | $factory(['id' => 123, 'first_name' => 'Jane', 'role' => 'author'])->__monkeyMakeCurrent(); 290 | 291 | $this->expectOutputString('Jane rocks!'); 292 | 293 | $rocks = static function (\WP_User $user): \WP_User { 294 | if (array_intersect(['editor', 'author'], $user->roles)) { 295 | print "{$user->user_firstname} rocks!"; // phpcs:ignore 296 | } 297 | 298 | return $user; 299 | }; 300 | 301 | if (get_current_user_id() === 123 && current_user_can('edit_published_posts')) { 302 | $user = $rocks(wp_get_current_user()); 303 | if (filter_var($user->user_url, FILTER_VALIDATE_URL)) { 304 | wp_safe_redirect($user->user_url); 305 | } 306 | } 307 | } 308 | 309 | /** 310 | * @param \WP_User $left 311 | * @param \WP_User $right 312 | */ 313 | private function compareUser(\WP_User $left, \WP_User $right): void 314 | { 315 | static::assertSame($left->to_array(), $right->to_array()); 316 | static::assertSame($left->allcaps, $right->allcaps); 317 | static::assertSame($left->caps, $right->caps); 318 | static::assertSame($left->roles, $right->roles); 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /tests/cases/unit/ProvidersInitTest.php: -------------------------------------------------------------------------------- 1 | expectExceptionMessageRegExp('/initialized/'); 24 | 25 | $provider = new Providers(Factory::create()); 26 | $provider->wp(); 27 | } 28 | 29 | public function testCallMethodRequiresInitialization() 30 | { 31 | $this->expectExceptionMessageRegExp('/initialized/'); 32 | 33 | $provider = new Providers(Factory::create()); 34 | /** @noinspection PhpUndefinedMethodInspection */ 35 | $provider->test(); 36 | } 37 | 38 | /** 39 | * @runInSeparateProcess 40 | */ 41 | public function testResetUniqueness() 42 | { 43 | $provider = new Providers(Factory::create()); 44 | 45 | $php = <<<'PHP' 46 | namespace Brain\Faker\Tests\Unit; 47 | 48 | class ExampleClass extends \Brain\Faker\Provider\Provider { 49 | public function __invoke(array $args = []) { 50 | return (object)['digit' => $this->uniqueGenerator->randomDigitNotNull]; 51 | } 52 | } 53 | PHP; 54 | eval($php); // phpcs:ignore 55 | 56 | /** @noinspection PhpUndefinedClassInspection */ 57 | $provider->__addProviderClass(ExampleClass::class, 'thing', 'things'); 58 | 59 | $exceptionHappened = false; 60 | 61 | try { 62 | /** @noinspection PhpUndefinedMethodInspection */ 63 | $provider->wp()->things(10); 64 | } catch (\OverflowException $exception) { 65 | $exceptionHappened = true; 66 | } 67 | 68 | static::assertTrue($exceptionHappened); 69 | 70 | $provider->wp()->__resetUnique(); 71 | 72 | static::assertIsInt($provider->wp()->thing->digit); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tests/cases/unit/ProvidersTest.php: -------------------------------------------------------------------------------- 1 | expectException(\Error::class); 26 | $this->expectExceptionMessageMatches('/class/'); 27 | 28 | $provider = new Providers(Factory::create()); 29 | $provider->__addProviderClass(__CLASS__, 'bar', 'baz'); 30 | } 31 | 32 | public function testAddProviderClassFailsForNotExistentClass() 33 | { 34 | $this->expectException(\Error::class); 35 | $this->expectExceptionMessageMatches('/class/'); 36 | 37 | $provider = new Providers(Factory::create()); 38 | $provider->__addProviderClass('Meh', 'bar', 'baz'); 39 | } 40 | 41 | public function testAddProviderClassFailsForNotInstantiableClass() 42 | { 43 | $this->expectException(\Error::class); 44 | $this->expectExceptionMessageMatches('/not a valid provider class/'); 45 | 46 | $class = $this->declarePhpClass(null, null, null, 'abstract'); 47 | 48 | $provider = new Providers(Factory::create()); 49 | /** @noinspection PhpUndefinedClassInspection */ 50 | $provider->__addProviderClass($class, 'foo', 'bar'); 51 | } 52 | 53 | public function testAddProviderClassFailsForReservedMethodOne() 54 | { 55 | $this->expectExceptionMessageMatches('/reserved method/'); 56 | 57 | $class = $this->declarePhpClass(); 58 | 59 | $provider = new Providers(Factory::create()); 60 | $provider->__addProviderClass($class, 'wp', 'bar'); 61 | } 62 | 63 | public function testAddProviderClassFailsForReservedMethodMany() 64 | { 65 | $this->expectExceptionMessageMatches('/reserved method/'); 66 | 67 | $class = $this->declarePhpClass(); 68 | 69 | $provider = new Providers(Factory::create()); 70 | $provider->__addProviderClass($class, 'foo', '__call'); 71 | } 72 | 73 | public function testAddProviderClassFailsForNotUniqueClass() 74 | { 75 | $this->expectExceptionMessageMatches('/uniquely identify/'); 76 | 77 | $class1 = $this->declarePhpClass('ClassOne'); 78 | $class2 = $this->declarePhpClass('ClassTwo'); 79 | 80 | $provider = new Providers(Factory::create()); 81 | $provider->__addProviderClass($class1, 'foo', 'notUnique'); 82 | $provider->__addProviderClass($class2, 'notUnique', 'baz'); 83 | 84 | /** @noinspection PhpUndefinedMethodInspection */ 85 | $provider->wp()->notUnique(); 86 | } 87 | 88 | public function testAddProviderClassSuccessfully() 89 | { 90 | $class = $this->declarePhpClass(); 91 | 92 | $provider = new Providers(Factory::create()); 93 | 94 | static::assertSame($provider, $provider->__addProviderClass($class, 'one', 'many')); 95 | } 96 | 97 | public function testCallNotRegisteredMethod() 98 | { 99 | $this->expectExceptionMessageMatches('/undefined method/'); 100 | 101 | $class = $this->declarePhpClass(); 102 | 103 | $provider = new Providers(Factory::create()); 104 | 105 | $provider->__addProviderClass($class, 'one', 'many'); 106 | 107 | /** @noinspection PhpUndefinedMethodInspection */ 108 | $provider->wp()->foo(); 109 | } 110 | 111 | public function testCallOne() 112 | { 113 | $class = $this->declarePhpClass(null, 'return (object)["name" => "one"];'); 114 | 115 | $provider = new Providers(Factory::create()); 116 | 117 | $provider->__addProviderClass($class, 'one', 'many'); 118 | /** @noinspection PhpUndefinedMethodInspection */ 119 | $result = $provider->wp()->one(); 120 | 121 | static::assertInstanceOf(\stdClass::class, $result); 122 | static::assertSame('one', $result->name); 123 | } 124 | 125 | public function testCallManyFailsIfNoNumber() 126 | { 127 | $this->expectExceptionMessageMatches('/number/'); 128 | 129 | $class = $this->declarePhpClass(); 130 | 131 | $provider = new Providers(Factory::create()); 132 | 133 | $provider->__addProviderClass($class, 'one', 'many'); 134 | /** @noinspection PhpUndefinedMethodInspection */ 135 | $provider->wp()->many('meh'); 136 | } 137 | 138 | public function testCallManyFailsIfNoArray() 139 | { 140 | $this->expectExceptionMessageMatches('/array/'); 141 | 142 | $class = $this->declarePhpClass(); 143 | 144 | $provider = new Providers(Factory::create()); 145 | 146 | $provider->__addProviderClass($class, 'one', 'many'); 147 | /** @noinspection PhpUndefinedMethodInspection */ 148 | $provider->wp()->many(1, 'meh'); 149 | } 150 | 151 | public function testCallMany() 152 | { 153 | $invoke = <<<'PHP' 154 | $args['str'] = $this->uniqueGenerator->word(); 155 | $args['num'] = $this->generator->randomNumber(); 156 | return (object)$args; 157 | PHP; 158 | 159 | $class = $this->declarePhpClass(null, $invoke); 160 | 161 | $provider = new Providers(Factory::create()); 162 | 163 | $provider->__addProviderClass($class, 'one', 'many'); 164 | 165 | /** @noinspection PhpUndefinedMethodInspection */ 166 | $result = $provider->wp()->many(7); 167 | 168 | static::assertIsArray($result); 169 | static::assertCount(7, $result); 170 | 171 | $last = null; 172 | foreach ($result as $element) { 173 | static::assertNotSame($last, $element); 174 | static::assertInstanceOf(\stdClass::class, $element); 175 | static::assertIsString($element->str); 176 | static::assertIsInt($element->num); 177 | $last = $element; 178 | } 179 | } 180 | 181 | public function testCallManyWithDefaults() 182 | { 183 | $class = $this->declarePhpClass(); 184 | 185 | $provider = new Providers(Factory::create()); 186 | 187 | $provider->__addProviderClass($class, 'one', 'many'); 188 | /** @noinspection PhpUndefinedMethodInspection */ 189 | $result = $provider->wp()->many(); 190 | 191 | static::assertIsArray($result); 192 | 193 | foreach ($result as $element) { 194 | static::assertInstanceOf(\stdClass::class, $element); 195 | } 196 | } 197 | 198 | public function testCallManyReturnEmptyArrayIfRequiringZeroItems() 199 | { 200 | $class = $this->declarePhpClass(); 201 | 202 | $provider = new Providers(Factory::create()); 203 | 204 | $provider->__addProviderClass($class, 'one', 'many'); 205 | 206 | /** @noinspection PhpUndefinedMethodInspection */ 207 | static::assertSame([], $provider->wp()->many(0)); 208 | } 209 | 210 | public function testCallOneWithArgs() 211 | { 212 | $class = $this->declarePhpClass(); 213 | 214 | $provider = new Providers(Factory::create()); 215 | 216 | $provider->__addProviderClass($class, 'one', 'many'); 217 | 218 | /** @noinspection PhpUndefinedMethodInspection */ 219 | $result = $provider->wp()->one(['hello' => 'world']); 220 | 221 | static::assertInstanceOf(\stdClass::class, $result); 222 | static::assertSame('world', $result->hello); 223 | } 224 | 225 | public function testCallOneViaProperty() 226 | { 227 | $class = $this->declarePhpClass(); 228 | 229 | $provider = new Providers(Factory::create()); 230 | 231 | $provider->__addProviderClass($class, 'one', 'many'); 232 | 233 | static::assertInstanceOf(\stdClass::class, $provider->wp()->one); 234 | } 235 | 236 | public function testCallManyViaProperty() 237 | { 238 | $class = $this->declarePhpClass(); 239 | 240 | $provider = new Providers(Factory::create()); 241 | 242 | $provider->__addProviderClass($class, 'one', 'many'); 243 | 244 | static::assertIsArray($provider->wp()->many); 245 | } 246 | 247 | public function testAtLeast() 248 | { 249 | $class = $this->declarePhpClass(); 250 | 251 | $provider = new Providers(Factory::create()); 252 | 253 | $provider->__addProviderClass($class, 'item', 'items'); 254 | /** @noinspection PhpUndefinedMethodInspection */ 255 | $result = $provider->wp()->atLeast3items(); 256 | 257 | static::assertIsArray($result); 258 | static::assertGreaterThanOrEqual(3, count($result)); 259 | } 260 | 261 | public function testAtMost() 262 | { 263 | $class1 = $this->declarePhpClass('ExampleOne'); 264 | $class2 = $this->declarePhpClass('ExampleTwo'); 265 | 266 | $provider = new Providers(Factory::create()); 267 | 268 | $provider->__addProviderClass($class1, 'post', 'posts'); 269 | $provider->__addProviderClass($class2, 'user', 'users'); 270 | 271 | /** @noinspection PhpUndefinedMethodInspection */ 272 | $atMost2posts = $provider->wp()->atMost2posts(); 273 | /** @noinspection PhpUndefinedMethodInspection */ 274 | $atMost3users = $provider->wp()->atMost3users(); 275 | /** @noinspection PhpUndefinedMethodInspection */ 276 | $atMost1users = $provider->wp()->atMost1users(); 277 | /** @noinspection PhpUndefinedMethodInspection */ 278 | $atMost0posts = $provider->wp()->atMost0posts(); 279 | 280 | static::assertIsArray($atMost2posts); 281 | static::assertIsArray($atMost3users); 282 | static::assertIsArray($atMost1users); 283 | static::assertSame([], $atMost0posts); 284 | static::assertContains(count($atMost2posts), [0, 1, 2]); 285 | static::assertContains(count($atMost3users), [0, 1, 2, 3]); 286 | static::assertContains(count($atMost1users), [0, 1]); 287 | } 288 | 289 | public function testAtLeastAndAtMostDynamic() 290 | { 291 | $class = $this->declarePhpClass(); 292 | 293 | $provider = new Providers(Factory::create()); 294 | 295 | $provider->__addProviderClass($class, 'thing', 'things'); 296 | 297 | /** @noinspection PhpUndefinedMethodInspection */ 298 | $atLeastOneThing = $provider->wp()->atLeastOneThing(); 299 | /** @noinspection PhpUndefinedMethodInspection */ 300 | $atLeastTwoThings = $provider->wp()->atLeastTwoThings(); 301 | /** @noinspection PhpUndefinedMethodInspection */ 302 | $atLeastThreeThings = $provider->wp()->atLeastThreeThings(); 303 | /** @noinspection PhpUndefinedMethodInspection */ 304 | $atLeastFourThings = $provider->wp()->atLeastFourThings(); 305 | /** @noinspection PhpUndefinedMethodInspection */ 306 | $atLeastFiveThings = $provider->wp()->atLeastFiveThings(); 307 | 308 | /** @noinspection PhpUndefinedMethodInspection */ 309 | $atMostOneThing = $provider->wp()->atMostOneThing(); 310 | /** @noinspection PhpUndefinedMethodInspection */ 311 | $atMostTwoThings = $provider->wp()->atMostTwoThings(); 312 | /** @noinspection PhpUndefinedMethodInspection */ 313 | $atMostThreeThings = $provider->wp()->atMostThreeThings(); 314 | /** @noinspection PhpUndefinedMethodInspection */ 315 | $atMostFourThings = $provider->wp()->atMostFourThings(); 316 | /** @noinspection PhpUndefinedMethodInspection */ 317 | $atMostFiveThings = $provider->wp()->atMostFiveThings(); 318 | 319 | static::assertIsArray($atLeastOneThing); 320 | static::assertIsArray($atLeastTwoThings); 321 | static::assertIsArray($atLeastThreeThings); 322 | static::assertIsArray($atLeastFourThings); 323 | static::assertIsArray($atLeastFiveThings); 324 | static::assertIsArray($atMostOneThing); 325 | static::assertIsArray($atMostTwoThings); 326 | static::assertIsArray($atMostThreeThings); 327 | static::assertIsArray($atMostFourThings); 328 | static::assertIsArray($atMostFiveThings); 329 | 330 | static::assertGreaterThanOrEqual(1, count($atLeastOneThing)); 331 | static::assertGreaterThanOrEqual(2, count($atLeastTwoThings)); 332 | static::assertGreaterThanOrEqual(3, count($atLeastThreeThings)); 333 | static::assertGreaterThanOrEqual(4, count($atLeastFourThings)); 334 | static::assertGreaterThanOrEqual(5, count($atLeastFiveThings)); 335 | 336 | static::assertLessThanOrEqual(1, count($atMostOneThing)); 337 | static::assertLessThanOrEqual(2, count($atMostTwoThings)); 338 | static::assertLessThanOrEqual(3, count($atMostThreeThings)); 339 | static::assertLessThanOrEqual(4, count($atMostFourThings)); 340 | static::assertLessThanOrEqual(5, count($atMostFiveThings)); 341 | } 342 | 343 | public function testBetweenDynamic() 344 | { 345 | $class = $this->declarePhpClass(); 346 | 347 | $provider = new Providers(Factory::create()); 348 | 349 | $provider->__addProviderClass($class, 'thing', 'things'); 350 | 351 | $allTheThings = [ 352 | ['betweenOneAndThreeThings', 1, 3], 353 | ['betweenFiveAndTwoThings', 2, 5], 354 | ['betweenFourAndFourThings', 4, 4], 355 | ['between1AndThreeThings', 1, 3], 356 | ['between5AndTwoThings', 2, 5], 357 | ['between4AndFourThings', 4, 4], 358 | ['betweenOneAnd3Things', 1, 3], 359 | ['betweenFiveAnd2Things', 2, 5], 360 | ['betweenFourAnd4Things', 4, 4], 361 | ['between1And3Things', 1, 3], 362 | ['between5And2Things', 2, 5], 363 | ['between4And4Things', 4, 4], 364 | ]; 365 | 366 | foreach ($allTheThings as [$method, $min, $max]) { 367 | $things = $provider->wp()->{$method}(['x' => 'X']); 368 | 369 | static::assertIsArray($things); 370 | 371 | foreach ($things as $thing) { 372 | static::assertInstanceOf(\stdClass::class, $thing); 373 | static::assertSame('X', $thing->x); 374 | } 375 | 376 | $count = count($things); 377 | 378 | if ($min === $max) { 379 | static::assertSame($min, $count); 380 | continue; 381 | } 382 | 383 | static::assertGreaterThanOrEqual($min, $count); 384 | static::assertLessThanOrEqual($max, $count); 385 | } 386 | } 387 | 388 | public function testBetweenDynamicWrongEdgeCases() 389 | { 390 | $class = $this->declarePhpClass(); 391 | 392 | $provider = new Providers(Factory::create()); 393 | 394 | $provider->__addProviderClass($class, 'thing', 'things'); 395 | 396 | $methods = [ 397 | 'atLeastOneAndThreeThings', 398 | 'atMost1And3Things', 399 | 'atMostOneMeh', 400 | 'between2Things', 401 | 'between2And3Stuff', 402 | ]; 403 | 404 | $expected = count($methods); 405 | $exceptionsCount = 0; 406 | 407 | try { 408 | execute: { 409 | $method = array_shift($methods); 410 | $provider->wp()->{$method}(); 411 | } 412 | } catch (\Error $error) { 413 | static::assertRegExp('/undefined method/', $error->getMessage()); 414 | $exceptionsCount++; 415 | if ($methods) { 416 | goto execute; 417 | } 418 | } 419 | 420 | static::assertSame($expected, $exceptionsCount); 421 | } 422 | 423 | /** 424 | * @param string $name 425 | * @param string|null $invokeBody 426 | * @param string $invokeVisibility 427 | * @param string|null $classModifier 428 | * @return string 429 | */ 430 | private function declarePhpClass( 431 | ?string $name = null, 432 | ?string $invokeBody = null, 433 | ?string $invokeVisibility = null, 434 | ?string $classModifier = null 435 | ): string { 436 | 437 | isset($name) or $name = 'ExampleClass'; 438 | isset($invokeBody) or $invokeBody = 'return (object)$args;'; 439 | isset($invokeVisibility) or $invokeVisibility = 'public'; 440 | isset($classModifier) or $classModifier = ''; 441 | 442 | $php = <<faker = Brain\faker(); 37 | 38 | /** @noinspection PhpUndefinedMethodInspection */ 39 | $this->wpFaker = $this->faker->wp(); 40 | } 41 | 42 | /** 43 | * @return void 44 | */ 45 | protected function tearDown(): void 46 | { 47 | Brain\fakerReset(); 48 | parent::tearDown(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/src/ProviderTestCase.php: -------------------------------------------------------------------------------- 1 | functions = new FunctionExpectations(); 34 | } 35 | 36 | protected function tearDown(): void 37 | { 38 | $this->functions->reset(); 39 | $this->functions = null; 40 | 41 | /** @var Provider$provider */ 42 | foreach ($this->providers as $provider) { 43 | $provider->reset(); 44 | } 45 | 46 | parent::tearDown(); 47 | } 48 | 49 | /** 50 | * @param string $class 51 | * @return Provider 52 | */ 53 | protected function factoryProvider(string $class): Provider 54 | { 55 | /** @var Provider $provider */ 56 | $provider = new $class(Factory::create(), $this->functions); 57 | $this->providers[] = $provider; 58 | 59 | return $provider; 60 | } 61 | 62 | /** 63 | * @param string $formatString 64 | * @return \DateTime|null 65 | */ 66 | protected function dateByMySql(string $formatString): ?\DateTime 67 | { 68 | return \DateTime::createFromFormat('Y-m-d H:i:s', $formatString) ?: null; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /tests/src/TestCase.php: -------------------------------------------------------------------------------- 1 |