Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Added `scheduled()` state to post factory to create scheduled posts.
- Added `create_ordered_set_and_get()` method to factories to create and
retrieve an ordered set of models.
- Added `collect_many()` and `collect_many_and_get()` methods to factories to
create multiple models and return them as a collection.
- Added support for passing callables as model attributes in factories.
- Added `PermalinkStructure` attribute to set the permalink structure during tests.

### Changed

- Clear `$_COOKIE` and `$_SESSION` in addition to other superglobals when
cleaning the global scope between tests.


## v1.12.4

### Fixed
Expand Down
45 changes: 34 additions & 11 deletions src/mantle/database/factory/class-factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -277,34 +277,51 @@ public function with( array|callable $state ): static {
}

/**
* Creates multiple objects.
* Creates multiple objects as an array.
*
* @param int $count Amount of objects to create.
* @param array $args Optional. The arguments for the object to create. Default is empty array.
*
* @return array<int, int>
*/
public function create_many( int $count, array $args = [] ): array {
return collect()
->pad( $count, null )
->map( fn () => $this->create( $args ) )
->to_array();
return $this->collect_many( $count, $args )->all();
}

/**
* Creates multiple objects as a collection.
*
* @param int $count Amount of objects to create.
* @param array $args Optional. The arguments for the object to create. Default is empty array.
*
* @return Collection<int, int>
*/
public function collect_many( int $count, array $args = [] ): Collection {
return collect()->times( $count, fn () => $this->create( $args ) );
}

/**
* Creates multiple objects and returns their objects.
* Creates multiple objects and returns their objects in an array.
*
* @param int $count Amount of objects to create.
* @param array $args Optional. The arguments for the object to create. Default is empty array.
*
* @return array<int, TReturnValue>
*/
public function create_many_and_get( int $count, array $args = [] ): array {
return collect()
->pad( $count, null )
->map( fn () => $this->create_and_get( $args ) )
->all();
return $this->collect_many_and_get( $count, $args )->all();
}

/**
* Creates multiple objects and returns their objects in a collection.
*
* @param int $count Amount of objects to create.
* @param array $args Optional. The arguments for the object to create. Default is empty array.
*
* @return Collection<int, TReturnValue>
*/
public function collect_many_and_get( int $count, array $args = [] ): Collection {
return collect()->times( $count, fn () => $this->create_and_get( $args ) );
}

/**
Expand All @@ -320,7 +337,7 @@ public function create_and_get( array $args = [] ): mixed {
/**
* Pass arguments through the middleware and return a core object.
*
* @param array $args Arguments to pass through the middleware.
* @param array<mixed> $args Arguments to pass through the middleware.
* @return TObject|Core_Object|null
*/
protected function make( array $args ) {
Expand All @@ -339,6 +356,12 @@ function ( array $args ) use ( $factory ): Model {
$args = wp_slash( $args );
}

foreach ( $args as $key => $value ) {
if ( is_callable( $value ) ) {
$args[ $key ] = $value( $key, $args );
}
}

return $this->get_model()::create( $args );
},
);
Expand Down
9 changes: 4 additions & 5 deletions src/mantle/database/factory/class-fluent-factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,10 @@ public function create_and_get( array $args = [] ): mixed {
return $this->factory->create_and_get( $args );
}

return collect()
->times(
$this->count,
fn () => $this->factory->create_and_get( $args ),
);
return collect()->times(
$this->count,
fn () => $this->factory->create_and_get( $args ),
);
}

/**
Expand Down
67 changes: 54 additions & 13 deletions src/mantle/database/factory/class-post-factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

use Carbon\Carbon;
use Closure;
use DateTimeInterface;
use Faker\Generator;
use Mantle\Database\Model\Attachment;
use Mantle\Database\Model\Post;
Expand Down Expand Up @@ -234,6 +235,32 @@ public function for( string $post_type ): static {
return $this->with_post_type( $post_type );
}

/**
* Create a new factory instance to create scheduled posts.
*
* @throws \InvalidArgumentException If the date is not in the future.
*
* @param DateTimeInterface|string|null $date The date to schedule the post for. Defaults to 1 day in the future.
*/
public function scheduled( DateTimeInterface|string|null $date = null ): static {
$date = match ( true ) {
$date instanceof DateTimeInterface => $date->format( 'Y-m-d H:i:s' ),
is_string( $date ) => $date,
default => Carbon::now()->addDay()->format( 'Y-m-d H:i:s' ),
};

// Ensure the date is in the future.
if ( strtotime( $date ) <= current_time( 'timestamp' ) ) { // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
throw new \InvalidArgumentException( 'The date for a scheduled post must be in the future.' );
}

return $this->state( [
'post_date' => $date,
'post_date_gmt' => get_gmt_from_date( $date ),
'post_status' => 'future',
] );
}

/**
* Definition of the factory.
*
Expand Down Expand Up @@ -289,19 +316,33 @@ public function create_ordered_set(
// Set the date for the first post (seconds added before each run).
$starting_date->subSeconds( $separation );

return collect()
->pad( $count, null )
->map(
fn () => $this->create(
array_merge(
$args,
[
'date' => $starting_date->addSeconds( $separation )->format( 'Y-m-d H:i:s' ),
]
)
)
)
->to_array();
return collect()->times( $count, fn () => $this->create( array_merge( $args, [
'date' => $starting_date->addSeconds( $separation )->format( 'Y-m-d H:i:s' ),
] ) ) )->all();
}

/**
* Create and get an ordered set of posts.
*
* @see Post_Factory::create_ordered_set() for details.
*
* @param int $count The number of posts to create.
* @param array<mixed> $args The arguments.
* @param Carbon|string $starting_date The starting date for the posts, defaults to
* a month ago.
* @param int $separation The number of seconds between each post.
* @return array<int, TModel>
* @phpstan-return array<int, TModel>
*/
public function create_ordered_set_and_get(
int $count = 10,
array $args = [],
Carbon|string|null $starting_date = null,
int $separation = 3600
): array {
return collect( $this->create_ordered_set( $count, $args, $starting_date, $separation ) )
->map( $this->get_object_by_id( ... ) )
->all();
}

/**
Expand Down
4 changes: 4 additions & 0 deletions src/mantle/testing/concerns/trait-wordpress-state.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
/**
* This file contains the WordPress_State trait
*
* phpcs:disable WordPressVIPMinimum.Variables.RestrictedVariables
*
* @package Mantle
*/

Expand Down Expand Up @@ -64,9 +66,11 @@ public function register_permalink_structure_attribute(): void {
* Cleans the global scope (e.g `$_GET` and `$_POST`).
*/
public static function clean_up_global_scope(): void {
$_COOKIE = [];
$_GET = [];
$_POST = [];
$_REQUEST = [];
$_SESSION = [];

self::flush_cache();
}
Expand Down
40 changes: 38 additions & 2 deletions tests/Database/Factory/FactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Mantle\Database\Factory\Post_Factory;
use Mantle\Database\Model;
use Mantle\Database\Model\Post;
use Mantle\Support\Collection;
use Mantle\Testing\FrameworkTestCase;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;
Expand Down Expand Up @@ -33,12 +34,26 @@ public function test_create_many(): void {
$post_ids = $factory->create_many( 5 );

$this->assertCount( 5, $post_ids );
$this->assertIsArray( $post_ids );

foreach ( $post_ids as $post_id ) {
$this->assertIsInt( $post_id );
if ( method_exists( $this, 'assertContainsOnlyInt' ) ) {
$this->assertContainsOnlyInt( $post_ids );
} else {
$this->assertContainsOnly( 'int', $post_ids );
}
}

public function test_collect_many(): void {
$factory = Post::factory();

$this->assertInstanceOf( Factory\Post_Factory::class, $factory );

$posts = $factory->collect_many( 5 );

$this->assertCount( 5, $posts );
$this->assertInstanceOf( Collection::class, $posts );
}

public function test_create_many_and_get(): void {
$factory = Post::factory();

Expand All @@ -50,6 +65,18 @@ public function test_create_many_and_get(): void {
$this->assertContainsOnlyInstancesOf( Post::class, $posts );
}

public function test_collect_many_and_get(): void {
$factory = Post::factory();

$this->assertInstanceOf( Factory\Post_Factory::class, $factory );

$posts = $factory->collect_many_and_get( 5 );

$this->assertCount( 5, $posts );
$this->assertInstanceOf( Collection::class, $posts );
$this->assertContainsOnlyInstancesOf( Post::class, $posts->all() );
}

public function test_create_model_with_custom_factory() {
$factory = Testable_Post_With_Factory::factory();

Expand Down Expand Up @@ -151,6 +178,7 @@ public function test_create_multiple_fluently() {

$this->assertIsArray( $post_ids );
$this->assertCount( 3, $post_ids );

if ( method_exists( $this, 'assertContainsOnlyInt' ) ) {
$this->assertContainsOnlyInt( $post_ids );
} else {
Expand Down Expand Up @@ -227,6 +255,14 @@ public function test_create_custom_taxonomy_model() {
$this->assertEquals( 'custom_taxonomy', $post->taxonomy );
$this->assertNotEmpty( $post->name );
}

public function test_callable_as_attribute(): void {
$post = Testable_Post::factory()->create_and_get( [
'post_title' => fn () => 'Title from callable',
] );

$this->assertEquals( 'Title from callable', $post->post_title );
}
}

class Testable_Post extends Model\Post {
Expand Down
36 changes: 29 additions & 7 deletions tests/Database/Factory/UnitTestingFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Mantle\Support\Str;
use Mantle\Testing\Concerns\With_Faker;
use Mantle\Testing\FrameworkTestCase;
use Mantle\Testing\Utils;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;

Expand All @@ -24,6 +25,12 @@
class UnitTestingFactoryTest extends FrameworkTestCase {
use With_Faker;

protected function setUp(): void {
parent::setUp();

Utils::delete_all_posts();
}

public function test_post_factory() {
$this->assertInstanceOf( \WP_Post::class, static::factory()->post->create_and_get() );

Expand Down Expand Up @@ -63,11 +70,7 @@ public function test_post_with_thumbnail_middleware() {
}

public function test_create_ordered_set() {
$post_ids = static::factory()->post->create_ordered_set( 10, [
'meta' => [
'_test_date_meta_key' => '_test_meta_value',
],
] );
$post_ids = static::factory()->post->create_ordered_set( 10 );

$this->assertCount( 10, $post_ids );

Expand All @@ -85,8 +88,6 @@ public function test_create_ordered_set() {
// Query the posts and ensure the order matches.
$queried_post_ids = get_posts( [
'fields' => 'ids',
'meta_key' => '_test_date_meta_key',
'meta_value' => '_test_meta_value',
'order' => 'DESC',
'orderby' => 'post_date',
'posts_per_page' => 50,
Expand All @@ -99,6 +100,13 @@ public function test_create_ordered_set() {
$this->assertEquals( array_reverse( $post_ids ), $queried_post_ids );
}

public function test_create_and_get_ordered_set(): void {
$posts = static::factory()->post->create_ordered_set_and_get( 10 );

$this->assertCount( 10, $posts );
$this->assertContainsOnlyInstancesOf( \WP_Post::class, $posts );
}

public function test_attachment_factory() {
$this->shim_test( \WP_Post::class, 'attachment' );

Expand Down Expand Up @@ -594,6 +602,20 @@ public function test_first_or_create(): void {
$this->assertEquals( $existing->ID, $post->ID );
}

public function test_scheduled_post(): void {
$post = static::factory()->post->scheduled()->create_and_get();

$this->assertEquals( 'future', get_post_status( $post ) );
$this->assertGreaterThan( current_time( 'timestamp' ), strtotime( $post->post_date ) );
}

public function test_scheduled_post_in_past(): void {
$this->expectException( \InvalidArgumentException::class );
$this->expectExceptionMessage( 'The date for a scheduled post must be in the future.' );

static::factory()->post->scheduled( Carbon::now()->subDay() )->create_and_get();
}

public static function slug_id_dataprovider(): array {
return [
'term_id' => [ 'term_id' ],
Expand Down
Loading