Skip to content

Commit

Permalink
Create terms when passed a slug during testing (#597)
Browse files Browse the repository at this point in the history
* Add support for an array of term slugs with the factory

* Create terms when passed an unknown slug

* Use controllable prop

* CHANGELOG

* CHANGELOG

* Adding legacy dataprovider for phpunit 9
  • Loading branch information
srtfisher authored Oct 31, 2024
1 parent 770b9b5 commit 3573a07
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 17 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Changed

- Post factories that are passed a term slug to `with_terms()` will create the
term if it doesn't exist by default. This can be disabled by calling the
`create_terms()` method on the factory or replacing the with terms method call
with `with_terms_only_existing()`.

### Fixed

- Ensure that the `delete()` method of the HTTP Client doesn't set a body by default.
- Ensure that `with_terms()` can support an array of term slugs when passed with a
taxonomy index.

## v1.2.0 - 2024-09-23

Expand Down
54 changes: 52 additions & 2 deletions src/mantle/database/factory/class-post-factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ class Post_Factory extends Factory {
*/
protected string $model = Post::class;

/**
* Flag to create terms by default.
*/
protected bool $create_terms = true;

/**
* Flag to append terms by default.
*/
protected bool $append_terms = true;

/**
* Constructor.
*
Expand All @@ -46,21 +56,61 @@ public function __construct( Generator $faker, public string $post_type = 'post'
parent::__construct( $faker );
}

/**
* Change the default creation of terms with the post factory.
*
* @param bool $value Value to set.
*/
public function create_terms( bool $value = true ): void {
$this->create_terms = $value;
}

/**
* Change the default appending of terms with the post factory.
*
* @param bool $value Value to set.
*/
public function append_terms( bool $value = true ): void {
$this->append_terms = $value;
}

/**
* Create a new factory instance to create posts with a set of terms.
*
* Any slugs passed that are not found will be created. If you want to
* only use existing terms, use `with_terms_only_existing()`.
*
* @param array<int|string, \WP_Term|int|string|array<string, mixed>>|\WP_Term|int|string ...$terms Terms to assign to the post.
*/
public function with_terms( ...$terms ): static {
// Handle an array in the first argument.
if ( 1 === count( $terms ) && is_array( $terms[0] ) ) {
if ( 1 === count( $terms ) && isset( $terms[0] ) && is_array( $terms[0] ) ) {
$terms = $terms[0];
}

$terms = collect( $terms )->all();

return $this->with_middleware(
fn ( array $args, Closure $next ) => $next( $args )->set_terms( $terms, append: $this->append_terms, create: $this->create_terms ),
);
}

/**
* Create a new factory instance to create posts with a set of terms without creating
* any unknown terms.
*
* @param array<int|string, \WP_Term|int|string|array<string, mixed>>|\WP_Term|int|string ...$terms Terms to assign to the post.
*/
public function with_terms_only_existing( ...$terms ): static {
// Handle an array in the first argument.
if ( 1 === count( $terms ) && isset( $terms[0] ) && is_array( $terms[0] ) ) {
$terms = $terms[0];
}

$terms = collect( $terms )->all();

return $this->with_middleware(
fn ( array $args, Closure $next ) => $next( $args )->set_terms( $terms ),
fn ( array $args, Closure $next ) => $next( $args )->set_terms( $terms, append: $this->append_terms, create: false ),
);
}

Expand Down
29 changes: 24 additions & 5 deletions src/mantle/database/model/term/trait-model-term.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Mantle\Database\Model\Term;
use Mantle\Support\Arr;
use Mantle\Support\Collection;
use Mantle\Support\Str;
use WP_Term;

use function Mantle\Support\Helpers\collect;
Expand Down Expand Up @@ -120,18 +121,19 @@ public function get_terms( string $taxonomy ): array {
* @param mixed $terms Accepts an array of or a single instance of terms.
* @param string $taxonomy Taxonomy name, optional.
* @param bool $append Append to the object's terms, defaults to false.
* @param bool $create Create the term if it does not exist, defaults to false.
* @return static
*
* @throws Model_Exception Thrown if the $taxonomy cannot be inferred from $terms.
* @throws Model_Exception Thrown if error saving the post's terms.
*/
public function set_terms( $terms, ?string $taxonomy = null, bool $append = false ) {
public function set_terms( $terms, ?string $taxonomy = null, bool $append = false, bool $create = false ) {
$terms = collect( Arr::wrap( $terms ) );

// If taxonomy is not specified, chunk the terms into taxonomy groups.
if ( ! $taxonomy ) {
$terms = $terms->reduce(
function ( array $carry, $term ): array {
function ( array $carry, $term, $index ) use ( $create ): array {
if ( $term instanceof WP_Term ) {
$carry[ $term->taxonomy ][] = $term;

Expand Down Expand Up @@ -165,16 +167,33 @@ function ( array $carry, $term ): array {
continue;
}

// Use the parent array key as the taxonomy if the parent array
// key is a string and the current array index is not.
if ( ! is_string( $taxonomy ) && is_string( $index ) ) {
$taxonomy = $index;
}

// Attempt to infer if the key is a taxonomy slug and this is a
// taxonomy => term slug pair.
if ( ! is_string( $taxonomy ) || ! taxonomy_exists( $taxonomy ) ) {
continue;
}

$item = get_term_object_by( 'slug', $item, $taxonomy );
$term = get_term_object_by( 'slug', $item, $taxonomy );

// Optionally create the term if it does not exist.
if ( ! $term && $create ) {
$term = wp_insert_term( Str::headline( $item ), $taxonomy, [ 'slug' => $item ] );

if ( is_wp_error( $term ) ) {
throw new Model_Exception( "Error creating term: [{$term->get_error_message()}]" );
}

$term = get_term( $term['term_id'], $taxonomy );
}

if ( $item ) {
$carry[ $taxonomy ][] = $item;
if ( $term instanceof WP_Term ) {
$carry[ $taxonomy ][] = $term;
}
}

Expand Down
100 changes: 90 additions & 10 deletions tests/Database/Factory/UnitTestingFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ public function test_factory_middleware() {
$this->assertEquals( '_test_meta_value', get_post_meta( $post->ID, '_test_meta_key', true ) );
}

#[Group( 'with_terms' )]
public function test_posts_with_terms() {
$post = static::factory()->post->with_terms(
[
Expand All @@ -183,6 +184,7 @@ public function test_posts_with_terms() {
$this->assertTrue( has_term( $category->term_id, 'category', $post ) );
}

#[Group( 'with_terms' )]
public function test_posts_with_term_ids() {
$post = static::factory()->post->with_terms(
[
Expand All @@ -196,15 +198,7 @@ public function test_posts_with_term_ids() {
$this->assertTrue( has_term( $category->term_id, 'category', $post ) );
}

public function test_terms_with_posts() {
$post_ids = static::factory()->post->create_many( 2 );

$category = static::factory()->category->with_posts( $post_ids )->create_and_get();

$this->assertTrue( has_term( $category->term_id, 'category', $post_ids[0] ) );
$this->assertTrue( has_term( $category->term_id, 'category', $post_ids[1] ) );
}

#[Group( 'with_terms' )]
public function test_posts_with_terms_multiple_taxonomies() {
$post = static::factory()->post->with_terms(
$category = static::factory()->category->create_and_get(),
Expand All @@ -223,7 +217,8 @@ public function test_posts_with_terms_multiple_taxonomies() {
$this->assertTrue( has_term( $tag->term_id, 'post_tag', $post ) );
}

public function test_posts_with_terms_multiple_taxonomies_and_term_slug() {
#[Group( 'with_terms' )]
public function test_posts_with_multiple_taxonomies_with_mixed_objects() {
$tag = static::factory()->tag->create_and_get();

// Test with the arguments passed as individual parameters.
Expand All @@ -249,6 +244,84 @@ public function test_posts_with_terms_multiple_taxonomies_and_term_slug() {
$this->assertTrue( has_term( $tag->term_id, 'post_tag', $post ) );
}

#[Group( 'with_terms' )]
public function test_posts_with_multiple_terms_single_array_argument() {
$tags = collect( static::factory()->tag->create_many( 5 ) )
->map( fn ( $term_id ) => get_term( $term_id ) )
->pluck( 'term_id' )
->all();

$post = static::factory()->post->with_terms( $tags )->create_and_get();

$post_tags = get_the_terms( $post, 'post_tag' );

$this->assertCount( 5, $post_tags );
$this->assertEquals(
collect( $tags )->sort()->values()->all(),
collect( $post_tags )->pluck( 'term_id' )->sort()->values()->all(),
);
}

#[Group( 'with_terms' )]
public function test_posts_with_multiple_terms_spread_array_argument() {
$tags = collect( static::factory()->tag->create_many( 5 ) )
->map( fn ( $term_id ) => get_term( $term_id ) )
->pluck( 'term_id' )
->all();

$post = static::factory()->post->with_terms( ...$tags )->create_and_get();

$post_tags = get_the_terms( $post, 'post_tag' );

$this->assertCount( 5, $post_tags );
$this->assertEquals(
collect( $tags )->sort()->values()->all(),
collect( $post_tags )->pluck( 'term_id' )->sort()->values()->all(),
);
}

/**
* @dataProvider slug_id_dataprovider
*/
#[DataProvider( 'slug_id_dataprovider' )]
#[Group( 'with_terms' )]
public function test_posts_with_multiple_terms_single_array( string $field ) {
$tags = collect( static::factory()->tag->create_many( 5 ) )
->map( fn ( $term_id ) => get_term( $term_id ) )
->pluck( $field )
->all();

$post = static::factory()->post->with_terms( [ 'post_tag' => $tags ] )->create_and_get();
$post_tags = get_the_terms( $post, 'post_tag' );

$this->assertCount( 5, $post_tags );
$this->assertEquals(
collect( $tags )->sort()->values()->all(),
collect( $post_tags )->pluck( $field )->sort()->values()->all(),
);
}

#[Group( 'with_terms' )]
public function test_posts_with_terms_create_unknown_term() {
$post = static::factory()->post->with_terms( [
'post_tag' => [ 'unknown-term' ],
] )->create_and_get();

$post_tags = get_the_terms( $post, 'post_tag' );

$this->assertCount( 1, $post_tags );
$this->assertEquals( 'unknown-term', $post_tags[0]->slug );
}

public function test_terms_with_posts() {
$post_ids = static::factory()->post->create_many( 2 );

$category = static::factory()->category->with_posts( $post_ids )->create_and_get();

$this->assertTrue( has_term( $category->term_id, 'category', $post_ids[0] ) );
$this->assertTrue( has_term( $category->term_id, 'category', $post_ids[1] ) );
}

public function test_post_with_meta() {
$post = static::factory()->post->with_meta(
[
Expand Down Expand Up @@ -358,6 +431,13 @@ public function test_dynamic_factory_conflict() {

static::factory()->conflict->create_and_get();
}

public static function slug_id_dataprovider(): array {
return [
'term_id' => [ 'term_id' ],
'slug' => [ 'slug' ],
];
}
}

class Testable_Post_Tag extends Term {
Expand Down

0 comments on commit 3573a07

Please sign in to comment.