mirror of
https://github.com/wp-graphql/wp-graphql-woocommerce.git
synced 2026-08-14 12:53:44 +02:00
* fix: session secret key, cart session persistence, null variation attributes - Use wp_salt() as fallback secret key instead of hardcoded 24-byte string to satisfy php-jwt v7's HS256 minimum key length requirement (Closes #1009) - Call get_cart_from_session() after wc_load_cart() in initialize_session_and_cart() to prevent cart queries from clearing persisted session data (Closes #1010) - Guard against null $attrs in variation_attributes_to_data_array() to prevent PHP warnings when variations have no attributes (Closes #1011) - Return empty array instead of null for product attribute options when no terms exist * chore: Linter compliances met
335 lines
10 KiB
PHP
335 lines
10 KiB
PHP
<?php
|
|
|
|
class VariationAttributeQueriesTest extends \Tests\WPGraphQL\WooCommerce\TestCase\WooGraphQLTestCase {
|
|
public function expectedAttributes( $id ) {
|
|
$product = wc_get_product( $id );
|
|
$attributes = 'variable' === $product->get_type()
|
|
? $product->get_default_attributes()
|
|
: $product->get_attributes();
|
|
|
|
$expected = [];
|
|
foreach ( $attributes as $name => $value ) {
|
|
$term = \get_term_by( 'slug', $value, $name );
|
|
$expected_id = base64_encode( $id . '||' . $name . '||' . $value ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
|
|
if ( ! $term instanceof \WP_Term ) {
|
|
$expected[] = $this->expectedNode(
|
|
'nodes',
|
|
[
|
|
$this->expectedField( 'id', $expected_id ),
|
|
$this->expectedField( 'attributeId', 0 ),
|
|
$this->expectedField( 'name', $name ),
|
|
$this->expectedField( 'value', $value ),
|
|
]
|
|
);
|
|
} else {
|
|
$expected[] = $this->expectedNode(
|
|
'nodes',
|
|
[
|
|
$this->expectedField( 'id', $expected_id ),
|
|
$this->expectedField( 'attributeId', $term->term_id ),
|
|
$this->expectedField( 'name', $term->taxonomy ),
|
|
$this->expectedField( 'value', $term->name ),
|
|
]
|
|
);
|
|
}
|
|
}
|
|
|
|
return $expected;
|
|
}
|
|
|
|
public function expectedDefaultAttributes( $id ) {
|
|
$product = wc_get_product( $id );
|
|
$attributes = $product->get_attributes();
|
|
|
|
$expected = [];
|
|
foreach ( $attributes as $attribute ) {
|
|
$name = $attribute->get_name();
|
|
if ( $attribute->is_taxonomy() ) {
|
|
$attribute_values = wc_get_product_terms( $id, $attribute->get_name(), [ 'fields' => 'all' ] );
|
|
foreach ( $attribute_values as $attribute_value ) {
|
|
$expected[] = $this->expectedNode(
|
|
'nodes',
|
|
[
|
|
$this->expectedField( 'id', base64_encode( $id . '|' . $name . '|' . $attribute_value->name ) ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
|
|
$this->expectedField( 'attributeId', $attribute_value->term_id ),
|
|
$this->expectedField( 'name', $name ),
|
|
$this->expectedField( 'value', $attribute_value->name ),
|
|
]
|
|
);
|
|
}
|
|
} else {
|
|
$values = $attribute->get_options();
|
|
foreach ( $values as $attribute_value ) {
|
|
$expected[] = $this->expectedNode(
|
|
'nodes',
|
|
[
|
|
$this->expectedField( 'id', base64_encode( $id . '|' . $name . '|' . $attribute_value ) ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
|
|
$this->expectedField( 'attributeId', 0 ),
|
|
$this->expectedField( 'name', $name ),
|
|
$this->expectedField( 'value', $attribute_value ),
|
|
]
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $expected;
|
|
}
|
|
// tests
|
|
public function testProductVariationToVariationAttributeQuery() {
|
|
// Create a product and variation.
|
|
$product_ids = $this->factory->product_variation->createSome();
|
|
$variation_id = $product_ids['variations'][0];
|
|
|
|
// Create a query.
|
|
$query = '
|
|
query fromVariationQuery( $id: ID! ) {
|
|
productVariation( id: $id ) {
|
|
id
|
|
attributes {
|
|
nodes {
|
|
id
|
|
attributeId
|
|
name
|
|
value
|
|
}
|
|
}
|
|
}
|
|
}
|
|
';
|
|
|
|
/**
|
|
* Assertion One
|
|
*
|
|
* Test query and results
|
|
*/
|
|
$variables = [ 'id' => $this->toRelayId( 'post', $variation_id ) ];
|
|
$response = $this->graphql( compact( 'query', 'variables' ) );
|
|
$expected = [
|
|
$this->expectedField( 'productVariation.id', $this->toRelayId( 'post', $variation_id ) ),
|
|
$this->expectedObject( 'productVariation.attributes', $this->expectedAttributes( $variation_id ) ),
|
|
];
|
|
|
|
$this->assertQuerySuccessful( $response, $expected );
|
|
}
|
|
|
|
public function testVariableProductToVariationAttributeQuery() {
|
|
// Create a variable product w/ default attributes..
|
|
$product_ids = $this->factory->product_variation->createSome();
|
|
$product_id = $product_ids['product'];
|
|
|
|
// Create a query.
|
|
$query = '
|
|
query ( $id: ID! ) {
|
|
product( id: $id ) {
|
|
... on VariableProduct {
|
|
id
|
|
defaultAttributes {
|
|
nodes {
|
|
id
|
|
attributeId
|
|
name
|
|
value
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
';
|
|
|
|
/**
|
|
* Assertion One
|
|
*
|
|
* Test query and results
|
|
*/
|
|
$variables = [ 'id' => $this->toRelayId( 'post', $product_id ) ];
|
|
$response = $this->graphql( compact( 'query', 'variables' ) );
|
|
$expected = [
|
|
$this->expectedField( 'product.id', $this->toRelayId( 'post', $product_id ) ),
|
|
$this->expectedObject( 'product.defaultAttributes', $this->expectedAttributes( $product_id ) ),
|
|
];
|
|
|
|
$this->assertQuerySuccessful( $response, $expected );
|
|
}
|
|
|
|
public function testSimpleProductToVariationAttributeQuery() {
|
|
// Create a product w/ default attributes.
|
|
$attribute_data = [
|
|
$this->factory->product->createAttribute( 'size', [ 'small' ] ),
|
|
$this->factory->product->createAttribute( 'color', [ 'red' ] ),
|
|
[
|
|
'attribute_id' => 0,
|
|
'attribute_taxonomy' => 'logo',
|
|
'term_ids' => [ 'Yes' ],
|
|
],
|
|
];
|
|
$attributes = array_map(
|
|
static function ( $data, $index ) {
|
|
$attribute = new \WC_Product_Attribute();
|
|
$attribute->set_id( $data['attribute_id'] );
|
|
$attribute->set_name( $data['attribute_taxonomy'] );
|
|
$attribute->set_options( $data['term_ids'] );
|
|
$attribute->set_position( $index );
|
|
$attribute->set_visible( true );
|
|
$attribute->set_variation( false );
|
|
return $attribute;
|
|
},
|
|
$attribute_data,
|
|
array_keys( $attribute_data )
|
|
);
|
|
$product_id = $this->factory->product->createSimple(
|
|
[
|
|
'attributes' => $attributes,
|
|
'default_attributes' => [
|
|
'pa_size' => 'small',
|
|
'pa_color' => 'red',
|
|
'logo' => 'Yes',
|
|
],
|
|
]
|
|
);
|
|
|
|
// Create a query.
|
|
$query = '
|
|
query ( $id: ID! ) {
|
|
product( id: $id ) {
|
|
... on SimpleProduct {
|
|
id
|
|
defaultAttributes {
|
|
nodes {
|
|
id
|
|
attributeId
|
|
name
|
|
value
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
';
|
|
|
|
/**
|
|
* Assertion One
|
|
*
|
|
* Test query and results
|
|
*/
|
|
$variables = [ 'id' => $this->toRelayId( 'post', $product_id ) ];
|
|
$response = $this->graphql( compact( 'query', 'variables' ) );
|
|
$expected = [
|
|
$this->expectedField( 'product.id', $this->toRelayId( 'post', $product_id ) ),
|
|
$this->expectedObject( 'product.defaultAttributes', $this->expectedDefaultAttributes( $product_id ) ),
|
|
];
|
|
|
|
$this->assertQuerySuccessful( $response, $expected );
|
|
}
|
|
|
|
/**
|
|
* Test that VariationAttribute.label returns human-readable values.
|
|
*
|
|
* Global attributes should return the term name (e.g., "Small" not "small").
|
|
* Local attributes should return the attribute value (e.g., "Yes" not "logo").
|
|
*
|
|
* @see https://github.com/wp-graphql/wp-graphql-woocommerce/issues/965
|
|
*/
|
|
public function testVariationAttributeLabelReturnsHumanReadableValues() {
|
|
$product_ids = $this->factory->product_variation->createSome();
|
|
$variation_id = $product_ids['variations'][0];
|
|
|
|
$query = '
|
|
query ($id: ID!) {
|
|
productVariation(id: $id) {
|
|
attributes {
|
|
nodes {
|
|
name
|
|
label
|
|
value
|
|
}
|
|
}
|
|
}
|
|
}
|
|
';
|
|
|
|
$variables = [ 'id' => $this->toRelayId( 'post', $variation_id ) ];
|
|
$response = $this->graphql( compact( 'query', 'variables' ) );
|
|
|
|
// Build expected labels from the variation's attributes.
|
|
$product = wc_get_product( $variation_id );
|
|
$attributes = $product->get_attributes();
|
|
$expected = [];
|
|
foreach ( $attributes as $name => $value ) {
|
|
$term = get_term_by( 'slug', $value, $name );
|
|
if ( $term instanceof \WP_Term ) {
|
|
// Global attribute: label should be the human-readable attribute label.
|
|
$expected[] = $this->expectedNode(
|
|
'productVariation.attributes.nodes',
|
|
[
|
|
$this->expectedField( 'name', $term->taxonomy ),
|
|
$this->expectedField( 'label', wc_attribute_label( $term->taxonomy ) ),
|
|
$this->expectedField( 'value', $term->slug ),
|
|
]
|
|
);
|
|
} else {
|
|
// Local attribute: label should be the attribute name.
|
|
$expected[] = $this->expectedNode(
|
|
'productVariation.attributes.nodes',
|
|
[
|
|
$this->expectedField( 'name', $name ),
|
|
$this->expectedField( 'label', $name ),
|
|
$this->expectedField( 'value', $value ),
|
|
]
|
|
);
|
|
}
|
|
}
|
|
|
|
$this->assertQuerySuccessful( $response, $expected );
|
|
}
|
|
|
|
/**
|
|
* Test that querying variation attributes returns empty nodes when
|
|
* the variation has null attributes (e.g. corrupted or missing data).
|
|
*
|
|
* @see https://github.com/wp-graphql/wp-graphql-woocommerce/issues/1011
|
|
*/
|
|
public function testVariationWithNullAttributesReturnsEmptyNodes() {
|
|
// Create a variable product with no attributes, then create a bare variation.
|
|
$product_id = $this->factory->product->createVariable( [ 'attribute_data' => [] ] );
|
|
$variation_id = $this->factory->product_variation->create(
|
|
[
|
|
'parent_id' => $product_id,
|
|
'regular_price' => 10,
|
|
]
|
|
);
|
|
|
|
// Confirm WooCommerce returns empty attributes for this variation.
|
|
$variation = wc_get_product( $variation_id );
|
|
$this->assertEmpty( $variation->get_attributes(), 'Variation should have no attributes.' );
|
|
|
|
$query = '
|
|
query ($id: ID!) {
|
|
productVariation(id: $id) {
|
|
id
|
|
attributes {
|
|
nodes {
|
|
id
|
|
name
|
|
value
|
|
}
|
|
}
|
|
}
|
|
}
|
|
';
|
|
|
|
$variables = [ 'id' => $this->toRelayId( 'post', $variation_id ) ];
|
|
$response = $this->graphql( compact( 'query', 'variables' ) );
|
|
|
|
// The query should succeed without PHP warnings or errors,
|
|
// and attributes should be null (no populated attribute data).
|
|
$this->assertQuerySuccessful(
|
|
$response,
|
|
[
|
|
$this->expectedField( 'productVariation.id', $this->toRelayId( 'post', $variation_id ) ),
|
|
$this->expectedField( 'productVariation.attributes', static::IS_NULL ),
|
|
]
|
|
);
|
|
}
|
|
}
|