Remove API summaries in favour of client side code (https://github.com/woocommerce/woocommerce-blocks/pull/2387)

* Remove summary from API

* Add wordCountType to assets

* Update packages

* Remove summary from test data

* Featured product uses short desc

* Pass description instead of summary

* Use new Summary Component

* Component and tests

* Increased versititilty of methods

* Update assets/js/base/components/cart-checkout/product-summary/index.js

Co-authored-by: Darren Ethier <darren@roughsmootheng.in>

* Extra tests for html tags

Co-authored-by: Darren Ethier <darren@roughsmootheng.in>
This commit is contained in:
Mike Jolley 2020-05-06 11:30:15 +01:00 committed by GitHub
parent 87e8b26e6f
commit 74a4e55075
21 changed files with 357 additions and 223 deletions

View File

@ -4,22 +4,30 @@
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { useProductLayoutContext } from '@woocommerce/base-context';
import Summary from '@woocommerce/base-components/summary';
import { getSetting } from '@woocommerce/settings';
const ProductSummary = ( { className, product } ) => {
const { layoutStyleClassPrefix } = useProductLayoutContext();
if ( ! product.summary ) {
const source = product.short_description
? product.short_description
: product.description;
if ( ! source ) {
return null;
}
const countType = getSetting( 'wordCountType', 'words' );
return (
<div
<Summary
className={ classnames(
className,
`${ layoutStyleClassPrefix }__product-summary`
) }
dangerouslySetInnerHTML={ {
__html: product.summary,
} }
source={ source }
maxLength={ 150 }
countType={ countType }
/>
);
};

View File

@ -6,6 +6,7 @@ export { default as PlaceOrderButton } from './place-order-button';
export { default as Policies } from './policies';
export { default as ProductImage } from './product-image';
export { default as ProductLowStockBadge } from './product-low-stock-badge';
export { default as ProductSummary } from './product-summary';
export { default as ProductMetadata } from './product-metadata';
export { default as ProductName } from './product-name';
export { default as ProductPrice } from './product-price';

View File

@ -1,26 +1,34 @@
/**
* External dependencies
*/
import { RawHTML } from '@wordpress/element';
import PropTypes from 'prop-types';
/**
* Internal dependencies
*/
import ProductVariationData from '../product-variation-data';
import ProductSummary from '../product-summary';
import './style.scss';
const ProductMetadata = ( { summary, variation } ) => {
const ProductMetadata = ( {
shortDescription = '',
fullDescription = '',
variation = [],
} ) => {
return (
<div className="wc-block-product-metadata">
{ summary && <RawHTML>{ summary }</RawHTML> }
{ variation && <ProductVariationData variation={ variation } /> }
<ProductSummary
shortDescription={ shortDescription }
fullDescription={ fullDescription }
/>
<ProductVariationData variation={ variation } />
</div>
);
};
ProductMetadata.propTypes = {
summary: PropTypes.string,
shortDescription: PropTypes.string,
fullDescription: PropTypes.string,
variation: PropTypes.array,
};

View File

@ -0,0 +1,28 @@
/**
* External dependencies
*/
import PropTypes from 'prop-types';
import Summary from '@woocommerce/base-components/summary';
import { getSetting } from '@woocommerce/settings';
/**
* Returns an element containing a summary of the product.
*/
const ProductSummary = ( { shortDescription = '', fullDescription = '' } ) => {
const source = shortDescription ? shortDescription : fullDescription;
if ( ! source ) {
return null;
}
return (
<Summary source={ source } maxLength={ 15 } countType={ getSetting( 'wordCountType', 'words' ) } />
);
};
ProductSummary.propTypes = {
shortDescription: PropTypes.string,
fullDescription: PropTypes.string,
};
export default ProductSummary;

View File

@ -7,7 +7,11 @@ import PropTypes from 'prop-types';
/**
* Returns a formatted element containing variation details.
*/
const ProductVariationData = ( { variation } ) => {
const ProductVariationData = ( { variation = [] } ) => {
if ( ! variation ) {
return null;
}
const variationsText = variation
.map( ( v ) => {
if ( v.attribute ) {

View File

@ -0,0 +1,33 @@
/**
* External dependencies
*/
import { RawHTML, useMemo } from '@wordpress/element';
/**
* Internal dependencies
*/
import { generateSummary } from './utils';
/**
* Summary component.
*
* @param {Object} props Component props.
* @param {string} props.source Source text.
* @param {number} props.maxLength Max length of the summary, using countType.
* @param {string} props.countType One of words, characters_excluding_spaces, or characters_including_spaces.
* @param {string} props.className Class name for rendered component.
*/
export const Summary = ( {
source,
maxLength = 15,
countType = 'words',
className = '',
} ) => {
const summaryText = useMemo( () => {
return generateSummary( source, maxLength, countType );
}, [ source, maxLength, countType ] );
return <RawHTML className={ className }>{ summaryText }</RawHTML>;
};
export default Summary;

View File

@ -0,0 +1,109 @@
/**
* Internal dependencies
*/
import { generateSummary } from '../utils';
describe( 'Summary Component', () => {
describe( 'Test the generateSummary utility', () => {
const testContent =
'<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p><p>Ut enim ad minim veniam, quis <strong>nostrud</strong> exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p><p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p><p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>';
it( 'Default', async () => {
const result = generateSummary( testContent );
expect( result.trim() ).toEqual(
'<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore&hellip;</p>'
);
} );
it( 'No max words - return full description', async () => {
const result = generateSummary( testContent, 100000 );
expect( result.trim() ).toEqual(
'<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>\n<p>Ut enim ad minim veniam, quis <strong>nostrud</strong> exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>\n<p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p>\n<p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>'
);
} );
it( 'Limit to 3 words', async () => {
const result = generateSummary( testContent, 3 );
expect( result.trim() ).toEqual(
'<p>Lorem ipsum dolor&hellip;</p>'
);
} );
it( 'Limit to 1 word', async () => {
const result = generateSummary( testContent, 1 );
expect( result.trim() ).toEqual( '<p>Lorem&hellip;</p>' );
} );
it( 'Limit to 15 characters, including spaces.', async () => {
const result = generateSummary(
testContent,
15,
'characters_including_spaces'
);
expect( result.trim() ).toEqual( '<p>Lorem ipsum dol&hellip;</p>' );
} );
it( 'Limit to 15 characters, excluding spaces.', async () => {
const result = generateSummary(
testContent,
15,
'characters_excluding_spaces'
);
expect( result.trim() ).toEqual(
'<p>Lorem ipsum dolor&hellip;</p>'
);
} );
} );
describe( 'Test the generateSummary utility with HTML tags in strings', () => {
const testContent =
'<p>Lorem <strong class="classname">ipsum</strong> dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.</p>';
it( 'Limit string to 10 characters', async () => {
const result = generateSummary(
testContent,
10,
'characters_excluding_spaces'
);
expect( result.trim() ).toEqual( '<p>Lorem ipsum&hellip;</p>' );
} );
it( 'Limit string to 5 words', async () => {
const result = generateSummary( testContent, 5, 'words' );
expect( result.trim() ).toEqual(
'<p>Lorem ipsum dolor sit amet&hellip;</p>'
);
} );
it( 'First paragraph only - tags are not stripped.', async () => {
const result = generateSummary( testContent, 9999, 'words' );
expect( result.trim() ).toEqual(
'<p>Lorem <strong class="classname">ipsum</strong> dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.</p>'
);
} );
} );
describe( 'Test the generateSummary utility with special chars', () => {
const testContent =
'<p>我不知道这是否行得通。</p><p>我是用中文写的说明,因此我们可以测试如何修剪产品摘要中的单词。</p>';
it( 'Default', async () => {
const result = generateSummary(
testContent,
15,
'characters_excluding_spaces'
);
expect( result.trim() ).toEqual( '<p>我不知道这是否行得通。</p>' );
} );
it( 'Limit to 3 words', async () => {
const result = generateSummary(
testContent,
3,
'characters_excluding_spaces'
);
expect( result.trim() ).toEqual( '<p>我不知&hellip;</p>' );
} );
} );
} );

View File

@ -0,0 +1,131 @@
/**
* External dependencies
*/
import { count } from '@wordpress/wordcount';
import { autop } from '@wordpress/autop';
/**
* Generates the summary text from a string of text.
*a
* @param {string} source Source text.
* @param {number} maxLength Limit number of countType returned if text has multiple paragraphs.
* @param {string} countType What is being counted. One of words, characters_excluding_spaces, or characters_including_spaces.
* @return {string} Generated summary.
*/
export const generateSummary = (
source,
maxLength = 15,
countType = 'words'
) => {
const sourceWithParagraphs = autop( source );
const sourceWordCount = count( sourceWithParagraphs, countType );
if ( sourceWordCount <= maxLength ) {
return sourceWithParagraphs;
}
const firstParagraph = getFirstParagraph( sourceWithParagraphs );
const firstParagraphWordCount = count( firstParagraph, countType );
if ( firstParagraphWordCount <= maxLength ) {
return firstParagraph;
}
if ( countType === 'words' ) {
return trimWords( firstParagraph, maxLength );
}
return trimCharacters(
firstParagraph,
maxLength,
countType === 'characters_including_spaces'
);
};
/**
* Get first paragraph from some HTML text, or return whole string.
*
* @param {string} source Source text.
* @return {string} First paragraph found in string.
*/
const getFirstParagraph = ( source ) => {
const pIndex = source.indexOf( '</p>' );
if ( pIndex === -1 ) {
return source;
}
return source.substr( 0, pIndex + 4 );
};
/**
* Remove HTML tags from a string.
*
* @param {string} htmlString String to remove tags from.
* @return {string} Plain text string.
*/
const removeTags = ( htmlString ) => {
const tagsRegExp = /<\/?[a-z][^>]*?>/gi;
return htmlString.replace( tagsRegExp, '' );
};
/**
* Remove trailing punctuation and append some characters to a string.
*
* @param {string} text Text to append to.
* @param {string} moreText Text to append.
* @return {string} String with appended characters.
*/
const appendMoreText = ( text, moreText ) => {
return text.replace( /[\s|\.\,]+$/i, '' ) + moreText;
};
/**
* Limit words in string and returned trimmed version.
*
* @param {string} text Text to trim.
* @param {number} maxLength Number of countType to limit to.
* @param {string} moreText Appended to the trimmed string.
* @return {string} Trimmed string.
*/
const trimWords = ( text, maxLength, moreText = '&hellip;' ) => {
const textToTrim = removeTags( text );
const trimmedText = textToTrim
.split( ' ' )
.splice( 0, maxLength )
.join( ' ' );
return autop( appendMoreText( trimmedText, moreText ) );
};
/**
* Limit characters in string and returned trimmed version.
*
* @param {string} text Text to trim.
* @param {number} maxLength Number of countType to limit to.
* @param {boolean} includeSpaces Should spaces be included in the count.
* @param {string} moreText Appended to the trimmed string.
* @return {string} Trimmed string.
*/
const trimCharacters = (
text,
maxLength,
includeSpaces = true,
moreText = '&hellip;'
) => {
const textToTrim = removeTags( text );
const trimmedText = textToTrim.slice( 0, maxLength );
if ( includeSpaces ) {
return autop( appendMoreText( trimmedText, moreText ) );
}
const matchSpaces = trimmedText.match( /([\s]+)/g );
const spaceCount = matchSpaces ? matchSpaces.length : 0;
const trimmedTextExcludingSpaces = textToTrim.slice(
0,
maxLength + spaceCount
);
return autop( appendMoreText( trimmedTextExcludingSpaces, moreText ) );
};

View File

@ -39,7 +39,8 @@ const getAmountFromRawPrice = ( priceObject, currency ) => {
const CartLineItemRow = ( { lineItem } ) => {
const {
name = '',
summary = '',
short_description: shortDescription = '',
description: fullDescription = '',
low_stock_remaining: lowStockRemaining = null,
quantity_limit: quantityLimit = 99,
permalink = '',
@ -108,7 +109,11 @@ const CartLineItemRow = ( { lineItem } ) => {
disabled={ isPendingDelete }
/>
<ProductLowStockBadge lowStockRemaining={ lowStockRemaining } />
<ProductMetadata summary={ summary } variation={ variation } />
<ProductMetadata
shortDescription={ shortDescription }
fullDescription={ fullDescription }
variation={ variation }
/>
</td>
<td className="wc-block-cart-item__quantity">
<QuantitySelector

View File

@ -22,7 +22,8 @@ const CheckoutOrderSummaryItem = ( { cartItem } ) => {
permalink,
prices,
quantity,
summary,
short_description: shortDescription,
description: fullDescription,
variation,
} = cartItem;
@ -60,7 +61,11 @@ const CheckoutOrderSummaryItem = ( { cartItem } ) => {
/>
</div>
<ProductLowStockBadge lowStockRemaining={ lowStockRemaining } />
<ProductMetadata summary={ summary } variation={ variation } />
<ProductMetadata
shortDescription={ shortDescription }
fullDescription={ fullDescription }
variation={ variation }
/>
</div>
</div>
);

View File

@ -316,7 +316,7 @@ const FeaturedProduct = ( {
<div
className="wc-block-featured-product__description"
dangerouslySetInnerHTML={ {
__html: product.summary,
__html: product.short_description,
} }
/>
) }

View File

@ -36,7 +36,6 @@ const state = {
id: 19,
quantity: 1,
name: 'Album',
summary: '<p>This is a simple, virtual product.</p>',
short_description: '<p>This is a simple, virtual product.</p>',
description:
'<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum sagittis orci ac odio dictum tincidunt. Donec ut metus leo. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed luctus, dui eu sagittis sodales, nulla nibh sagittis augue, vel porttitor diam enim non metus. Vestibulum aliquam augue neque. Phasellus tincidunt odio eget ullamcorper efficitur. Cras placerat ut turpis pellentesque vulputate. Nam sed consequat tortor. Curabitur finibus sapien dolor. Ut eleifend tellus nec erat pulvinar dignissim. Nam non arcu purus. Vivamus et massa massa.</p>',
@ -77,7 +76,6 @@ const state = {
id: 11,
quantity: 1,
name: 'Beanie',
summary: '<p>This is a simple product.</p>',
short_description: '<p>This is a simple product.</p>',
description:
'<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>',

View File

@ -25,10 +25,6 @@ export const previewCart = {
id: 1,
quantity: 2,
name: __( 'Beanie', 'woo-gutenberg-products-block' ),
summary: __(
'Warm hat for winter',
'woo-gutenberg-products-block'
),
short_description: __(
'Warm hat for winter',
'woo-gutenberg-products-block'
@ -98,10 +94,6 @@ export const previewCart = {
id: 2,
quantity: 1,
name: __( 'Cap', 'woo-gutenberg-products-block' ),
summary: __(
'Lightweight baseball cap',
'woo-gutenberg-products-block'
),
short_description: __(
'Lightweight baseball cap',
'woo-gutenberg-products-block'

View File

@ -16,7 +16,6 @@ export const previewProducts = [
variation: '',
permalink: 'https://example.org',
sku: 'wp-pennant',
summary: shortDescription,
short_description: shortDescription,
description:
'Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.',

View File

@ -4745,7 +4745,6 @@
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/@wordpress/autop/-/autop-2.7.0.tgz",
"integrity": "sha512-XLNyxlsdXPQMTHl3NnR1nbsggcf12euBwpp6d6qdVLT3+s2FtU2dg9dMVJg/OHKd3/QgA6W+k7yjcyME2aOAFQ==",
"dev": true,
"requires": {
"@babel/runtime": "^7.9.2"
}
@ -7399,7 +7398,6 @@
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/@wordpress/wordcount/-/wordcount-2.8.0.tgz",
"integrity": "sha512-veM3WRmz6mijEjn0kwn2pt3CASIKUxezUCzDe60i9I8spaYAL1hQiykrXx5U3x/hehQaOw2enoqPPdtHIm1XHQ==",
"dev": true,
"requires": {
"@babel/runtime": "^7.9.2",
"lodash": "^4.17.15"

View File

@ -152,7 +152,9 @@
"@stripe/react-stripe-js": "1.1.2",
"@stripe/stripe-js": "1.5.0",
"@woocommerce/components": "4.0.0",
"@wordpress/autop": "^2.7.0",
"@wordpress/notices": "2.0.0",
"@wordpress/wordcount": "^2.8.0",
"classnames": "2.2.6",
"compare-versions": "3.6.0",
"config": "3.3.1",

View File

@ -155,6 +155,13 @@ class Assets {
'checkoutAllowsGuest' => 'yes' === get_option( 'woocommerce_enable_guest_checkout' ),
'checkoutAllowsSignup' => 'yes' === get_option( 'woocommerce_enable_signup_and_login_from_checkout' ),
'baseLocation' => wc_get_base_location(),
/*
* translators: If your word count is based on single characters (e.g. East Asian characters),
* enter 'characters_excluding_spaces' or 'characters_including_spaces'. Otherwise, enter 'words'.
* Do not translate into your own language.
*/
'wordCountType' => _x( 'words', 'Word count type. Do not translate!', 'woo-gutenberg-products-block' ),
]
);
}

View File

@ -9,8 +9,6 @@ namespace Automattic\WooCommerce\Blocks\StoreApi\Schemas;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\ProductSummary;
/**
* CartItemSchema class.
*
@ -77,12 +75,6 @@ class CartItemSchema extends ProductSchema {
'context' => [ 'view', 'edit' ],
'readonly' => true,
],
'summary' => [
'description' => __( 'A short summary (or excerpt from the full description) for the product in HTML format.', 'woo-gutenberg-products-block' ),
'type' => 'string',
'context' => [ 'view', 'edit' ],
'readonly' => true,
],
'short_description' => [
'description' => __( 'Product short description in HTML format.', 'woo-gutenberg-products-block' ),
'type' => 'string',
@ -293,7 +285,6 @@ class CartItemSchema extends ProductSchema {
'quantity' => wc_stock_amount( $cart_item['quantity'] ),
'quantity_limit' => $this->get_product_quantity_limit( $product ),
'name' => $this->prepare_html_response( $product->get_title() ),
'summary' => $this->prepare_html_response( ( new ProductSummary( $product ) )->get_summary( 150 ) ),
'short_description' => $this->prepare_html_response( wc_format_content( $product->get_short_description() ) ),
'description' => $this->prepare_html_response( wc_format_content( $product->get_description() ) ),
'sku' => $this->prepare_html_response( $product->get_sku() ),

View File

@ -9,8 +9,6 @@ namespace Automattic\WooCommerce\Blocks\StoreApi\Schemas;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\ProductSummary;
/**
* ProductSchema class.
*
@ -76,11 +74,6 @@ class ProductSchema extends AbstractSchema {
'context' => [ 'view', 'edit' ],
'readonly' => true,
],
'summary' => [
'description' => __( 'A short summary (or excerpt from the full description) for the product in HTML format.', 'woo-gutenberg-products-block' ),
'type' => 'string',
'context' => [ 'view', 'edit' ],
],
'short_description' => [
'description' => __( 'Product short description in HTML format.', 'woo-gutenberg-products-block' ),
'type' => 'string',
@ -245,7 +238,6 @@ class ProductSchema extends AbstractSchema {
'variation' => $this->prepare_html_response( $product->is_type( 'variation' ) ? wc_get_formatted_variation( $product, true, true, false ) : '' ),
'permalink' => $product->get_permalink(),
'sku' => $this->prepare_html_response( $product->get_sku() ),
'summary' => $this->prepare_html_response( ( new ProductSummary( $product ) )->get_summary( 150 ) ),
'short_description' => $this->prepare_html_response( wc_format_content( $product->get_short_description() ) ),
'description' => $this->prepare_html_response( wc_format_content( $product->get_description() ) ),
'on_sale' => $product->is_on_sale(),

View File

@ -1,109 +0,0 @@
<?php
/**
* Helper class to format a short summary of content for a product.
*
* @package WooCommerce/Blocks
*/
namespace Automattic\WooCommerce\Blocks\StoreApi\Utilities;
defined( 'ABSPATH' ) || exit;
/**
* Product Summary class.
*/
final class ProductSummary {
/**
* Reference to product object.
*
* @var \WC_Product
*/
private $product = '';
/**
* Constructor.
*
* @param \WC_Product $product The product to generate a summary for.
*/
public function __construct( \WC_Product $product ) {
$this->product = $product;
}
/**
* Return the formatted summary.
*
* @param int $max_words Word limit for summary.
* @return string
*/
public function get_summary( $max_words = 25 ) {
$summary = $this->get_content_for_summary();
if ( $max_words && $this->get_word_count( $summary ) > $max_words ) {
$summary = $this->generate_summary( $summary, $max_words );
}
return \wc_format_content( $summary );
}
/**
* Get description to base summary on from the product object.
*
* @return string
*/
private function get_content_for_summary() {
$content = $this->product->get_short_description();
if ( ! $content ) {
$content = $this->product->get_description();
}
return $content;
}
/**
* Get the word count. Based on the logic in `wp_trim_words`.
*
* @param string $content HTML Content.
* @return int Length
*/
private function get_word_count( $content ) {
$content = wp_strip_all_tags( $content );
/*
* translators: If your word count is based on single characters (e.g. East Asian characters),
* enter 'characters_excluding_spaces' or 'characters_including_spaces'. Otherwise, enter 'words'.
* Do not translate into your own language.
*/
$type = _x( 'words', 'Word count type. Do not translate!' ); // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
if ( strpos( $type, 'characters' ) === 0 && preg_match( '/^utf\-?8$/i', get_option( 'blog_charset' ) ) ) {
$content = trim( preg_replace( "/[\n\r\t ]+/", ' ', $content ), ' ' );
preg_match_all( '/./u', $content, $words_array );
$words_array = $words_array[0];
} else {
$words_array = preg_split( "/[\n\r\t ]+/", $content );
}
return count( array_filter( $words_array ) );
}
/**
* Get the first paragraph, or a short excerpt, from some content.
*
* @param string $content HTML Content.
* @param int $max_words Maximum allowed words for summary.
* @return string
*/
private function generate_summary( $content, $max_words ) {
$content_p = \wpautop( $content );
// This gets the first paragraph, if <p> tags are found. Otherwise returns the entire string.
$paragraph = \strstr( $content_p, '</p>' ) ? \substr( $content_p, 0, \strpos( $content_p, '</p>' ) + 4 ) : $content;
if ( $this->get_word_count( $paragraph ) > $max_words ) {
return \wp_trim_words( $paragraph, $max_words );
}
return $paragraph;
}
}

View File

@ -1,68 +0,0 @@
<?php
/**
* Utility Tests.
*
* @package WooCommerce\Blocks\Tests
*/
namespace Automattic\WooCommerce\Blocks\Tests\RestApi\Utilities;
use PHPUnit\Framework\TestCase;
use \WC_Helper_Product as ProductHelper;
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\ProductSummary;
/**
* ProductSummary Utility Tests.
*/
class ProductSummaryTests extends TestCase {
/**
* Tear down method.
*/
public function tearDown() {
remove_filter( 'gettext_with_context', array( $this, 'gettext_with_context_callback' ), 10, 3 );
parent::tearDown();
}
/**
* Test that we can get a valid summary.
*/
public function test_get_summary() {
$product = new \WC_Product();
$product->set_description( '<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p><p>Ut enim ad minim veniam, quis <strong>nostrud</strong> exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p><p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p><p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>' );
$class = new ProductSummary( $product );
// 25 word limit should return 1st para.
$this->assertEquals( "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>\n", $class->get_summary() );
// Large limit, should return full description.
$this->assertEquals( "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>\n<p>Ut enim ad minim veniam, quis <strong>nostrud</strong> exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>\n<p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p>\n<p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>\n", $class->get_summary( 1000 ) );
// Should return 3 words.
$this->assertEquals( "<p>Lorem ipsum dolor&hellip;</p>\n", $class->get_summary( 3 ) );
// Should return 1 word.
$this->assertEquals( "<p>Lorem&hellip;</p>\n", $class->get_summary( 1 ) );
// Test for languages where characters are words.
add_filter( 'gettext_with_context', array( $this, 'gettext_with_context_callback' ), 10, 3 );
$product->set_description( '<p>我不知道这是否行得通。</p><p>我是用中文写的说明,因此我们可以测试如何修剪产品摘要中的单词。</p>' );
$this->assertEquals( "<p>我不知道这是否行得通。</p>\n", $class->get_summary() );
$this->assertEquals( "<p>我不知&hellip;</p>\n", $class->get_summary( 3 ) );
}
/**
* Adjusts word count type to character based.
*
* @param string $translated Translated string.
* @param string $text Text to translate.
* @param string $context Translation context.
* @return string
*/
public function gettext_with_context_callback( $translated, $text, $context ) {
if ( $text === 'words' && $context === 'Word count type. Do not translate!' ) {
return 'characters';
}
return $translated;
}
}