Merge branch 'trunk' into update/ai-product-endpoint-last-product-param
This commit is contained in:
commit
1f1ddd17d4
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: tweak
|
||||
|
||||
[Product Block Editor]: minor Summary block enhancements
|
|
@ -26,7 +26,7 @@ import { SummaryAttributes } from './types';
|
|||
import { ALIGNMENT_CONTROLS } from './constants';
|
||||
import { ProductEditorBlockEditProps } from '../../../types';
|
||||
|
||||
export function Edit( {
|
||||
export function SummaryBlockEdit( {
|
||||
attributes,
|
||||
setAttributes,
|
||||
context,
|
||||
|
@ -36,7 +36,7 @@ export function Edit( {
|
|||
style: { direction },
|
||||
} );
|
||||
const contentId = useInstanceId(
|
||||
Edit,
|
||||
SummaryBlockEdit,
|
||||
'wp-block-woocommerce-product-summary-field__content'
|
||||
);
|
||||
const [ summary, setSummary ] = useEntityProp< string >(
|
||||
|
@ -88,23 +88,30 @@ export function Edit( {
|
|||
|
||||
<BaseControl
|
||||
id={ contentId.toString() }
|
||||
label={ createInterpolateElement(
|
||||
label || __( 'Summary', 'woocommerce' ),
|
||||
{
|
||||
optional: (
|
||||
<span className="woocommerce-product-form__optional-input">
|
||||
{ __( '(OPTIONAL)', 'woocommerce' ) }
|
||||
</span>
|
||||
),
|
||||
}
|
||||
) }
|
||||
label={
|
||||
typeof label === 'undefined'
|
||||
? createInterpolateElement(
|
||||
__( 'Summary', 'woocommerce' ),
|
||||
{
|
||||
optional: (
|
||||
<span className="woocommerce-product-form__optional-input">
|
||||
{ __(
|
||||
'(OPTIONAL)',
|
||||
'woocommerce'
|
||||
) }
|
||||
</span>
|
||||
),
|
||||
}
|
||||
)
|
||||
: label
|
||||
}
|
||||
help={
|
||||
typeof helpText === 'string'
|
||||
? helpText
|
||||
: __(
|
||||
typeof helpText === 'undefined'
|
||||
? __(
|
||||
"Summarize this product in 1-2 short sentences. We'll show it at the top of the page.",
|
||||
'woocommerce'
|
||||
)
|
||||
: helpText
|
||||
}
|
||||
>
|
||||
<div { ...blockProps }>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* Internal dependencies
|
||||
*/
|
||||
import blockConfiguration from './block.json';
|
||||
import { Edit } from './edit';
|
||||
import { SummaryBlockEdit } from './edit';
|
||||
import { registerProductEditorBlockType } from '../../../utils';
|
||||
|
||||
const { name, ...metadata } = blockConfiguration;
|
||||
|
@ -11,7 +11,7 @@ export { name, metadata };
|
|||
|
||||
export const settings = {
|
||||
example: {},
|
||||
edit: Edit,
|
||||
edit: SummaryBlockEdit,
|
||||
};
|
||||
|
||||
export function init() {
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: dev
|
||||
Comment: Create closeWelcomeModal, goToPageEditor and goToPostEditor utils in PlayWright e2e tests
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: fix
|
||||
Comment: Fixes a bug found in unreleased feature. No changelog needed.
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: fix
|
||||
Comment: Fixes unreleased feature, no changelog required.
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: performance
|
||||
|
||||
Prevent second query used to determine real post count from running if paging isn't being used.
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: add
|
||||
|
||||
Add functionality and UI for downloading log files directly from WC Admin
|
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: fix
|
||||
|
||||
Fixed a vertical alignment issue in the variations selector
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: add
|
||||
|
||||
Display variation upsells from private variable products to users with required permissions.
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: update
|
||||
|
||||
Update product template for external products, hiding shipping and variation tabs.
|
|
@ -182,9 +182,18 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Ensure variation label is vertically centered.
|
||||
div.product form.cart table.variations td select {
|
||||
min-width: 70%; // Fix for Safari.
|
||||
}
|
||||
|
||||
div.product form.cart table.variations td,
|
||||
div.product form.cart table.variations th {
|
||||
// Ensure variation label is vertically centered.
|
||||
line-height: 3.5rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.single_variation_wrap .woocommerce-variation {
|
||||
margin-bottom: var(--wp--style--block-gap);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -569,6 +569,9 @@
|
|||
status : 'new'
|
||||
}
|
||||
});
|
||||
} else {
|
||||
shippingMethodView.model.trigger( 'change:methods' );
|
||||
shippingMethodView.model.trigger( 'saved:methods' );
|
||||
}
|
||||
|
||||
$( document.body ).trigger( 'init_tooltips' );
|
||||
|
@ -629,17 +632,6 @@
|
|||
}
|
||||
} else if ( target === 'wc-modal-shipping-method-settings' ) {
|
||||
event.data.view.possiblyHideFreeShippingRequirements( data );
|
||||
const requiredFields = [ 'woocommerce_free_shipping_title', 'woocommerce_flat_rate_title', 'woocommerce_local_pickup_title' ];
|
||||
const requiredFieldPresent = requiredFields.find( ( field ) => {
|
||||
return data.hasOwnProperty( field ) && field;
|
||||
} );
|
||||
|
||||
if ( requiredFieldPresent ) {
|
||||
const shouldDisable = data[ requiredFieldPresent ].length === 0;
|
||||
const saveButton = document.getElementById( 'btn-ok' );
|
||||
saveButton.disabled = shouldDisable;
|
||||
saveButton.classList.toggle( 'disabled', shouldDisable );
|
||||
}
|
||||
}
|
||||
},
|
||||
onCloseConfigureShippingMethod: function( event, target, post_data, addButtonCalled ) {
|
||||
|
|
|
@ -1603,7 +1603,7 @@ class WC_Product extends WC_Abstract_Legacy_Product {
|
|||
if ( $this->get_parent_id() ) {
|
||||
$parent_product = wc_get_product( $this->get_parent_id() );
|
||||
|
||||
if ( $parent_product && 'publish' !== $parent_product->get_status() ) {
|
||||
if ( $parent_product && 'publish' !== $parent_product->get_status() && ! current_user_can( 'edit_post', $parent_product->get_id() ) ) {
|
||||
$visible = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -149,7 +149,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||
</article>
|
||||
<footer>
|
||||
<div class="inner">
|
||||
<button id="btn-ok" data-status='{{ data.status }}' disabled='{{ data.status === "new" ? "disabled" : "" }}' class="button button-primary button-large {{ data.status === 'new' ? 'disabled' : '' }}">
|
||||
<button id="btn-ok" data-status='{{ data.status }}' class="button button-primary button-large">
|
||||
<div class="wc-backbone-modal-action-{{ data.status === 'new' ? 'active' : 'inactive' }}"><?php esc_html_e( 'Create', 'woocommerce' ); ?></div>
|
||||
<div class="wc-backbone-modal-action-{{ data.status === 'existing' ? 'active' : 'inactive' }}"><?php esc_html_e( 'Save', 'woocommerce' ); ?></div>
|
||||
</button>
|
||||
|
|
|
@ -354,7 +354,7 @@ abstract class WC_REST_CRUD_Controller extends WC_REST_Posts_Controller {
|
|||
$result = $query->query( $query_args );
|
||||
|
||||
$total_posts = $query->found_posts;
|
||||
if ( $total_posts < 1 ) {
|
||||
if ( $total_posts < 1 && isset( $query_args['paged'] ) && absint( $query_args['paged'] ) > 1 ) {
|
||||
// Out-of-bounds, run the query again without LIMIT for total count.
|
||||
unset( $query_args['paged'] );
|
||||
$count_query = new WP_Query();
|
||||
|
|
|
@ -367,7 +367,7 @@ abstract class WC_REST_Posts_Controller extends WC_REST_Controller {
|
|||
$page = (int) $query_args['paged'];
|
||||
$total_posts = $posts_query->found_posts;
|
||||
|
||||
if ( $total_posts < 1 ) {
|
||||
if ( $total_posts < 1 && $page > 1 ) {
|
||||
// Out-of-bounds, run the query again without LIMIT for total count.
|
||||
unset( $query_args['paged'] );
|
||||
$count_query = new WP_Query();
|
||||
|
|
|
@ -15,7 +15,7 @@ $settings = array(
|
|||
'title' => __( 'Name', 'woocommerce' ),
|
||||
'type' => 'text',
|
||||
'description' => __( 'Your customers will see the name of this shipping method during checkout.', 'woocommerce' ),
|
||||
'default' => '',
|
||||
'default' => __( 'Flat rate', 'woocommerce' ),
|
||||
'placeholder' => __( 'e.g. Standard national', 'woocommerce' ),
|
||||
'desc_tip' => true,
|
||||
),
|
||||
|
|
|
@ -112,7 +112,7 @@ class WC_Shipping_Free_Shipping extends WC_Shipping_Method {
|
|||
'title' => __( 'Name', 'woocommerce' ),
|
||||
'type' => 'text',
|
||||
'description' => __( 'Your customers will see the name of this shipping method during checkout.', 'woocommerce' ),
|
||||
'default' => '',
|
||||
'default' => $this->method_title,
|
||||
'placeholder' => __( 'e.g. Free shipping', 'woocommerce' ),
|
||||
'desc_tip' => true,
|
||||
),
|
||||
|
|
|
@ -108,7 +108,7 @@ class WC_Shipping_Local_Pickup extends WC_Shipping_Method {
|
|||
'title' => __( 'Name', 'woocommerce' ),
|
||||
'type' => 'text',
|
||||
'description' => __( 'Your customers will see the name of this shipping method during checkout.', 'woocommerce' ),
|
||||
'default' => '',
|
||||
'default' => __( 'Local pickup', 'woocommerce' ),
|
||||
'placeholder' => __( 'e.g. Local pickup', 'woocommerce' ),
|
||||
'desc_tip' => true,
|
||||
),
|
||||
|
|
|
@ -93,7 +93,7 @@ class Controller extends GenericController implements ExportableInterface {
|
|||
$result = $query->query( $query_args );
|
||||
|
||||
$total_posts = $query->found_posts;
|
||||
if ( $total_posts < 1 ) {
|
||||
if ( $total_posts < 1 && isset( $query_args['paged'] ) && absint( $query_args['paged'] ) > 1 ) {
|
||||
// Out-of-bounds, run the query again without LIMIT for total count.
|
||||
unset( $query_args['paged'] );
|
||||
$count_query = new \WP_Query();
|
||||
|
|
|
@ -179,6 +179,15 @@ class File {
|
|||
return fclose( $this->stream );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full absolute path of the file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_path(): string {
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the file, with extension, but without full path.
|
||||
*
|
||||
|
|
|
@ -4,6 +4,7 @@ declare( strict_types = 1 );
|
|||
namespace Automattic\WooCommerce\Internal\Admin\Logging\FileV2;
|
||||
|
||||
use Automattic\Jetpack\Constants;
|
||||
use PclZip;
|
||||
use WC_Cache_Helper;
|
||||
use WP_Error;
|
||||
|
||||
|
@ -345,6 +346,68 @@ class FileController {
|
|||
return $deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream a single file to the browser without zipping it first.
|
||||
*
|
||||
* @param string $file_id A file ID (file basename without the hash).
|
||||
*
|
||||
* @return WP_Error|void Only returns something if there is an error.
|
||||
*/
|
||||
public function export_single_file( $file_id ) {
|
||||
$file = $this->get_file_by_id( $file_id );
|
||||
|
||||
if ( is_wp_error( $file ) ) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
$file_name = $file->get_file_id() . '.log';
|
||||
$exporter = new FileExporter( $file->get_path(), $file_name );
|
||||
|
||||
return $exporter->emit_file();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a zip archive of log files and stream it to the browser.
|
||||
*
|
||||
* @param array $file_ids An array of file IDs (file basename without the hash).
|
||||
*
|
||||
* @return WP_Error|void Only returns something if there is an error.
|
||||
*/
|
||||
public function export_multiple_files( array $file_ids ) {
|
||||
$files = $this->get_files_by_id( $file_ids );
|
||||
|
||||
if ( count( $files ) < 1 ) {
|
||||
return new WP_Error(
|
||||
'wc_logs_invalid_file',
|
||||
__( 'Could not access the specified files.', 'woocommerce' )
|
||||
);
|
||||
}
|
||||
|
||||
$temp_dir = get_temp_dir();
|
||||
|
||||
if ( ! is_dir( $temp_dir ) || ! wp_is_writable( $temp_dir ) ) {
|
||||
return new WP_Error(
|
||||
'wc_logs_invalid_directory',
|
||||
__( 'Could not write to the temp directory. Try downloading files one at a time instead.', 'woocommerce' )
|
||||
);
|
||||
}
|
||||
|
||||
require_once ABSPATH . 'wp-admin/includes/class-pclzip.php';
|
||||
|
||||
$path = trailingslashit( $temp_dir ) . 'woocommerce_logs_' . gmdate( 'Y-m-d_H-i-s' ) . '.zip';
|
||||
$file_paths = array_map(
|
||||
fn( $file ) => $file->get_path(),
|
||||
$files
|
||||
);
|
||||
$archive = new PclZip( $path );
|
||||
|
||||
$archive->create( $file_paths, PCLZIP_OPT_REMOVE_ALL_PATH );
|
||||
|
||||
$exporter = new FileExporter( $path );
|
||||
|
||||
return $exporter->emit_file();
|
||||
}
|
||||
|
||||
/**
|
||||
* Search within a set of log files for a particular string.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
<?php
|
||||
declare( strict_types=1 );
|
||||
|
||||
namespace Automattic\WooCommerce\Internal\Admin\Logging\FileV2;
|
||||
|
||||
use WP_Error;
|
||||
use WP_Filesystem_Direct;
|
||||
|
||||
/**
|
||||
* FileExport class.
|
||||
*/
|
||||
class FileExporter {
|
||||
/**
|
||||
* The number of bytes per read while streaming the file.
|
||||
*
|
||||
* @const int
|
||||
*/
|
||||
private const CHUNK_SIZE = 4 * KB_IN_BYTES;
|
||||
|
||||
/**
|
||||
* The absolute path of the file.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $path;
|
||||
|
||||
/**
|
||||
* A name of the file to send to the browser rather than the filename part of the path.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $alternate_filename;
|
||||
|
||||
/**
|
||||
* Class FileExporter.
|
||||
*
|
||||
* @param string $path The absolute path of the file.
|
||||
* @param string $alternate_filename Optional. The name of the file to send to the browser rather than the filename
|
||||
* part of the path.
|
||||
*/
|
||||
public function __construct( string $path, string $alternate_filename = '' ) {
|
||||
global $wp_filesystem;
|
||||
if ( ! $wp_filesystem instanceof WP_Filesystem_Direct ) {
|
||||
WP_Filesystem();
|
||||
}
|
||||
|
||||
$this->path = $path;
|
||||
$this->alternate_filename = $alternate_filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure PHP and stream the file to the browser.
|
||||
*
|
||||
* @return WP_Error|void Only returns something if there is an error.
|
||||
*/
|
||||
public function emit_file() {
|
||||
global $wp_filesystem;
|
||||
if ( ! $wp_filesystem->is_file( $this->path ) || ! $wp_filesystem->is_readable( $this->path ) ) {
|
||||
return new WP_Error(
|
||||
'wc_logs_invalid_file',
|
||||
__( 'Could not access file.', 'woocommerce' )
|
||||
);
|
||||
}
|
||||
|
||||
// These configuration tweaks are copied from WC_CSV_Exporter::send_headers().
|
||||
// phpcs:disable WordPress.PHP.NoSilencedErrors.Discouraged
|
||||
if ( function_exists( 'gc_enable' ) ) {
|
||||
gc_enable(); // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctions.gc_enableFound
|
||||
}
|
||||
if ( function_exists( 'apache_setenv' ) ) {
|
||||
@apache_setenv( 'no-gzip', '1' ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_apache_setenv
|
||||
}
|
||||
@ini_set( 'zlib.output_compression', 'Off' ); // phpcs:ignore WordPress.PHP.IniSet.Risky
|
||||
@ini_set( 'output_buffering', 'Off' ); // phpcs:ignore WordPress.PHP.IniSet.Risky
|
||||
@ini_set( 'output_handler', '' ); // phpcs:ignore WordPress.PHP.IniSet.Risky
|
||||
ignore_user_abort( true );
|
||||
wc_set_time_limit();
|
||||
wc_nocache_headers();
|
||||
// phpcs:enable WordPress.PHP.NoSilencedErrors.Discouraged
|
||||
|
||||
$this->send_headers();
|
||||
$this->send_contents();
|
||||
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send HTTP headers at the beginning of a file.
|
||||
*
|
||||
* Modeled on WC_CSV_Exporter::send_headers().
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function send_headers(): void {
|
||||
header( 'Content-Type: text/plain; charset=utf-8' );
|
||||
header( 'Content-Disposition: attachment; filename=' . $this->get_filename() );
|
||||
header( 'Pragma: no-cache' );
|
||||
header( 'Expires: 0' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the contents of the file.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function send_contents(): void {
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fopen -- No suitable alternative.
|
||||
$stream = fopen( $this->path, 'rb' );
|
||||
|
||||
while ( is_resource( $stream ) && ! feof( $stream ) ) {
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fread -- No suitable alternative.
|
||||
$chunk = fread( $stream, self::CHUNK_SIZE );
|
||||
|
||||
if ( is_string( $chunk ) ) {
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Outputting to file.
|
||||
echo $chunk;
|
||||
}
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fclose -- No suitable alternative.
|
||||
fclose( $stream );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the file that will be sent to the browser.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_filename(): string {
|
||||
if ( $this->alternate_filename ) {
|
||||
return $this->alternate_filename;
|
||||
}
|
||||
|
||||
return basename( $this->path );
|
||||
}
|
||||
}
|
|
@ -67,6 +67,7 @@ class FileListTable extends WP_List_Table {
|
|||
*/
|
||||
protected function get_bulk_actions(): array {
|
||||
return array(
|
||||
'export' => esc_html__( 'Download', 'woocommerce' ),
|
||||
'delete' => esc_html__( 'Delete permanently', 'woocommerce' ),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -196,22 +196,28 @@ class PageController {
|
|||
$rotations = $this->file_controller->get_file_rotations( $file->get_file_id() );
|
||||
$rotation_url_base = add_query_arg( 'view', 'single_file', $this->get_logs_tab_url() );
|
||||
|
||||
$delete_url = add_query_arg(
|
||||
$download_url = add_query_arg(
|
||||
array(
|
||||
'action' => 'export',
|
||||
'file_id' => array( $file->get_file_id() ),
|
||||
),
|
||||
wp_nonce_url( $this->get_logs_tab_url(), 'bulk-log-files' )
|
||||
);
|
||||
$delete_url = add_query_arg(
|
||||
array(
|
||||
'action' => 'delete',
|
||||
'file_id' => array( $file->get_file_id() ),
|
||||
),
|
||||
wp_nonce_url( $this->get_logs_tab_url(), 'bulk-log-files' )
|
||||
);
|
||||
|
||||
$stream = $file->get_stream();
|
||||
$line_number = 1;
|
||||
|
||||
$delete_confirmation_js = sprintf(
|
||||
"return window.confirm( '%s' )",
|
||||
esc_js( __( 'Delete this log file permanently?', 'woocommerce' ) )
|
||||
);
|
||||
|
||||
$stream = $file->get_stream();
|
||||
$line_number = 1;
|
||||
|
||||
?>
|
||||
<header id="logs-header" class="wc-logs-header">
|
||||
<h2>
|
||||
|
@ -255,6 +261,14 @@ class PageController {
|
|||
</nav>
|
||||
<?php endif; ?>
|
||||
<div class="wc-logs-single-file-actions">
|
||||
<?php
|
||||
// Download button.
|
||||
printf(
|
||||
'<a href="%1$s" class="button button-secondary">%2$s</a>',
|
||||
esc_url( $download_url ),
|
||||
esc_html__( 'Download', 'woocommerce' )
|
||||
);
|
||||
?>
|
||||
<?php
|
||||
// Delete button.
|
||||
printf(
|
||||
|
@ -464,6 +478,17 @@ class PageController {
|
|||
}
|
||||
|
||||
switch ( $action ) {
|
||||
case 'export':
|
||||
if ( 1 === count( $file_ids ) ) {
|
||||
$export_error = $this->file_controller->export_single_file( reset( $file_ids ) );
|
||||
} else {
|
||||
$export_error = $this->file_controller->export_multiple_files( $file_ids );
|
||||
}
|
||||
|
||||
if ( is_wp_error( $export_error ) ) {
|
||||
wp_die( wp_kses_post( $export_error ) );
|
||||
}
|
||||
break;
|
||||
case 'delete':
|
||||
$deleted = $this->file_controller->delete_files( $file_ids );
|
||||
$sendback = add_query_arg( 'deleted', $deleted, $sendback );
|
||||
|
|
|
@ -103,6 +103,18 @@ class SimpleProductTemplate extends AbstractProductFormTemplate implements Produ
|
|||
),
|
||||
)
|
||||
);
|
||||
$shipping_hide_conditions = array();
|
||||
if ( Features::is_enabled( 'product-grouped' ) ) {
|
||||
$shipping_hide_conditions[] = array(
|
||||
'expression' => 'editedProduct.type === "grouped"',
|
||||
);
|
||||
}
|
||||
if ( Features::is_enabled( 'product-external-affiliate' ) ) {
|
||||
$shipping_hide_conditions[] = array(
|
||||
'expression' => 'editedProduct.type === "external"',
|
||||
);
|
||||
}
|
||||
|
||||
$this->add_group(
|
||||
array(
|
||||
'id' => $this::GROUP_IDS['SHIPPING'],
|
||||
|
@ -110,21 +122,22 @@ class SimpleProductTemplate extends AbstractProductFormTemplate implements Produ
|
|||
'attributes' => array(
|
||||
'title' => __( 'Shipping', 'woocommerce' ),
|
||||
),
|
||||
'hideConditions' => Features::is_enabled( 'product-grouped' ) ? array(
|
||||
array(
|
||||
'expression' => 'editedProduct.type === "grouped"',
|
||||
),
|
||||
) : null,
|
||||
'hideConditions' => $shipping_hide_conditions,
|
||||
)
|
||||
);
|
||||
if ( Features::is_enabled( 'product-variation-management' ) ) {
|
||||
$this->add_group(
|
||||
array(
|
||||
'id' => $this::GROUP_IDS['VARIATIONS'],
|
||||
'order' => 50,
|
||||
'attributes' => array(
|
||||
'id' => $this::GROUP_IDS['VARIATIONS'],
|
||||
'order' => 50,
|
||||
'attributes' => array(
|
||||
'title' => __( 'Variations', 'woocommerce' ),
|
||||
),
|
||||
'hideConditions' => Features::is_enabled( 'product-external-affiliate' ) ? array(
|
||||
array(
|
||||
'expression' => 'editedProduct.type === "external"',
|
||||
),
|
||||
) : null,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,36 +1,11 @@
|
|||
const { test, expect } = require( '@playwright/test' );
|
||||
const wcApi = require( '@woocommerce/woocommerce-rest-api' ).default;
|
||||
const { goToPostEditor } = require( '../../utils/editor' );
|
||||
|
||||
// need to figure out whether tests are being run on a mac
|
||||
const macOS = process.platform === 'darwin';
|
||||
const cmdKeyCombo = macOS ? 'Meta+k' : 'Control+k';
|
||||
|
||||
const goToPostEditor = async ( { page } ) => {
|
||||
await page.goto( 'wp-admin/post-new.php' );
|
||||
|
||||
const welcomeModalVisible =
|
||||
await test.step( 'Check if the Welcome modal appeared', async () => {
|
||||
return await page
|
||||
.getByRole( 'heading', {
|
||||
name: 'Welcome to the block editor',
|
||||
} )
|
||||
.isVisible();
|
||||
} );
|
||||
|
||||
if ( welcomeModalVisible ) {
|
||||
await test.step( 'Welcome modal appeared. Close it.', async () => {
|
||||
await page
|
||||
.getByRole( 'document' )
|
||||
.getByRole( 'button', { name: 'Close' } )
|
||||
.click();
|
||||
} );
|
||||
} else {
|
||||
await test.step( 'Welcome modal did not appear.', async () => {
|
||||
// do nothing.
|
||||
} );
|
||||
}
|
||||
};
|
||||
|
||||
const clickOnCommandPaletteOption = async ( { page, optionName } ) => {
|
||||
// Press `Ctrl` + `K` to open the command palette.
|
||||
await page.keyboard.press( cmdKeyCombo );
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
const { test, expect, request } = require( '@playwright/test' );
|
||||
const { admin } = require( '../../test-data/data' );
|
||||
const { goToPageEditor } = require( '../../utils/editor' );
|
||||
|
||||
const pageTitle = `Page-${ new Date().getTime().toString() }`;
|
||||
|
||||
|
@ -32,17 +33,7 @@ test.describe( 'Can create a new page', () => {
|
|||
} );
|
||||
|
||||
test( 'can create new page', async ( { page } ) => {
|
||||
await page.goto( 'wp-admin/post-new.php?post_type=page' );
|
||||
|
||||
const welcomeModalVisible = await page
|
||||
.getByRole( 'heading', {
|
||||
name: 'Welcome to the block editor',
|
||||
} )
|
||||
.isVisible();
|
||||
|
||||
if ( welcomeModalVisible ) {
|
||||
await page.getByRole( 'button', { name: 'Close' } ).click();
|
||||
}
|
||||
await goToPageEditor( { page } );
|
||||
|
||||
await page
|
||||
.getByRole( 'textbox', { name: 'Add Title' } )
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
const { test, expect, request } = require( '@playwright/test' );
|
||||
const { admin } = require( '../../test-data/data' );
|
||||
const { goToPostEditor } = require( '../../utils/editor' );
|
||||
|
||||
const postTitle = `Post-${ new Date().getTime().toString() }`;
|
||||
|
||||
|
@ -33,31 +34,7 @@ test.describe( 'Can create a new post', () => {
|
|||
} );
|
||||
|
||||
test( 'can create new post', async ( { page } ) => {
|
||||
await page.goto( 'wp-admin/post-new.php' );
|
||||
|
||||
const welcomeModalVisible = await test.step(
|
||||
'Check if the Welcome modal appeared',
|
||||
async () => {
|
||||
return await page
|
||||
.getByRole( 'heading', {
|
||||
name: 'Welcome to the block editor',
|
||||
} )
|
||||
.isVisible();
|
||||
}
|
||||
);
|
||||
|
||||
if ( welcomeModalVisible ) {
|
||||
await test.step( 'Welcome modal appeared. Close it.', async () => {
|
||||
await page
|
||||
.getByRole( 'document' )
|
||||
.getByRole( 'button', { name: 'Close' } )
|
||||
.click();
|
||||
} );
|
||||
} else {
|
||||
await test.step( 'Welcome modal did not appear.', async () => {
|
||||
// do nothing.
|
||||
} );
|
||||
}
|
||||
await goToPostEditor( { page } );
|
||||
|
||||
await page
|
||||
.getByRole( 'textbox', { name: 'Add Title' } )
|
||||
|
|
|
@ -67,28 +67,36 @@ test.describe( 'WooCommerce Shipping Settings - Add new shipping zone', () => {
|
|||
.getByPlaceholder( 'Zone name' )
|
||||
.fill( shippingZoneNameLocalPickup );
|
||||
|
||||
const input = await page.getByPlaceholder( 'Start typing to filter zones' );
|
||||
const input = await page.getByPlaceholder(
|
||||
'Start typing to filter zones'
|
||||
);
|
||||
input.click();
|
||||
input.type( 'British Columbia, Canada' );
|
||||
|
||||
await page
|
||||
.getByText( 'British Columbia, Canada' ).last()
|
||||
.click();
|
||||
await page.getByText( 'British Columbia, Canada' ).last().click();
|
||||
|
||||
// Close dropdown
|
||||
await page.keyboard.press('Escape');
|
||||
|
||||
await page.getByRole( 'link', { name: 'Limit to specific ZIP/postcodes' } ).click();
|
||||
await page.getByPlaceholder( 'List 1 postcode per line' ).fill( maynePostal );
|
||||
|
||||
await page.getByRole( 'button', { name: 'Add shipping method' } ).click();
|
||||
await page.getByText( 'Local pickup', { exact: true } ).click();
|
||||
await page.getByRole('button', { name: 'Continue' } ).last().click();
|
||||
await page.waitForLoadState( 'networkidle' );
|
||||
await page.keyboard.press( 'Escape' );
|
||||
|
||||
await page
|
||||
.getByPlaceholder( 'e.g. Local pickup' )
|
||||
.fill( 'Local pickup' );
|
||||
.getByRole( 'link', {
|
||||
name: 'Limit to specific ZIP/postcodes',
|
||||
} )
|
||||
.click();
|
||||
await page
|
||||
.getByPlaceholder( 'List 1 postcode per line' )
|
||||
.fill( maynePostal );
|
||||
|
||||
await page
|
||||
.getByRole( 'button', { name: 'Add shipping method' } )
|
||||
.click();
|
||||
await page.getByText( 'Local pickup', { exact: true } ).click();
|
||||
await page
|
||||
.getByRole( 'button', { name: 'Continue' } )
|
||||
.last()
|
||||
.click();
|
||||
await page.waitForLoadState( 'networkidle' );
|
||||
|
||||
await page.locator( '#btn-ok' ).click();
|
||||
await page.waitForLoadState( 'networkidle' );
|
||||
|
||||
|
@ -130,28 +138,32 @@ test.describe( 'WooCommerce Shipping Settings - Add new shipping zone', () => {
|
|||
'wp-admin/admin.php?page=wc-settings&tab=shipping&zone_id=new',
|
||||
{ waitUntil: 'networkidle' }
|
||||
);
|
||||
await page.getByPlaceholder( 'Zone name' ).fill( shippingZoneNameFreeShip );
|
||||
await page
|
||||
.getByPlaceholder( 'Zone name' )
|
||||
.fill( shippingZoneNameFreeShip );
|
||||
|
||||
const input = await page.getByPlaceholder( 'Start typing to filter zones' );
|
||||
const input = await page.getByPlaceholder(
|
||||
'Start typing to filter zones'
|
||||
);
|
||||
input.click();
|
||||
input.type( 'British Columbia, Canada' );
|
||||
|
||||
await page
|
||||
.getByText( 'British Columbia, Canada' ).last()
|
||||
.click();
|
||||
await page.getByText( 'British Columbia, Canada' ).last().click();
|
||||
|
||||
// Close dropdown
|
||||
await page.keyboard.press('Escape');
|
||||
|
||||
await page.getByRole( 'button', { name: 'Add shipping method' } ).click();
|
||||
|
||||
await page.getByText( 'Free shipping', { exact: true } ).click();
|
||||
await page.getByRole('button', { name: 'Continue' } ).last().click();
|
||||
await page.waitForLoadState( 'networkidle' );
|
||||
await page.keyboard.press( 'Escape' );
|
||||
|
||||
await page
|
||||
.getByPlaceholder( 'e.g. Free shipping' )
|
||||
.fill( 'Free shipping' );
|
||||
.getByRole( 'button', { name: 'Add shipping method' } )
|
||||
.click();
|
||||
|
||||
await page.getByText( 'Free shipping', { exact: true } ).click();
|
||||
await page
|
||||
.getByRole( 'button', { name: 'Continue' } )
|
||||
.last()
|
||||
.click();
|
||||
await page.waitForLoadState( 'networkidle' );
|
||||
|
||||
await page.locator( '#btn-ok' ).click();
|
||||
await page.waitForLoadState( 'networkidle' );
|
||||
|
||||
|
@ -190,27 +202,31 @@ test.describe( 'WooCommerce Shipping Settings - Add new shipping zone', () => {
|
|||
'wp-admin/admin.php?page=wc-settings&tab=shipping&zone_id=new',
|
||||
{ waitUntil: 'networkidle' }
|
||||
);
|
||||
await page.getByPlaceholder( 'Zone name' ).fill( shippingZoneNameFlatRate );
|
||||
await page
|
||||
.getByPlaceholder( 'Zone name' )
|
||||
.fill( shippingZoneNameFlatRate );
|
||||
|
||||
const input = await page.getByPlaceholder( 'Start typing to filter zones' );
|
||||
const input = await page.getByPlaceholder(
|
||||
'Start typing to filter zones'
|
||||
);
|
||||
input.click();
|
||||
input.type( 'Canada' );
|
||||
|
||||
await page.getByText( 'Canada' ).last().click();
|
||||
|
||||
// Close dropdown
|
||||
await page.keyboard.press( 'Escape' );
|
||||
|
||||
await page
|
||||
.getByText('Canada').last()
|
||||
.getByRole( 'button', { name: 'Add shipping method' } )
|
||||
.click();
|
||||
|
||||
// Close dropdown
|
||||
await page.keyboard.press('Escape');
|
||||
|
||||
await page.getByRole( 'button', { name: 'Add shipping method' } ).click();
|
||||
await page.getByText( 'Flat rate', { exact: true } ).click();
|
||||
await page.getByRole('button', { name: 'Continue' } ).last().click();
|
||||
await page
|
||||
.getByRole( 'button', { name: 'Continue' } )
|
||||
.last()
|
||||
.click();
|
||||
await page.waitForLoadState( 'networkidle' );
|
||||
|
||||
await page
|
||||
.getByPlaceholder( 'e.g. Standard national' )
|
||||
.fill( 'Flat rate' );
|
||||
await page.locator( '#btn-ok' ).click();
|
||||
await page.waitForLoadState( 'networkidle' );
|
||||
|
||||
|
@ -220,7 +236,11 @@ test.describe( 'WooCommerce Shipping Settings - Add new shipping zone', () => {
|
|||
.filter( { hasText: 'Flat rate' } )
|
||||
).toBeVisible();
|
||||
|
||||
await page.locator( 'td:has-text("Flat rate") ~ td.wc-shipping-zone-actions a.wc-shipping-zone-action-edit' ).click();
|
||||
await page
|
||||
.locator(
|
||||
'td:has-text("Flat rate") ~ td.wc-shipping-zone-actions a.wc-shipping-zone-action-edit'
|
||||
)
|
||||
.click();
|
||||
await page.getByLabel( 'Cost', { exact: true } ).fill( '10' );
|
||||
await page.getByRole( 'button', { name: 'Save' } ).last().click();
|
||||
await page.waitForLoadState( 'networkidle' );
|
||||
|
@ -257,16 +277,16 @@ test.describe( 'WooCommerce Shipping Settings - Add new shipping zone', () => {
|
|||
);
|
||||
await page.locator( '#zone_name' ).fill( shippingZoneNameUSRegion );
|
||||
|
||||
const input = await page.getByPlaceholder( 'Start typing to filter zones' );
|
||||
const input = await page.getByPlaceholder(
|
||||
'Start typing to filter zones'
|
||||
);
|
||||
input.click();
|
||||
input.type( 'United States' );
|
||||
|
||||
await page
|
||||
.getByText( 'United States' ).last()
|
||||
.click();
|
||||
await page.getByText( 'United States' ).last().click();
|
||||
|
||||
// Close dropdown
|
||||
await page.keyboard.press('Escape');
|
||||
// Close dropdown
|
||||
await page.keyboard.press( 'Escape' );
|
||||
|
||||
await page.locator( '#submit' ).click();
|
||||
await page.waitForFunction( () => {
|
||||
|
@ -286,7 +306,11 @@ test.describe( 'WooCommerce Shipping Settings - Add new shipping zone', () => {
|
|||
//delete created shipping zone region after confirmation it exists
|
||||
await page.goto( 'wp-admin/admin.php?page=wc-settings&tab=shipping' );
|
||||
|
||||
await page.locator( 'td:has-text("USA Zone") ~ td.wc-shipping-zone-actions a.wc-shipping-zone-action-edit' ).click();
|
||||
await page
|
||||
.locator(
|
||||
'td:has-text("USA Zone") ~ td.wc-shipping-zone-actions a.wc-shipping-zone-action-edit'
|
||||
)
|
||||
.click();
|
||||
|
||||
//delete
|
||||
await page.getByRole( 'button', { name: 'Remove' } ).click();
|
||||
|
@ -319,27 +343,27 @@ test.describe( 'WooCommerce Shipping Settings - Add new shipping zone', () => {
|
|||
);
|
||||
await page.locator( '#zone_name' ).fill( shippingZoneNameFlatRate );
|
||||
|
||||
const input = await page.getByPlaceholder( 'Start typing to filter zones' );
|
||||
const input = await page.getByPlaceholder(
|
||||
'Start typing to filter zones'
|
||||
);
|
||||
input.click();
|
||||
input.type( 'Canada' );
|
||||
|
||||
await page
|
||||
.getByText( 'Canada' ).last()
|
||||
.click();
|
||||
await page.getByText( 'Canada' ).last().click();
|
||||
|
||||
// Close dropdown
|
||||
await page.keyboard.press('Escape');
|
||||
// Close dropdown
|
||||
await page.keyboard.press( 'Escape' );
|
||||
|
||||
await page.locator( 'text=Add shipping method' ).click();
|
||||
|
||||
await page.getByText( 'Flat rate', { exact: true } ).click();
|
||||
await page.getByRole('button', { name: 'Continue' } ).last().click();
|
||||
await page
|
||||
.getByRole( 'button', { name: 'Continue' } )
|
||||
.last()
|
||||
.click();
|
||||
|
||||
await page.waitForLoadState( 'networkidle' );
|
||||
|
||||
await page
|
||||
.getByPlaceholder( 'e.g. Standard national' )
|
||||
.fill( 'Flat rate' );
|
||||
await page.locator( '#btn-ok' ).click();
|
||||
await page.waitForLoadState( 'networkidle' );
|
||||
|
||||
|
@ -349,7 +373,11 @@ test.describe( 'WooCommerce Shipping Settings - Add new shipping zone', () => {
|
|||
.filter( { hasText: 'Flat rate' } )
|
||||
).toBeVisible();
|
||||
|
||||
await page.locator( 'td:has-text("Flat rate") ~ td.wc-shipping-zone-actions a.wc-shipping-zone-action-edit' ).click();
|
||||
await page
|
||||
.locator(
|
||||
'td:has-text("Flat rate") ~ td.wc-shipping-zone-actions a.wc-shipping-zone-action-edit'
|
||||
)
|
||||
.click();
|
||||
await page.locator( '#woocommerce_flat_rate_cost' ).fill( '10' );
|
||||
await page.locator( '#btn-ok' ).click();
|
||||
await page.waitForLoadState( 'networkidle' );
|
||||
|
@ -435,20 +463,13 @@ test.describe( 'Verifies shipping options from customer perspective', () => {
|
|||
method_id: 'flat_rate',
|
||||
settings: {
|
||||
cost: '10.00',
|
||||
title: 'Flat rate',
|
||||
},
|
||||
} );
|
||||
await api.post( `shipping/zones/${ shippingFreeId }/methods`, {
|
||||
method_id: 'free_shipping',
|
||||
settings: {
|
||||
title: 'Free shipping',
|
||||
}
|
||||
} );
|
||||
await api.post( `shipping/zones/${ shippingLocalId }/methods`, {
|
||||
method_id: 'local_pickup',
|
||||
settings: {
|
||||
title: 'Local pickup',
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
const { test, expect } = require( '@playwright/test' );
|
||||
const { admin } = require( '../../test-data/data' );
|
||||
const { closeWelcomeModal } = require( '../../utils/editor' );
|
||||
const wcApi = require( '@woocommerce/woocommerce-rest-api' ).default;
|
||||
|
||||
const firstProductName = 'First Product';
|
||||
|
@ -145,14 +146,7 @@ test.describe( 'Cart Block Calculate Shipping', () => {
|
|||
await page.locator( 'input[name="pwd"]' ).fill( admin.password );
|
||||
await page.locator( 'text=Log In' ).click();
|
||||
|
||||
// close welcome popup if prompted
|
||||
try {
|
||||
await page
|
||||
.getByLabel( 'Close', { exact: true } )
|
||||
.click( { timeout: 5000 } );
|
||||
} catch ( error ) {
|
||||
console.log( "Welcome modal wasn't present, skipping action." );
|
||||
}
|
||||
await closeWelcomeModal( { page } );
|
||||
|
||||
await page
|
||||
.getByRole( 'textbox', { name: 'Add title' } )
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const { test, expect } = require( '@playwright/test' );
|
||||
const wcApi = require( '@woocommerce/woocommerce-rest-api' ).default;
|
||||
const { admin } = require( '../../test-data/data' );
|
||||
const { closeWelcomeModal } = require( '../../utils/editor' );
|
||||
|
||||
const productName = 'First Product Cart Block Taxing';
|
||||
const productPrice = '100.00';
|
||||
|
@ -106,14 +107,7 @@ test.describe( 'Shopper Cart Block Tax Display', () => {
|
|||
await page.locator( 'input[name="pwd"]' ).fill( admin.password );
|
||||
await page.locator( 'text=Log In' ).click();
|
||||
|
||||
// Close welcome popup if prompted
|
||||
try {
|
||||
await page
|
||||
.getByLabel( 'Close', { exact: true } )
|
||||
.click( { timeout: 5000 } );
|
||||
} catch ( error ) {
|
||||
console.log( "Welcome modal wasn't present, skipping action." );
|
||||
}
|
||||
await closeWelcomeModal( { page } );
|
||||
|
||||
await page
|
||||
.getByRole( 'textbox', { name: 'Add title' } )
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
const { test, expect } = require( '@playwright/test' );
|
||||
const { admin } = require( '../../test-data/data' );
|
||||
const { closeWelcomeModal } = require( '../../utils/editor' );
|
||||
const wcApi = require( '@woocommerce/woocommerce-rest-api' ).default;
|
||||
|
||||
const simpleProductName = 'A Simple Product';
|
||||
|
@ -91,14 +92,7 @@ test.describe( 'Cart Block Applying Coupons', () => {
|
|||
await page.locator( 'input[name="pwd"]' ).fill( admin.password );
|
||||
await page.locator( 'text=Log In' ).click();
|
||||
|
||||
// Close welcome popup if prompted
|
||||
try {
|
||||
await page
|
||||
.getByLabel( 'Close', { exact: true } )
|
||||
.click( { timeout: 5000 } );
|
||||
} catch ( error ) {
|
||||
console.log( "Welcome modal wasn't present, skipping action." );
|
||||
}
|
||||
await closeWelcomeModal( { page } );
|
||||
|
||||
await page
|
||||
.getByRole( 'textbox', { name: 'Add title' } )
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
const { test, expect } = require( '@playwright/test' );
|
||||
const { admin } = require( '../../test-data/data' );
|
||||
const { closeWelcomeModal } = require( '../../utils/editor' );
|
||||
const wcApi = require( '@woocommerce/woocommerce-rest-api' ).default;
|
||||
|
||||
const simpleProductName = 'Single Simple Product';
|
||||
|
@ -91,14 +92,7 @@ test.describe( 'Cart Block page', () => {
|
|||
await page.locator( 'input[name="pwd"]' ).fill( admin.password );
|
||||
await page.locator( 'text=Log In' ).click();
|
||||
|
||||
// Close welcome popup if prompted
|
||||
try {
|
||||
await page
|
||||
.getByLabel( 'Close', { exact: true } )
|
||||
.click( { timeout: 5000 } );
|
||||
} catch ( error ) {
|
||||
console.log( "Welcome modal wasn't present, skipping action." );
|
||||
}
|
||||
await closeWelcomeModal( { page } );
|
||||
|
||||
await page
|
||||
.getByRole( 'textbox', { name: 'Add title' } )
|
||||
|
|
|
@ -78,22 +78,15 @@ test.describe( 'Cart Calculate Shipping', () => {
|
|||
// set shipping zone methods
|
||||
await api.post( `shipping/zones/${ shippingZoneDEId }/methods`, {
|
||||
method_id: 'free_shipping',
|
||||
settings: {
|
||||
title: 'Free shipping',
|
||||
},
|
||||
} );
|
||||
await api.post( `shipping/zones/${ shippingZoneFRId }/methods`, {
|
||||
method_id: 'flat_rate',
|
||||
settings: {
|
||||
title: 'Flat rate',
|
||||
cost: '5.00',
|
||||
},
|
||||
} );
|
||||
await api.post( `shipping/zones/${ shippingZoneFRId }/methods`, {
|
||||
method_id: 'local_pickup',
|
||||
settings: {
|
||||
title: 'Local pickup',
|
||||
},
|
||||
} );
|
||||
// confirm that we allow shipping to any country
|
||||
await api.put( 'settings/general/woocommerce_allowed_countries', {
|
||||
|
@ -174,9 +167,9 @@ test.describe( 'Cart Calculate Shipping', () => {
|
|||
await page.locator( 'text=Local pickup' ).click();
|
||||
|
||||
// Verify updated shipping costs
|
||||
await expect( page.locator( '.order-total .amount' ).first() ).toContainText(
|
||||
`$${ firstProductPrice }`
|
||||
);
|
||||
await expect(
|
||||
page.locator( '.order-total .amount' ).first()
|
||||
).toContainText( `$${ firstProductPrice }` );
|
||||
} );
|
||||
|
||||
test( 'should show correct total cart price after updating quantity', async ( {
|
||||
|
|
|
@ -66,9 +66,6 @@ test.describe( 'Shopper Checkout Create Account', () => {
|
|||
] );
|
||||
await api.post( `shipping/zones/${ shippingZoneId }/methods`, {
|
||||
method_id: 'free_shipping',
|
||||
settings: {
|
||||
title: 'Free shipping',
|
||||
}
|
||||
} );
|
||||
await api.put( 'payment_gateways/cod', {
|
||||
enabled: true,
|
||||
|
|
|
@ -62,9 +62,6 @@ test.describe( 'Shopper Checkout Login Account', () => {
|
|||
] );
|
||||
await api.post( `shipping/zones/${ shippingZoneId }/methods`, {
|
||||
method_id: 'free_shipping',
|
||||
settings: {
|
||||
title: 'Free shipping',
|
||||
}
|
||||
} );
|
||||
// create customer and save its id
|
||||
await api
|
||||
|
@ -175,6 +172,8 @@ test.describe( 'Shopper Checkout Login Account', () => {
|
|||
// check my account page
|
||||
await page.goto( '/my-account/' );
|
||||
await expect( page.url() ).toContain( 'my-account/' );
|
||||
await expect( page.getByRole( 'heading', { name: 'My account' } ) ).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole( 'heading', { name: 'My account' } )
|
||||
).toBeVisible();
|
||||
} );
|
||||
} );
|
||||
|
|
|
@ -67,9 +67,6 @@ test.describe( 'Checkout page', () => {
|
|||
] );
|
||||
await api.post( `shipping/zones/${ shippingZoneId }/methods`, {
|
||||
method_id: 'free_shipping',
|
||||
settings: {
|
||||
title: 'Free shipping',
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
||||
|
@ -117,7 +114,6 @@ test.describe( 'Checkout page', () => {
|
|||
await api.put( 'payment_gateways/cod', {
|
||||
enabled: true,
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
test( 'should display cart items in order review', async ( { page } ) => {
|
||||
|
@ -132,7 +128,11 @@ test.describe( 'Checkout page', () => {
|
|||
await expect( page.locator( 'strong.product-quantity' ) ).toContainText(
|
||||
'1'
|
||||
);
|
||||
let totalPrice = await page.getByRole( 'row', { name: 'Total' } ).last().locator( 'td' ).textContent();
|
||||
let totalPrice = await page
|
||||
.getByRole( 'row', { name: 'Total' } )
|
||||
.last()
|
||||
.locator( 'td' )
|
||||
.textContent();
|
||||
console.log( `Total Price: ${ totalPrice }` );
|
||||
totalPrice = Number( totalPrice.replace( /[^\d.-]/g, '' ) );
|
||||
console.log( `Number: ${ totalPrice }` );
|
||||
|
@ -154,7 +154,11 @@ test.describe( 'Checkout page', () => {
|
|||
await expect( page.locator( 'strong.product-quantity' ) ).toContainText(
|
||||
'2'
|
||||
);
|
||||
let totalPrice = await page.getByRole( 'row', { name: 'Total' } ).last().locator( 'td' ).textContent();
|
||||
let totalPrice = await page
|
||||
.getByRole( 'row', { name: 'Total' } )
|
||||
.last()
|
||||
.locator( 'td' )
|
||||
.textContent();
|
||||
console.log( `Total Price: ${ totalPrice }` );
|
||||
totalPrice = Number( totalPrice.replace( /[^\d.-]/g, '' ) );
|
||||
console.log( `Number: ${ totalPrice }` );
|
||||
|
@ -178,7 +182,11 @@ test.describe( 'Checkout page', () => {
|
|||
await expect( page.locator( 'strong.product-quantity' ) ).toContainText(
|
||||
'3'
|
||||
);
|
||||
let totalPrice = await page.getByRole( 'row', { name: 'Total' } ).last().locator( 'td' ).textContent();
|
||||
let totalPrice = await page
|
||||
.getByRole( 'row', { name: 'Total' } )
|
||||
.last()
|
||||
.locator( 'td' )
|
||||
.textContent();
|
||||
console.log( `Total Price: ${ totalPrice }` );
|
||||
totalPrice = Number( totalPrice.replace( /[^\d.-]/g, '' ) );
|
||||
console.log( `Number: ${ totalPrice }` );
|
||||
|
@ -200,24 +208,44 @@ test.describe( 'Checkout page', () => {
|
|||
await expect( page.locator( '#billing_email' ) ).toBeEditable();
|
||||
} );
|
||||
|
||||
test( 'warn when customer is missing required details', async ( { page } ) => {
|
||||
await page.goto( `/shop/?add-to-cart=${ productId }`, { waitUntil: 'networkidle' } );
|
||||
test( 'warn when customer is missing required details', async ( {
|
||||
page,
|
||||
} ) => {
|
||||
await page.goto( `/shop/?add-to-cart=${ productId }`, {
|
||||
waitUntil: 'networkidle',
|
||||
} );
|
||||
|
||||
await page.goto( '/checkout/' );
|
||||
|
||||
// first try submitting the form with no fields complete
|
||||
await page.getByRole('button', { name: 'Place order' }).click();
|
||||
await expect( page.locator('form[name="checkout"]').getByRole('alert') ).toBeVisible();
|
||||
await expect( page.getByText( 'Billing First name is a required field.' ) ).toBeVisible();
|
||||
await expect( page.getByText( 'Billing Last name is a required field.' ) ).toBeVisible();
|
||||
await expect( page.getByText( 'Billing Street address is a required field.' ) ).toBeVisible();
|
||||
await expect( page.getByText( 'Billing Town / City is a required field.' ) ).toBeVisible();
|
||||
await expect( page.getByText( 'Billing ZIP Code is a required field.' ) ).toBeVisible();
|
||||
await expect( page.getByText( 'Billing Phone is a required field.' ) ).toBeVisible();
|
||||
await expect( page.getByText( 'Billing Email address is a required field.' ) ).toBeVisible();
|
||||
await page.getByRole( 'button', { name: 'Place order' } ).click();
|
||||
await expect(
|
||||
page.locator( 'form[name="checkout"]' ).getByRole( 'alert' )
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByText( 'Billing First name is a required field.' )
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByText( 'Billing Last name is a required field.' )
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByText( 'Billing Street address is a required field.' )
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByText( 'Billing Town / City is a required field.' )
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByText( 'Billing ZIP Code is a required field.' )
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByText( 'Billing Phone is a required field.' )
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByText( 'Billing Email address is a required field.' )
|
||||
).toBeVisible();
|
||||
|
||||
// toggle ship to different address, fill out billing info and confirm error shown
|
||||
await page.getByText('Ship to a different address?').click();
|
||||
await page.getByText( 'Ship to a different address?' ).click();
|
||||
await page.locator( '#billing_first_name' ).fill( 'Homer' );
|
||||
await page.locator( '#billing_last_name' ).fill( 'Simpson' );
|
||||
await page
|
||||
|
@ -229,14 +257,24 @@ test.describe( 'Checkout page', () => {
|
|||
await page.locator( '#billing_postcode' ).fill( '97403' );
|
||||
await page.locator( '#billing_phone' ).fill( '555 555-5555' );
|
||||
await page.locator( '#billing_email' ).fill( customer.email );
|
||||
await page.getByRole('button', { name: 'Place order' }).click();
|
||||
await page.getByRole( 'button', { name: 'Place order' } ).click();
|
||||
|
||||
await expect( page.locator( 'ul.woocommerce-error' ) ).toBeVisible();
|
||||
await expect( page.getByText( 'Shipping First name is a required field.' ) ).toBeVisible();
|
||||
await expect( page.getByText( 'Shipping Last name is a required field.' ) ).toBeVisible();
|
||||
await expect( page.getByText( 'Shipping Street address is a required field.' ) ).toBeVisible();
|
||||
await expect( page.getByText( 'Shipping Town / City is a required field.' ) ).toBeVisible();
|
||||
await expect( page.getByText( 'Shipping ZIP Code is a required field.' ) ).toBeVisible();
|
||||
await expect(
|
||||
page.getByText( 'Shipping First name is a required field.' )
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByText( 'Shipping Last name is a required field.' )
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByText( 'Shipping Street address is a required field.' )
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByText( 'Shipping Town / City is a required field.' )
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByText( 'Shipping ZIP Code is a required field.' )
|
||||
).toBeVisible();
|
||||
} );
|
||||
|
||||
test( 'allows customer to fill shipping details', async ( { page } ) => {
|
||||
|
@ -249,7 +287,11 @@ test.describe( 'Checkout page', () => {
|
|||
await expect( page.locator( 'strong.product-quantity' ) ).toContainText(
|
||||
'2'
|
||||
);
|
||||
let totalPrice = await page.getByRole( 'row', { name: 'Total' } ).last().locator( 'td' ).textContent();
|
||||
let totalPrice = await page
|
||||
.getByRole( 'row', { name: 'Total' } )
|
||||
.last()
|
||||
.locator( 'td' )
|
||||
.textContent();
|
||||
console.log( `Total Price: ${ totalPrice }` );
|
||||
totalPrice = Number( totalPrice.replace( /[^\d.-]/g, '' ) );
|
||||
console.log( `Number: ${ totalPrice }` );
|
||||
|
@ -281,7 +323,11 @@ test.describe( 'Checkout page', () => {
|
|||
await expect( page.locator( 'strong.product-quantity' ) ).toContainText(
|
||||
'2'
|
||||
);
|
||||
let totalPrice = await page.getByRole( 'row', { name: 'Total' } ).last().locator( 'td' ).textContent();
|
||||
let totalPrice = await page
|
||||
.getByRole( 'row', { name: 'Total' } )
|
||||
.last()
|
||||
.locator( 'td' )
|
||||
.textContent();
|
||||
console.log( `Total Price: ${ totalPrice }` );
|
||||
totalPrice = Number( totalPrice.replace( /[^\d.-]/g, '' ) );
|
||||
console.log( `Number: ${ totalPrice }` );
|
||||
|
@ -315,7 +361,6 @@ test.describe( 'Checkout page', () => {
|
|||
.textContent();
|
||||
guestOrderId = await orderReceivedText.split( /(\s+)/ )[ 6 ].toString();
|
||||
|
||||
|
||||
// Let's simulate a new browser context (by dropping all cookies), and reload the page. This approximates a
|
||||
// scenario where the server can no longer identify the shopper. However, so long as we are within the 10 minute
|
||||
// grace period following initial order placement, the 'order received' page should still be rendered.
|
||||
|
@ -327,19 +372,23 @@ test.describe( 'Checkout page', () => {
|
|||
|
||||
// Let's simulate a scenario where the 10 minute grace period has expired. This time, we expect the shopper to
|
||||
// be presented with a request to verify their email address.
|
||||
await setFilterValue( page, 'woocommerce_order_email_verification_grace_period', 0 );
|
||||
await page.reload();
|
||||
await expect( page.locator( 'form.woocommerce-verify-email p:nth-child(3)' ) ).toContainText(
|
||||
/verify the email address associated with the order/
|
||||
await setFilterValue(
|
||||
page,
|
||||
'woocommerce_order_email_verification_grace_period',
|
||||
0
|
||||
);
|
||||
await page.reload();
|
||||
await expect(
|
||||
page.locator( 'form.woocommerce-verify-email p:nth-child(3)' )
|
||||
).toContainText( /verify the email address associated with the order/ );
|
||||
|
||||
// Supplying an email address other than the actual order billing email address will take them back to the same
|
||||
// page with an error message.
|
||||
await page.fill( '#email', 'incorrect@email.address' );
|
||||
await page.locator( 'form.woocommerce-verify-email button' ).click();
|
||||
await expect( page.locator( 'form.woocommerce-verify-email p:nth-child(4)' ) ).toContainText(
|
||||
/verify the email address associated with the order/
|
||||
);
|
||||
await expect(
|
||||
page.locator( 'form.woocommerce-verify-email p:nth-child(4)' )
|
||||
).toContainText( /verify the email address associated with the order/ );
|
||||
await expect( page.locator( 'ul.woocommerce-error li' ) ).toContainText(
|
||||
/We were unable to verify the email address you provided/
|
||||
);
|
||||
|
@ -362,7 +411,9 @@ test.describe( 'Checkout page', () => {
|
|||
);
|
||||
|
||||
await expect(
|
||||
page.getByRole( 'heading', { name: `Order #${ guestOrderId } details` } )
|
||||
page.getByRole( 'heading', {
|
||||
name: `Order #${ guestOrderId } details`,
|
||||
} )
|
||||
).toBeVisible();
|
||||
await expect( page.locator( '.wc-order-item-name' ) ).toContainText(
|
||||
simpleProductName
|
||||
|
@ -381,8 +432,12 @@ test.describe( 'Checkout page', () => {
|
|||
|
||||
test( 'allows existing customer to place order', async ( { page } ) => {
|
||||
await page.goto( 'my-account/' );
|
||||
await page.locator( 'input[name="username"]' ).fill( customer.username );
|
||||
await page.locator( 'input[name="password"]' ).fill( customer.password );
|
||||
await page
|
||||
.locator( 'input[name="username"]' )
|
||||
.fill( customer.username );
|
||||
await page
|
||||
.locator( 'input[name="password"]' )
|
||||
.fill( customer.password );
|
||||
await page.locator( 'text=Log In' ).click();
|
||||
await page.waitForLoadState( 'networkidle' );
|
||||
for ( let i = 1; i < 3; i++ ) {
|
||||
|
@ -394,7 +449,11 @@ test.describe( 'Checkout page', () => {
|
|||
await expect( page.locator( 'strong.product-quantity' ) ).toContainText(
|
||||
'2'
|
||||
);
|
||||
let totalPrice = await page.getByRole( 'row', { name: 'Total' } ).last().locator( 'td' ).textContent();
|
||||
let totalPrice = await page
|
||||
.getByRole( 'row', { name: 'Total' } )
|
||||
.last()
|
||||
.locator( 'td' )
|
||||
.textContent();
|
||||
console.log( `Total Price: ${ totalPrice }` );
|
||||
totalPrice = Number( totalPrice.replace( /[^\d.-]/g, '' ) );
|
||||
console.log( `Number: ${ totalPrice }` );
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
const { test, expect, request } = require( '@playwright/test' );
|
||||
const { admin } = require( '../../test-data/data' );
|
||||
const { closeWelcomeModal } = require( '../../utils/editor' );
|
||||
const wcApi = require( '@woocommerce/woocommerce-rest-api' ).default;
|
||||
|
||||
const pageTitle = 'Mini Cart';
|
||||
|
@ -143,14 +144,7 @@ test.describe( 'Mini Cart block page', () => {
|
|||
await page.locator( 'input[name="pwd"]' ).fill( admin.password );
|
||||
await page.locator( 'text=Log In' ).click();
|
||||
|
||||
// Close welcome popup if prompted
|
||||
try {
|
||||
await page
|
||||
.getByLabel( 'Close', { exact: true } )
|
||||
.click( { timeout: 5000 } );
|
||||
} catch ( error ) {
|
||||
console.log( "Welcome modal wasn't present, skipping action." );
|
||||
}
|
||||
await closeWelcomeModal( { page } );
|
||||
|
||||
await page
|
||||
.getByRole( 'textbox', { name: 'Add title' } )
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const { test, expect, request } = require( '@playwright/test' );
|
||||
const { admin } = require( '../../test-data/data' );
|
||||
const pageTitle = 'Product Showcase';
|
||||
const { goToPageEditor } = require( '../../utils/editor' );
|
||||
const wcApi = require( '@woocommerce/woocommerce-rest-api' ).default;
|
||||
|
||||
const singleProductPrice1 = '5.00';
|
||||
|
@ -265,17 +266,7 @@ test.describe( 'Browse product tags and attributes from the product page', () =>
|
|||
|
||||
test( 'can see products showcase', async ( { page } ) => {
|
||||
// create as a merchant a new page with Products block
|
||||
await page.goto( 'wp-admin/post-new.php?post_type=page' );
|
||||
|
||||
const welcomeModalVisible = await page
|
||||
.getByRole( 'heading', {
|
||||
name: 'Welcome to the block editor',
|
||||
} )
|
||||
.isVisible();
|
||||
|
||||
if ( welcomeModalVisible ) {
|
||||
await page.getByRole( 'button', { name: 'Close' } ).click();
|
||||
}
|
||||
await goToPageEditor( { page } );
|
||||
|
||||
await page
|
||||
.getByRole( 'textbox', { name: 'Add Title' } )
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
const { test } = require( '@playwright/test' );
|
||||
|
||||
const closeWelcomeModal = async ( { page } ) => {
|
||||
const welcomeModalVisible =
|
||||
await test.step( 'Check if the Welcome modal appeared', async () => {
|
||||
return await page
|
||||
.getByRole( 'heading', {
|
||||
name: 'Welcome to the block editor',
|
||||
} )
|
||||
.isVisible();
|
||||
} );
|
||||
|
||||
if ( welcomeModalVisible ) {
|
||||
await test.step( 'Welcome modal appeared. Close it.', async () => {
|
||||
await page
|
||||
.getByRole( 'document' )
|
||||
.getByRole( 'button', { name: 'Close' } )
|
||||
.click();
|
||||
} );
|
||||
} else {
|
||||
await test.step( 'Welcome modal did not appear.', async () => {
|
||||
// do nothing.
|
||||
} );
|
||||
}
|
||||
};
|
||||
|
||||
const goToPageEditor = async ( { page } ) => {
|
||||
await page.goto( 'wp-admin/post-new.php?post_type=page' );
|
||||
|
||||
await closeWelcomeModal( { page } );
|
||||
};
|
||||
|
||||
const goToPostEditor = async ( { page } ) => {
|
||||
await page.goto( 'wp-admin/post-new.php' );
|
||||
|
||||
await closeWelcomeModal( { page } );
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
closeWelcomeModal,
|
||||
goToPageEditor,
|
||||
goToPostEditor,
|
||||
};
|
|
@ -680,7 +680,7 @@ class WC_Tests_API_Shipping_Zones_V2 extends WC_REST_Unit_Test_Case {
|
|||
$data = $response->get_data();
|
||||
|
||||
$this->assertArrayHasKey( 'title', $data['settings'] );
|
||||
$this->assertEquals( '', $data['settings']['title']['value'] );
|
||||
$this->assertEquals( 'Flat rate', $data['settings']['title']['value'] );
|
||||
$this->assertArrayHasKey( 'tax_status', $data['settings'] );
|
||||
$this->assertEquals( 'taxable', $data['settings']['tax_status']['value'] );
|
||||
$this->assertArrayHasKey( 'cost', $data['settings'] );
|
||||
|
@ -691,8 +691,7 @@ class WC_Tests_API_Shipping_Zones_V2 extends WC_REST_Unit_Test_Case {
|
|||
$request->set_body_params(
|
||||
array(
|
||||
'settings' => array(
|
||||
'cost' => 5,
|
||||
'title' => 'Flat rate',
|
||||
'cost' => 5,
|
||||
),
|
||||
)
|
||||
);
|
||||
|
|
|
@ -703,7 +703,7 @@ class WC_Tests_API_Shipping_Zones extends WC_REST_Unit_Test_Case {
|
|||
$data = $response->get_data();
|
||||
|
||||
$this->assertArrayHasKey( 'title', $data['settings'] );
|
||||
$this->assertEquals( '', $data['settings']['title']['value'] );
|
||||
$this->assertEquals( 'Flat rate', $data['settings']['title']['value'] );
|
||||
$this->assertArrayHasKey( 'tax_status', $data['settings'] );
|
||||
$this->assertEquals( 'taxable', $data['settings']['tax_status']['value'] );
|
||||
$this->assertArrayHasKey( 'cost', $data['settings'] );
|
||||
|
@ -714,8 +714,7 @@ class WC_Tests_API_Shipping_Zones extends WC_REST_Unit_Test_Case {
|
|||
$request->set_body_params(
|
||||
array(
|
||||
'settings' => array(
|
||||
'cost' => 5,
|
||||
'title' => 'Flat rate',
|
||||
'cost' => 5,
|
||||
),
|
||||
)
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue