Merge branch 'trunk' into fix/unit-tests

This commit is contained in:
Claudio Sanches 2021-08-17 13:13:15 -03:00
commit 6feac59adf
28 changed files with 534 additions and 662 deletions

View File

@ -40,6 +40,7 @@ jobs:
E2E_SLACK_TOKEN: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }} E2E_SLACK_TOKEN: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }}
E2E_SLACK_CHANNEL: ${{ secrets.SMOKE_TEST_SLACK_CHANNEL }} E2E_SLACK_CHANNEL: ${{ secrets.SMOKE_TEST_SLACK_CHANNEL }}
UPDATE_WC: 1 UPDATE_WC: 1
DEFAULT_TIMEOUT_OVERRIDE: 120000
run: | run: |
npx wc-e2e test:e2e ./tests/e2e/specs/smoke-tests/update-woocommerce.js npx wc-e2e test:e2e ./tests/e2e/specs/smoke-tests/update-woocommerce.js
npx wc-e2e test:e2e npx wc-e2e test:e2e

View File

@ -0,0 +1,47 @@
name: Smoke test release
on:
release:
types: [published]
jobs:
login-run:
name: Daily smoke test on release.
runs-on: ubuntu-18.04
steps:
- name: Create dirs.
run: |
mkdir -p code/woocommerce
mkdir -p package/woocommerce
mkdir -p tmp/woocommerce
mkdir -p node_modules
- name: Checkout code.
uses: actions/checkout@v2
with:
ref: trunk
- name: Install prerequisites.
run: |
npm install
composer install --no-dev
npm run build:assets
npm install jest
- name: Run smoke test.
env:
SMOKE_TEST_URL: ${{ secrets.RELEASE_TEST_URL }}
SMOKE_TEST_ADMIN_USER: ${{ secrets.RELEASE_TEST_ADMIN_USER }}
SMOKE_TEST_ADMIN_PASSWORD: ${{ secrets.RELEASE_TEST_ADMIN_PASSWORD }}
SMOKE_TEST_ADMIN_USER_EMAIL: ${{ secrets.RELEASE_TEST_ADMIN_USER_EMAIL }}
SMOKE_TEST_CUSTOMER_USER: ${{ secrets.RELEASE_TEST_CUSTOMER_USER }}
SMOKE_TEST_CUSTOMER_PASSWORD: ${{ secrets.RELEASE_TEST_CUSTOMER_PASSWORD }}
WC_E2E_SCREENSHOTS: 1
E2E_RETEST: 1
E2E_SLACK_TOKEN: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }}
E2E_SLACK_CHANNEL: ${{ secrets.SMOKE_TEST_SLACK_CHANNEL }}
TEST_RELEASE: 1
UPDATE_WC: 1
DEFAULT_TIMEOUT_OVERRIDE: 120000
run: |
npx wc-e2e test:e2e ./tests/e2e/specs/smoke-tests/update-woocommerce.js
npx wc-e2e test:e2e

View File

@ -5,6 +5,8 @@ on:
jobs: jobs:
stale: stale:
if: |
! contains(github.event.issue.labels.*.name, 'enhancement')
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v3 - uses: actions/stale@v3

File diff suppressed because it is too large Load Diff

View File

@ -1,41 +1,27 @@
/*! /*!
* SelectWoo 1.0.9 * Select2 4.0.3
* https://github.com/woocommerce/selectWoo * https://select2.github.io
* *
* Released under the MIT license * Released under the MIT license
* https://github.com/woocommerce/selectWoo/blob/master/LICENSE.md * https://github.com/select2/select2/blob/master/LICENSE.md
*/ */
(function (factory) { (function (factory) {
if (typeof define === 'function' && define.amd) { if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module. // AMD. Register as an anonymous module.
define(['jquery'], factory); define(['jquery'], factory);
} else if (typeof module === 'object' && module.exports) { } else if (typeof exports === 'object') {
// Node/CommonJS // Node/CommonJS
module.exports = function (root, jQuery) { factory(require('jquery'));
if (jQuery === undefined) {
// require('jQuery') returns a factory that requires window to
// build a jQuery instance, we normalize how we use modules
// that require this pattern but the window provided is a noop
// if it's defined (how jquery works)
if (typeof window !== 'undefined') {
jQuery = require('jquery');
}
else {
jQuery = require('jquery')(root);
}
}
factory(jQuery);
return jQuery;
};
} else { } else {
// Browser globals // Browser globals
factory(jQuery); factory(jQuery);
} }
} (function (jQuery) { }(function (jQuery) {
// This is needed so we can catch the AMD loader configuration and use it // This is needed so we can catch the AMD loader configuration and use it
// The inner file should be wrapped (by `banner.start.js`) in a function that // The inner file should be wrapped (by `banner.start.js`) in a function that
// returns the AMD loader references. // returns the AMD loader references.
var S2 =(function () { var S2 =
(function () {
// Restore the Select2 AMD loader so it can be used // Restore the Select2 AMD loader so it can be used
// Needed mostly in the language files, where the loader is not inserted // Needed mostly in the language files, where the loader is not inserted
if (jQuery && jQuery.fn && jQuery.fn.select2 && jQuery.fn.select2.amd) { if (jQuery && jQuery.fn && jQuery.fn.select2 && jQuery.fn.select2.amd) {
@ -44,11 +30,13 @@
var S2;(function () { if (!S2 || !S2.requirejs) { var S2;(function () { if (!S2 || !S2.requirejs) {
if (!S2) { S2 = {}; } else { require = S2; } if (!S2) { S2 = {}; } else { require = S2; }
/** /**
* @license almond 0.3.3 Copyright jQuery Foundation and other contributors. * @license almond 0.3.1 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved.
* Released under MIT license, http://github.com/requirejs/almond/LICENSE * Available via the MIT or new BSD license.
* see: http://github.com/jrburke/almond for details
*/ */
//Going sloppy to avoid 'use strict' string cost, but strict practices should //Going sloppy to avoid 'use strict' string cost, but strict practices should
//be followed. //be followed.
/*jslint sloppy: true */
/*global setTimeout: false */ /*global setTimeout: false */
var requirejs, require, define; var requirejs, require, define;
@ -76,58 +64,60 @@ var requirejs, require, define;
*/ */
function normalize(name, baseName) { function normalize(name, baseName) {
var nameParts, nameSegment, mapValue, foundMap, lastIndex, var nameParts, nameSegment, mapValue, foundMap, lastIndex,
foundI, foundStarMap, starI, i, j, part, normalizedBaseParts, foundI, foundStarMap, starI, i, j, part,
baseParts = baseName && baseName.split("/"), baseParts = baseName && baseName.split("/"),
map = config.map, map = config.map,
starMap = (map && map['*']) || {}; starMap = (map && map['*']) || {};
//Adjust any relative paths. //Adjust any relative paths.
if (name) { if (name && name.charAt(0) === ".") {
name = name.split('/'); //If have a base name, try to normalize against it,
lastIndex = name.length - 1; //otherwise, assume it is a top-level require that will
//be relative to baseUrl in the end.
if (baseName) {
name = name.split('/');
lastIndex = name.length - 1;
// If wanting node ID compatibility, strip .js from end // Node .js allowance:
// of IDs. Have to do this here, and not in nameToUrl if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
// because node allows either .js or non .js to map name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
// to same file. }
if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
}
// Starts with a '.' so need the baseName //Lop off the last part of baseParts, so that . matches the
if (name[0].charAt(0) === '.' && baseParts) { //"directory" and not name of the baseName's module. For instance,
//Convert baseName to array, and lop off the last part, //baseName of "one/two/three", maps to "one/two/three.js", but we
//so that . matches that 'directory' and not name of the baseName's //want the directory, "one/two" for this normalization.
//module. For instance, baseName of 'one/two/three', maps to name = baseParts.slice(0, baseParts.length - 1).concat(name);
//'one/two/three.js', but we want the directory, 'one/two' for
//this normalization.
normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
name = normalizedBaseParts.concat(name);
}
//start trimDots //start trimDots
for (i = 0; i < name.length; i++) { for (i = 0; i < name.length; i += 1) {
part = name[i]; part = name[i];
if (part === '.') { if (part === ".") {
name.splice(i, 1); name.splice(i, 1);
i -= 1; i -= 1;
} else if (part === '..') { } else if (part === "..") {
// If at the start, or previous value is still .., if (i === 1 && (name[2] === '..' || name[0] === '..')) {
// keep them so that when converted to a path it may //End of the line. Keep at least one non-dot
// still work when converted to a path, even though //path segment at the front so it can be mapped
// as an ID it is less than ideal. In larger point //correctly to disk. Otherwise, there is likely
// releases, may be better to just kick out an error. //no path mapping for a path starting with '..'.
if (i === 0 || (i === 1 && name[2] === '..') || name[i - 1] === '..') { //This can still fail, but catches the most reasonable
continue; //uses of ..
} else if (i > 0) { break;
name.splice(i - 1, 2); } else if (i > 0) {
i -= 2; name.splice(i - 1, 2);
i -= 2;
}
} }
} }
} //end trimDots
//end trimDots
name = name.join('/'); name = name.join("/");
} else if (name.indexOf('./') === 0) {
// No baseName, so this is ID is resolved relative
// to baseUrl, pull off the leading dot.
name = name.substring(2);
}
} }
//Apply map config if available. //Apply map config if available.
@ -240,39 +230,32 @@ var requirejs, require, define;
return [prefix, name]; return [prefix, name];
} }
//Creates a parts array for a relName where first part is plugin ID,
//second part is resource ID. Assumes relName has already been normalized.
function makeRelParts(relName) {
return relName ? splitPrefix(relName) : [];
}
/** /**
* Makes a name map, normalizing the name, and using a plugin * Makes a name map, normalizing the name, and using a plugin
* for normalization if necessary. Grabs a ref to plugin * for normalization if necessary. Grabs a ref to plugin
* too, as an optimization. * too, as an optimization.
*/ */
makeMap = function (name, relParts) { makeMap = function (name, relName) {
var plugin, var plugin,
parts = splitPrefix(name), parts = splitPrefix(name),
prefix = parts[0], prefix = parts[0];
relResourceName = relParts[1];
name = parts[1]; name = parts[1];
if (prefix) { if (prefix) {
prefix = normalize(prefix, relResourceName); prefix = normalize(prefix, relName);
plugin = callDep(prefix); plugin = callDep(prefix);
} }
//Normalize according //Normalize according
if (prefix) { if (prefix) {
if (plugin && plugin.normalize) { if (plugin && plugin.normalize) {
name = plugin.normalize(name, makeNormalize(relResourceName)); name = plugin.normalize(name, makeNormalize(relName));
} else { } else {
name = normalize(name, relResourceName); name = normalize(name, relName);
} }
} else { } else {
name = normalize(name, relResourceName); name = normalize(name, relName);
parts = splitPrefix(name); parts = splitPrefix(name);
prefix = parts[0]; prefix = parts[0];
name = parts[1]; name = parts[1];
@ -319,14 +302,13 @@ var requirejs, require, define;
}; };
main = function (name, deps, callback, relName) { main = function (name, deps, callback, relName) {
var cjsModule, depName, ret, map, i, relParts, var cjsModule, depName, ret, map, i,
args = [], args = [],
callbackType = typeof callback, callbackType = typeof callback,
usingExports; usingExports;
//Use name if no relName //Use name if no relName
relName = relName || name; relName = relName || name;
relParts = makeRelParts(relName);
//Call the callback to define the module, if necessary. //Call the callback to define the module, if necessary.
if (callbackType === 'undefined' || callbackType === 'function') { if (callbackType === 'undefined' || callbackType === 'function') {
@ -335,7 +317,7 @@ var requirejs, require, define;
//Default to [require, exports, module] if no deps //Default to [require, exports, module] if no deps
deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps; deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps;
for (i = 0; i < deps.length; i += 1) { for (i = 0; i < deps.length; i += 1) {
map = makeMap(deps[i], relParts); map = makeMap(deps[i], relName);
depName = map.f; depName = map.f;
//Fast path CommonJS standard dependencies. //Fast path CommonJS standard dependencies.
@ -391,7 +373,7 @@ var requirejs, require, define;
//deps arg is the module name, and second arg (if passed) //deps arg is the module name, and second arg (if passed)
//is just the relName. //is just the relName.
//Normalize module name, if it contains . or .. //Normalize module name, if it contains . or ..
return callDep(makeMap(deps, makeRelParts(callback)).f); return callDep(makeMap(deps, callback).f);
} else if (!deps.splice) { } else if (!deps.splice) {
//deps is a config object, not an array. //deps is a config object, not an array.
config = deps; config = deps;
@ -755,12 +737,6 @@ S2.define('select2/utils',[
}); });
}; };
Utils.entityDecode = function (html) {
var txt = document.createElement('textarea');
txt.innerHTML = html;
return txt.value;
}
// Append an array of jQuery nodes to a given element. // Append an array of jQuery nodes to a given element.
Utils.appendMany = function ($element, $nodes) { Utils.appendMany = function ($element, $nodes) {
// jQuery 1.7.x does not support $.fn.append() with an array // jQuery 1.7.x does not support $.fn.append() with an array
@ -778,14 +754,6 @@ S2.define('select2/utils',[
$element.append($nodes); $element.append($nodes);
}; };
// Determine whether the browser is on a touchscreen device.
Utils.isTouchscreen = function() {
if ('undefined' === typeof Utils._isTouchscreenCache) {
Utils._isTouchscreenCache = 'ontouchstart' in document.documentElement;
}
return Utils._isTouchscreenCache;
}
return Utils; return Utils;
}); });
@ -805,7 +773,7 @@ S2.define('select2/results',[
Results.prototype.render = function () { Results.prototype.render = function () {
var $results = $( var $results = $(
'<ul class="select2-results__options" role="listbox" tabindex="-1"></ul>' '<ul class="select2-results__options" role="tree"></ul>'
); );
if (this.options.get('multiple')) { if (this.options.get('multiple')) {
@ -828,7 +796,7 @@ S2.define('select2/results',[
this.hideLoading(); this.hideLoading();
var $message = $( var $message = $(
'<li role="alert" aria-live="assertive"' + '<li role="treeitem" aria-live="assertive"' +
' class="select2-results__option"></li>' ' class="select2-results__option"></li>'
); );
@ -890,9 +858,9 @@ S2.define('select2/results',[
Results.prototype.highlightFirstItem = function () { Results.prototype.highlightFirstItem = function () {
var $options = this.$results var $options = this.$results
.find('.select2-results__option[data-selected]'); .find('.select2-results__option[aria-selected]');
var $selected = $options.filter('[data-selected=true]'); var $selected = $options.filter('[aria-selected=true]');
// Check if there are any selected options // Check if there are any selected options
if ($selected.length > 0) { if ($selected.length > 0) {
@ -916,7 +884,7 @@ S2.define('select2/results',[
}); });
var $options = self.$results var $options = self.$results
.find('.select2-results__option[data-selected]'); .find('.select2-results__option[aria-selected]');
$options.each(function () { $options.each(function () {
var $option = $(this); var $option = $(this);
@ -928,9 +896,9 @@ S2.define('select2/results',[
if ((item.element != null && item.element.selected) || if ((item.element != null && item.element.selected) ||
(item.element == null && $.inArray(id, selectedIds) > -1)) { (item.element == null && $.inArray(id, selectedIds) > -1)) {
$option.attr('data-selected', 'true'); $option.attr('aria-selected', 'true');
} else { } else {
$option.attr('data-selected', 'false'); $option.attr('aria-selected', 'false');
} }
}); });
@ -962,18 +930,17 @@ S2.define('select2/results',[
option.className = 'select2-results__option'; option.className = 'select2-results__option';
var attrs = { var attrs = {
'role': 'option', 'role': 'treeitem',
'data-selected': 'false', 'aria-selected': 'false'
'tabindex': -1
}; };
if (data.disabled) { if (data.disabled) {
delete attrs['data-selected']; delete attrs['aria-selected'];
attrs['aria-disabled'] = 'true'; attrs['aria-disabled'] = 'true';
} }
if (data.id == null) { if (data.id == null) {
delete attrs['data-selected']; delete attrs['aria-selected'];
} }
if (data._resultId != null) { if (data._resultId != null) {
@ -985,8 +952,9 @@ S2.define('select2/results',[
} }
if (data.children) { if (data.children) {
attrs.role = 'group';
attrs['aria-label'] = data.text; attrs['aria-label'] = data.text;
delete attrs['data-selected']; delete attrs['aria-selected'];
} }
for (var attr in attrs) { for (var attr in attrs) {
@ -1003,7 +971,6 @@ S2.define('select2/results',[
var $label = $(label); var $label = $(label);
this.template(data, label); this.template(data, label);
$label.attr('role', 'presentation');
var $children = []; var $children = [];
@ -1016,11 +983,10 @@ S2.define('select2/results',[
} }
var $childrenContainer = $('<ul></ul>', { var $childrenContainer = $('<ul></ul>', {
'class': 'select2-results__options select2-results__options--nested', 'class': 'select2-results__options select2-results__options--nested'
'role': 'listbox'
}); });
$childrenContainer.append($children); $childrenContainer.append($children);
$option.attr('role', 'list');
$option.append(label); $option.append(label);
$option.append($childrenContainer); $option.append($childrenContainer);
@ -1116,7 +1082,7 @@ S2.define('select2/results',[
var data = $highlighted.data('data'); var data = $highlighted.data('data');
if ($highlighted.attr('data-selected') == 'true') { if ($highlighted.attr('aria-selected') == 'true') {
self.trigger('close', {}); self.trigger('close', {});
} else { } else {
self.trigger('select', { self.trigger('select', {
@ -1128,7 +1094,7 @@ S2.define('select2/results',[
container.on('results:previous', function () { container.on('results:previous', function () {
var $highlighted = self.getHighlightedResults(); var $highlighted = self.getHighlightedResults();
var $options = self.$results.find('[data-selected]'); var $options = self.$results.find('[aria-selected]');
var currentIndex = $options.index($highlighted); var currentIndex = $options.index($highlighted);
@ -1162,7 +1128,7 @@ S2.define('select2/results',[
container.on('results:next', function () { container.on('results:next', function () {
var $highlighted = self.getHighlightedResults(); var $highlighted = self.getHighlightedResults();
var $options = self.$results.find('[data-selected]'); var $options = self.$results.find('[aria-selected]');
var currentIndex = $options.index($highlighted); var currentIndex = $options.index($highlighted);
@ -1190,8 +1156,7 @@ S2.define('select2/results',[
}); });
container.on('results:focus', function (params) { container.on('results:focus', function (params) {
params.element.addClass('select2-results__option--highlighted').attr('aria-selected', 'true'); params.element.addClass('select2-results__option--highlighted');
self.$results.attr('aria-activedescendant', params.element.attr('id'));
}); });
container.on('results:message', function (params) { container.on('results:message', function (params) {
@ -1223,13 +1188,13 @@ S2.define('select2/results',[
}); });
} }
this.$results.on('mouseup', '.select2-results__option[data-selected]', this.$results.on('mouseup', '.select2-results__option[aria-selected]',
function (evt) { function (evt) {
var $this = $(this); var $this = $(this);
var data = $this.data('data'); var data = $this.data('data');
if ($this.attr('data-selected') === 'true') { if ($this.attr('aria-selected') === 'true') {
if (self.options.get('multiple')) { if (self.options.get('multiple')) {
self.trigger('unselect', { self.trigger('unselect', {
originalEvent: evt, originalEvent: evt,
@ -1248,13 +1213,12 @@ S2.define('select2/results',[
}); });
}); });
this.$results.on('mouseenter', '.select2-results__option[data-selected]', this.$results.on('mouseenter', '.select2-results__option[aria-selected]',
function (evt) { function (evt) {
var data = $(this).data('data'); var data = $(this).data('data');
self.getHighlightedResults() self.getHighlightedResults()
.removeClass('select2-results__option--highlighted') .removeClass('select2-results__option--highlighted');
.attr('aria-selected', 'false');
self.trigger('results:focus', { self.trigger('results:focus', {
data: data, data: data,
@ -1281,7 +1245,7 @@ S2.define('select2/results',[
return; return;
} }
var $options = this.$results.find('[data-selected]'); var $options = this.$results.find('[aria-selected]');
var currentIndex = $options.index($highlighted); var currentIndex = $options.index($highlighted);
@ -1359,7 +1323,7 @@ S2.define('select2/selection/base',[
BaseSelection.prototype.render = function () { BaseSelection.prototype.render = function () {
var $selection = $( var $selection = $(
'<span class="select2-selection" ' + '<span class="select2-selection" role="combobox" ' +
' aria-haspopup="true" aria-expanded="false">' + ' aria-haspopup="true" aria-expanded="false">' +
'</span>' '</span>'
); );
@ -1385,7 +1349,6 @@ S2.define('select2/selection/base',[
var id = container.id + '-container'; var id = container.id + '-container';
var resultsId = container.id + '-results'; var resultsId = container.id + '-results';
var searchHidden = this.options.get('minimumResultsForSearch') === Infinity;
this.container = container; this.container = container;
@ -1427,11 +1390,7 @@ S2.define('select2/selection/base',[
self.$selection.removeAttr('aria-activedescendant'); self.$selection.removeAttr('aria-activedescendant');
self.$selection.removeAttr('aria-owns'); self.$selection.removeAttr('aria-owns');
// This needs to be delayed as the active element is the body when the self.$selection.focus();
// key is pressed.
window.setTimeout(function () {
self.$selection.trigger( 'focus' );
}, 1);
self._detachCloseHandler(container); self._detachCloseHandler(container);
}); });
@ -1481,14 +1440,8 @@ S2.define('select2/selection/base',[
} }
var $element = $this.data('element'); var $element = $this.data('element');
$element.select2('close');
// Remove any focus when dropdown is closed by clicking outside the select area. $element.select2('close');
// Timeout of 1 required for close to finish wrapping up.
setTimeout(function(){
$this.find('*:focus').blur();
$target.focus();
}, 1);
}); });
}); });
}; };
@ -1547,21 +1500,8 @@ S2.define('select2/selection/single',[
var id = container.id + '-container'; var id = container.id + '-container';
this.$selection.find('.select2-selection__rendered') this.$selection.find('.select2-selection__rendered').attr('id', id);
.attr('id', id) this.$selection.attr('aria-labelledby', id);
.attr('role', 'textbox')
.attr('aria-readonly', 'true');
var label = this.options.get( 'label' );
if ( typeof( label ) === 'string' ) {
this.$selection.attr( 'aria-label', label );
} else {
this.$selection.attr( 'aria-labelledby', id );
}
// This makes single non-search selects work in screen readers. If it causes problems elsewhere, remove.
this.$selection.attr('role', 'combobox');
this.$selection.on('mousedown', function (evt) { this.$selection.on('mousedown', function (evt) {
// Only respond to left clicks // Only respond to left clicks
@ -1578,20 +1518,13 @@ S2.define('select2/selection/single',[
// User focuses on the container // User focuses on the container
}); });
this.$selection.on('keydown', function (evt) {
// If user starts typing an alphanumeric key on the keyboard, open if not opened.
if (!container.isOpen() && evt.which >= 48 && evt.which <= 90) {
container.open();
}
});
this.$selection.on('blur', function (evt) { this.$selection.on('blur', function (evt) {
// User exits the container // User exits the container
}); });
container.on('focus', function (evt) { container.on('focus', function (evt) {
if (!container.isOpen()) { if (!container.isOpen()) {
self.$selection.trigger( 'focus' ); self.$selection.focus();
} }
}); });
@ -1624,9 +1557,9 @@ S2.define('select2/selection/single',[
var selection = data[0]; var selection = data[0];
var $rendered = this.$selection.find('.select2-selection__rendered'); var $rendered = this.$selection.find('.select2-selection__rendered');
var formatted = Utils.entityDecode(this.display(selection, $rendered)); var formatted = this.display(selection, $rendered);
$rendered.empty().text(formatted); $rendered.empty().append(formatted);
$rendered.prop('title', selection.title || selection.text); $rendered.prop('title', selection.title || selection.text);
}; };
@ -1650,7 +1583,7 @@ S2.define('select2/selection/multiple',[
$selection.addClass('select2-selection--multiple'); $selection.addClass('select2-selection--multiple');
$selection.html( $selection.html(
'<ul class="select2-selection__rendered" aria-live="polite" aria-relevant="additions removals" aria-atomic="true"></ul>' '<ul class="select2-selection__rendered"></ul>'
); );
return $selection; return $selection;
@ -1687,18 +1620,6 @@ S2.define('select2/selection/multiple',[
}); });
} }
); );
this.$selection.on('keydown', function (evt) {
// If user starts typing an alphanumeric key on the keyboard, open if not opened.
if (!container.isOpen() && evt.which >= 48 && evt.which <= 90) {
container.open();
}
});
// Focus on the search field when the container is focused instead of the main container.
container.on( 'focus', function(){
self.focusOnSearch();
});
}; };
MultipleSelection.prototype.clear = function () { MultipleSelection.prototype.clear = function () {
@ -1715,7 +1636,7 @@ S2.define('select2/selection/multiple',[
MultipleSelection.prototype.selectionContainer = function () { MultipleSelection.prototype.selectionContainer = function () {
var $container = $( var $container = $(
'<li class="select2-selection__choice">' + '<li class="select2-selection__choice">' +
'<span class="select2-selection__choice__remove" role="presentation" aria-hidden="true">' + '<span class="select2-selection__choice__remove" role="presentation">' +
'&times;' + '&times;' +
'</span>' + '</span>' +
'</li>' '</li>'
@ -1724,24 +1645,6 @@ S2.define('select2/selection/multiple',[
return $container; return $container;
}; };
/**
* Focus on the search field instead of the main multiselect container.
*/
MultipleSelection.prototype.focusOnSearch = function() {
var self = this;
if ('undefined' !== typeof self.$search) {
// Needs 1 ms delay because of other 1 ms setTimeouts when rendering.
setTimeout(function(){
// Prevent the dropdown opening again when focused from this.
// This gets reset automatically when focus is triggered.
self._keyUpPrevented = true;
self.$search.focus();
}, 1);
}
}
MultipleSelection.prototype.update = function (data) { MultipleSelection.prototype.update = function (data) {
this.clear(); this.clear();
@ -1755,14 +1658,9 @@ S2.define('select2/selection/multiple',[
var selection = data[d]; var selection = data[d];
var $selection = this.selectionContainer(); var $selection = this.selectionContainer();
var removeItemTag = $selection.html();
var formatted = this.display(selection, $selection); var formatted = this.display(selection, $selection);
if ('string' === typeof formatted) {
formatted = Utils.entityDecode(formatted.trim());
}
$selection.text(formatted); $selection.append(formatted);
$selection.prepend(removeItemTag);
$selection.prop('title', selection.title || selection.text); $selection.prop('title', selection.title || selection.text);
$selection.data('data', selection); $selection.data('data', selection);
@ -1801,7 +1699,7 @@ S2.define('select2/selection/placeholder',[
Placeholder.prototype.createPlaceholder = function (decorated, placeholder) { Placeholder.prototype.createPlaceholder = function (decorated, placeholder) {
var $placeholder = this.selectionContainer(); var $placeholder = this.selectionContainer();
$placeholder.text(Utils.entityDecode(this.display(placeholder))); $placeholder.html(this.display(placeholder));
$placeholder.addClass('select2-selection__placeholder') $placeholder.addClass('select2-selection__placeholder')
.removeClass('select2-selection__choice'); .removeClass('select2-selection__choice');
@ -1938,8 +1836,8 @@ S2.define('select2/selection/search',[
Search.prototype.render = function (decorated) { Search.prototype.render = function (decorated) {
var $search = $( var $search = $(
'<li class="select2-search select2-search--inline">' + '<li class="select2-search select2-search--inline">' +
'<input class="select2-search__field" type="text" tabindex="-1"' + '<input class="select2-search__field" type="search" tabindex="-1"' +
' autocomplete="off" autocorrect="off" autocapitalize="none"' + ' autocomplete="off" autocorrect="off" autocapitalize="off"' +
' spellcheck="false" role="textbox" aria-autocomplete="list" />' + ' spellcheck="false" role="textbox" aria-autocomplete="list" />' +
'</li>' '</li>'
); );
@ -1956,19 +1854,16 @@ S2.define('select2/selection/search',[
Search.prototype.bind = function (decorated, container, $container) { Search.prototype.bind = function (decorated, container, $container) {
var self = this; var self = this;
var resultsId = container.id + '-results';
decorated.call(this, container, $container); decorated.call(this, container, $container);
container.on('open', function () { container.on('open', function () {
self.$search.attr('aria-owns', resultsId);
self.$search.trigger('focus'); self.$search.trigger('focus');
}); });
container.on('close', function () { container.on('close', function () {
self.$search.val(''); self.$search.val('');
self.$search.removeAttr('aria-activedescendant'); self.$search.removeAttr('aria-activedescendant');
self.$search.removeAttr('aria-owns');
self.$search.trigger('focus'); self.$search.trigger('focus');
}); });
@ -1987,7 +1882,7 @@ S2.define('select2/selection/search',[
}); });
container.on('results:focus', function (params) { container.on('results:focus', function (params) {
self.$search.attr('aria-activedescendant', params.data._resultId); self.$search.attr('aria-activedescendant', params.id);
}); });
this.$selection.on('focusin', '.select2-search--inline', function (evt) { this.$selection.on('focusin', '.select2-search--inline', function (evt) {
@ -2018,9 +1913,6 @@ S2.define('select2/selection/search',[
evt.preventDefault(); evt.preventDefault();
} }
} else if (evt.which === KEYS.ENTER) {
container.open();
evt.preventDefault();
} }
}); });
@ -2109,7 +2001,7 @@ S2.define('select2/selection/search',[
this.resizeSearch(); this.resizeSearch();
if (searchHadFocus) { if (searchHadFocus) {
this.$search.trigger( 'focus' ); this.$search.focus();
} }
}; };
@ -3112,15 +3004,8 @@ S2.define('select2/data/base',[
}; };
BaseAdapter.prototype.generateResultId = function (container, data) { BaseAdapter.prototype.generateResultId = function (container, data) {
var id = ''; var id = container.id + '-result-';
if (container != null) {
id += container.id
} else {
id += Utils.generateChars(4);
}
id += '-result-';
id += Utils.generateChars(4); id += Utils.generateChars(4);
if (data.id != null) { if (data.id != null) {
@ -3306,7 +3191,7 @@ S2.define('select2/data/select',[
} }
} }
if (data.id !== undefined) { if (data.id) {
option.value = data.id; option.value = data.id;
} }
@ -3404,7 +3289,7 @@ S2.define('select2/data/select',[
item.text = item.text.toString(); item.text = item.text.toString();
} }
if (item._resultId == null && item.id) { if (item._resultId == null && item.id && this.container != null) {
item._resultId = this.generateResultId(this.container, item); item._resultId = this.generateResultId(this.container, item);
} }
@ -3547,7 +3432,7 @@ S2.define('select2/data/ajax',[
if (this._request != null) { if (this._request != null) {
// JSONP requests cannot always be aborted // JSONP requests cannot always be aborted
if ( typeof this._request.abort === 'function' ) { if ($.isFunction(this._request.abort)) {
this._request.abort(); this._request.abort();
} }
@ -3572,7 +3457,7 @@ S2.define('select2/data/ajax',[
if (self.options.get('debug') && window.console && console.error) { if (self.options.get('debug') && window.console && console.error) {
// Check to make sure that the response included a `results` key. // Check to make sure that the response included a `results` key.
if (!results || !results.results || ! Array.isArray( results.results ) ) { if (!results || !results.results || !$.isArray(results.results)) {
console.error( console.error(
'Select2: The AJAX results did not return an array in the ' + 'Select2: The AJAX results did not return an array in the ' +
'`results` key of the response.' '`results` key of the response.'
@ -3581,7 +3466,6 @@ S2.define('select2/data/ajax',[
} }
callback(results); callback(results);
self.container.focusOnActiveElement();
}, function () { }, function () {
// Attempt to detect if a request was aborted // Attempt to detect if a request was aborted
// Only works if the transport exposes a status property // Only works if the transport exposes a status property
@ -3631,7 +3515,7 @@ S2.define('select2/data/tags',[
decorated.call(this, $element, options); decorated.call(this, $element, options);
if ( Array.isArray( tags ) ) { if ($.isArray(tags)) {
for (var t = 0; t < tags.length; t++) { for (var t = 0; t < tags.length; t++) {
var tag = tags[t]; var tag = tags[t];
var item = this._normalizeItem(tag); var item = this._normalizeItem(tag);
@ -3666,10 +3550,7 @@ S2.define('select2/data/tags',[
}, true) }, true)
); );
var optionText = (option.text || '').toUpperCase(); var checkText = option.text === params.term;
var paramsTerm = (params.term || '').toUpperCase();
var checkText = optionText === paramsTerm;
if (checkText || checkChildren) { if (checkText || checkChildren) {
if (child) { if (child) {
@ -3707,7 +3588,7 @@ S2.define('select2/data/tags',[
}; };
Tags.prototype.createTag = function (decorated, params) { Tags.prototype.createTag = function (decorated, params) {
var term = ( params.term || 0 == params.term ) ? params.term.toString().trim() : ''; var term = $.trim(params.term);
if (term === '') { if (term === '') {
return null; return null;
@ -3800,7 +3681,7 @@ S2.define('select2/data/tokenizer',[
// Replace the search term if we have the search box // Replace the search term if we have the search box
if (this.$search.length) { if (this.$search.length) {
this.$search.val(tokenData.term); this.$search.val(tokenData.term);
this.$search.trigger( 'focus' ); this.$search.focus();
} }
params.term = tokenData.term; params.term = tokenData.term;
@ -4006,9 +3887,9 @@ S2.define('select2/dropdown/search',[
var $search = $( var $search = $(
'<span class="select2-search select2-search--dropdown">' + '<span class="select2-search select2-search--dropdown">' +
'<input class="select2-search__field" type="text" tabindex="-1"' + '<input class="select2-search__field" type="search" tabindex="-1"' +
' autocomplete="off" autocorrect="off" autocapitalize="none"' + ' autocomplete="off" autocorrect="off" autocapitalize="off"' +
' spellcheck="false" role="combobox" aria-autocomplete="list" aria-expanded="true" />' + ' spellcheck="false" role="textbox" />' +
'</span>' '</span>'
); );
@ -4022,7 +3903,6 @@ S2.define('select2/dropdown/search',[
Search.prototype.bind = function (decorated, container, $container) { Search.prototype.bind = function (decorated, container, $container) {
var self = this; var self = this;
var resultsId = container.id + '-results';
decorated.call(this, container, $container); decorated.call(this, container, $container);
@ -4046,24 +3926,23 @@ S2.define('select2/dropdown/search',[
container.on('open', function () { container.on('open', function () {
self.$search.attr('tabindex', 0); self.$search.attr('tabindex', 0);
self.$search.attr('aria-owns', resultsId);
self.$search.trigger( 'focus' ); self.$search.focus();
window.setTimeout(function () { window.setTimeout(function () {
self.$search.trigger( 'focus' ); self.$search.focus();
}, 0); }, 0);
}); });
container.on('close', function () { container.on('close', function () {
self.$search.attr('tabindex', -1); self.$search.attr('tabindex', -1);
self.$search.removeAttr('aria-activedescendant');
self.$search.removeAttr('aria-owns');
self.$search.val(''); self.$search.val('');
}); });
container.on('focus', function () { container.on('focus', function () {
if (!container.isOpen()) { if (container.isOpen()) {
self.$search.trigger( 'focus' ); self.$search.focus();
} }
}); });
@ -4078,10 +3957,6 @@ S2.define('select2/dropdown/search',[
} }
} }
}); });
container.on('results:focus', function (params) {
self.$search.attr('aria-activedescendant', params.data._resultId);
});
}; };
Search.prototype.handleSearch = function (evt) { Search.prototype.handleSearch = function (evt) {
@ -4223,7 +4098,7 @@ S2.define('select2/dropdown/infiniteScroll',[
var $option = $( var $option = $(
'<li ' + '<li ' +
'class="select2-results__option select2-results__option--load-more"' + 'class="select2-results__option select2-results__option--load-more"' +
'role="option" aria-disabled="true"></li>' 'role="treeitem" aria-disabled="true"></li>'
); );
var message = this.options.get('translations').get('loadingMore'); var message = this.options.get('translations').get('loadingMore');
@ -4878,7 +4753,7 @@ S2.define('select2/defaults',[
} }
} }
if ( Array.isArray( options.language ) ) { if ($.isArray(options.language)) {
var languages = new Translation(); var languages = new Translation();
options.language.push('en'); options.language.push('en');
@ -4941,7 +4816,7 @@ S2.define('select2/defaults',[
function matcher (params, data) { function matcher (params, data) {
// Always return the object if there is nothing to compare // Always return the object if there is nothing to compare
if ( params.term == null || params.term.toString().trim() === '' ) { if ($.trim(params.term) === '') {
return data; return data;
} }
@ -5469,22 +5344,16 @@ S2.define('select2/core',[
}); });
}); });
this.on('open', function(){ this.on('keypress', function (evt) {
// Focus on the active element when opening dropdown.
// Needs 1 ms delay because of other 1 ms setTimeouts when rendering.
setTimeout(function(){
self.focusOnActiveElement();
}, 1);
});
$(document).on('keydown', function (evt) {
var key = evt.which; var key = evt.which;
if (self.isOpen()) { if (self.isOpen()) {
if (key === KEYS.ESC || (key === KEYS.UP && evt.altKey)) { if (key === KEYS.ESC || key === KEYS.TAB ||
(key === KEYS.UP && evt.altKey)) {
self.close(); self.close();
evt.preventDefault(); evt.preventDefault();
} else if (key === KEYS.ENTER || key === KEYS.TAB) { } else if (key === KEYS.ENTER) {
self.trigger('results:select', {}); self.trigger('results:select', {});
evt.preventDefault(); evt.preventDefault();
@ -5501,42 +5370,17 @@ S2.define('select2/core',[
evt.preventDefault(); evt.preventDefault();
} }
} else {
var $searchField = self.$dropdown.find('.select2-search__field');
if (! $searchField.length) {
$searchField = self.$container.find('.select2-search__field');
}
// Move the focus to the selected element on keyboard navigation.
// Required for screen readers to work properly.
if (key === KEYS.DOWN || key === KEYS.UP) {
self.focusOnActiveElement();
} else {
// Focus on the search if user starts typing.
$searchField.focus();
// Focus back to active selection when finished typing.
// Small delay so typed character can be read by screen reader.
setTimeout(function(){
self.focusOnActiveElement();
}, 1000);
}
} else if (self.hasFocus()) {
if (key === KEYS.ENTER || key === KEYS.SPACE || if (key === KEYS.ENTER || key === KEYS.SPACE ||
key === KEYS.DOWN) { (key === KEYS.DOWN && evt.altKey)) {
self.open(); self.open();
evt.preventDefault(); evt.preventDefault();
} }
} }
}); });
}; };
Select2.prototype.focusOnActiveElement = function () {
// Don't mess with the focus on touchscreens because it causes havoc with on-screen keyboards.
if (this.isOpen() && ! Utils.isTouchscreen()) {
this.$results.find('li.select2-results__option--highlighted').focus();
}
};
Select2.prototype._syncAttributes = function () { Select2.prototype._syncAttributes = function () {
this.options.set('disabled', this.$element.prop('disabled')); this.options.set('disabled', this.$element.prop('disabled'));
@ -5724,7 +5568,7 @@ S2.define('select2/core',[
var newVal = args[0]; var newVal = args[0];
if ( Array.isArray( newVal ) ) { if ($.isArray(newVal)) {
newVal = $.map(newVal, function (obj) { newVal = $.map(newVal, function (obj) {
return obj.toString(); return obj.toString();
}); });
@ -5809,11 +5653,11 @@ S2.define('jquery.select2',[
'./select2/core', './select2/core',
'./select2/defaults' './select2/defaults'
], function ($, _, Select2, Defaults) { ], function ($, _, Select2, Defaults) {
if ($.fn.selectWoo == null) { if ($.fn.select2 == null) {
// All methods that should return the element // All methods that should return the element
var thisMethods = ['open', 'close', 'destroy']; var thisMethods = ['open', 'close', 'destroy'];
$.fn.selectWoo = function (options) { $.fn.select2 = function (options) {
options = options || {}; options = options || {};
if (typeof options === 'object') { if (typeof options === 'object') {
@ -5853,17 +5697,10 @@ S2.define('jquery.select2',[
}; };
} }
if ($.fn.select2 != null && $.fn.select2.defaults != null) { if ($.fn.select2.defaults == null) {
$.fn.selectWoo.defaults = $.fn.select2.defaults; $.fn.select2.defaults = Defaults;
} }
if ($.fn.selectWoo.defaults == null) {
$.fn.selectWoo.defaults = Defaults;
}
// Also register selectWoo under select2 if select2 is not already present.
$.fn.select2 = $.fn.select2 || $.fn.selectWoo;
return Select2; return Select2;
}); });
@ -5882,7 +5719,6 @@ S2.define('jquery.select2',[
// This allows Select2 to use the internal loader outside of this file, such // This allows Select2 to use the internal loader outside of this file, such
// as in the language files. // as in the language files.
jQuery.fn.select2.amd = S2; jQuery.fn.select2.amd = S2;
jQuery.fn.selectWoo.amd = S2;
// Return the Select2 instance for anyone who is importing it. // Return the Select2 instance for anyone who is importing it.
return select2; return select2;

View File

@ -68,16 +68,16 @@
}, },
{ {
"name": "league/flysystem", "name": "league/flysystem",
"version": "1.1.4", "version": "1.1.5",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/thephpleague/flysystem.git", "url": "https://github.com/thephpleague/flysystem.git",
"reference": "f3ad69181b8afed2c9edf7be5a2918144ff4ea32" "reference": "18634df356bfd4119fe3d6156bdb990c414c14ea"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/f3ad69181b8afed2c9edf7be5a2918144ff4ea32", "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/18634df356bfd4119fe3d6156bdb990c414c14ea",
"reference": "f3ad69181b8afed2c9edf7be5a2918144ff4ea32", "reference": "18634df356bfd4119fe3d6156bdb990c414c14ea",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -150,7 +150,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/thephpleague/flysystem/issues", "issues": "https://github.com/thephpleague/flysystem/issues",
"source": "https://github.com/thephpleague/flysystem/tree/1.1.4" "source": "https://github.com/thephpleague/flysystem/tree/1.1.5"
}, },
"funding": [ "funding": [
{ {
@ -158,7 +158,7 @@
"type": "other" "type": "other"
} }
], ],
"time": "2021-06-23T21:56:05+00:00" "time": "2021-08-17T13:49:42+00:00"
}, },
{ {
"name": "league/mime-type-detection", "name": "league/mime-type-detection",

View File

@ -25,7 +25,7 @@
* Dev - Apply `woocommerce_logout_default_redirect_url` filter to logout for custom endpoint. #29967 * Dev - Apply `woocommerce_logout_default_redirect_url` filter to logout for custom endpoint. #29967
* Dev - Added new `woocommerce_email_sent` hook. #30123 * Dev - Added new `woocommerce_email_sent` hook. #30123
**WooCommerce Admin - 2.5.0** **WooCommerce Admin - 2.5.0 & 2.5.1**
- Add - Add a delete option to completed tasks #7300 - Add - Add a delete option to completed tasks #7300
- Add - Add unit tests around extended payment gateway controller #7133 - Add - Add unit tests around extended payment gateway controller #7133
@ -79,6 +79,7 @@
- Fix - Fix analytics overview re-arrangement on initial load. #7475 - Fix - Fix analytics overview re-arrangement on initial load. #7475
- Fix - Fixes action button mis-alignment within card footer. #7412 - Fix - Fixes action button mis-alignment within card footer. #7412
- Fix - Fix up onboarding profiler not working when opted out of tracking - Fix - Fix up onboarding profiler not working when opted out of tracking
- Fix - Fix blank screen by setting a default value #7506
- Tweak - Remove performance indicators when Analytics Flag disabled #7234 - Tweak - Remove performance indicators when Analytics Flag disabled #7234
- Tweak - Change event name when installing Google Listings and Ads. #7276 - Tweak - Change event name when installing Google Listings and Ads. #7276
- Tweak - Removed unused feature flags #7233 and #7273 - Tweak - Removed unused feature flags #7233 and #7273

View File

@ -21,7 +21,7 @@
"pelago/emogrifier": "3.1.0", "pelago/emogrifier": "3.1.0",
"psr/container": "1.0.0", "psr/container": "1.0.0",
"woocommerce/action-scheduler": "3.2.1", "woocommerce/action-scheduler": "3.2.1",
"woocommerce/woocommerce-admin": "2.5.0", "woocommerce/woocommerce-admin": "2.5.1",
"woocommerce/woocommerce-blocks": "5.5.1" "woocommerce/woocommerce-blocks": "5.5.1"
}, },
"require-dev": { "require-dev": {

14
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "e66790f6451aac2d4cb8f418bb26268e", "content-hash": "f1450dbdfedc77735b411ae8a019b534",
"packages": [ "packages": [
{ {
"name": "automattic/jetpack-autoloader", "name": "automattic/jetpack-autoloader",
@ -532,16 +532,16 @@
}, },
{ {
"name": "woocommerce/woocommerce-admin", "name": "woocommerce/woocommerce-admin",
"version": "2.5.0", "version": "2.5.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/woocommerce/woocommerce-admin.git", "url": "https://github.com/woocommerce/woocommerce-admin.git",
"reference": "99a0bd3a48bf33a054efd3b0df29bf42766761f8" "reference": "093d698d770f49d41df8abe89a716dc897bb9e96"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/woocommerce/woocommerce-admin/zipball/99a0bd3a48bf33a054efd3b0df29bf42766761f8", "url": "https://api.github.com/repos/woocommerce/woocommerce-admin/zipball/093d698d770f49d41df8abe89a716dc897bb9e96",
"reference": "99a0bd3a48bf33a054efd3b0df29bf42766761f8", "reference": "093d698d770f49d41df8abe89a716dc897bb9e96",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -596,9 +596,9 @@
"homepage": "https://github.com/woocommerce/woocommerce-admin", "homepage": "https://github.com/woocommerce/woocommerce-admin",
"support": { "support": {
"issues": "https://github.com/woocommerce/woocommerce-admin/issues", "issues": "https://github.com/woocommerce/woocommerce-admin/issues",
"source": "https://github.com/woocommerce/woocommerce-admin/tree/v2.5.0" "source": "https://github.com/woocommerce/woocommerce-admin/tree/v2.5.1"
}, },
"time": "2021-08-09T21:00:32+00:00" "time": "2021-08-16T14:31:33+00:00"
}, },
{ {
"name": "woocommerce/woocommerce-blocks", "name": "woocommerce/woocommerce-blocks",

View File

@ -226,7 +226,7 @@ final class WC_Cart_Totals {
$item->taxable = 'taxable' === $cart_item['data']->get_tax_status(); $item->taxable = 'taxable' === $cart_item['data']->get_tax_status();
$item->price_includes_tax = wc_prices_include_tax(); $item->price_includes_tax = wc_prices_include_tax();
$item->quantity = $cart_item['quantity']; $item->quantity = $cart_item['quantity'];
$item->price = wc_add_number_precision_deep( $cart_item['data']->get_price() * $cart_item['quantity'] ); $item->price = wc_add_number_precision_deep( (float) $cart_item['data']->get_price() * (float) $cart_item['quantity'] );
$item->product = $cart_item['data']; $item->product = $cart_item['data'];
$item->tax_rates = $this->get_item_tax_rates( $item ); $item->tax_rates = $this->get_item_tax_rates( $item );
$this->items[ $cart_item_key ] = $item; $this->items[ $cart_item_key ] = $item;

View File

@ -809,6 +809,7 @@ class WC_Countries {
'AF' => array( 'AF' => array(
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'AO' => array( 'AO' => array(
@ -826,6 +827,7 @@ class WC_Countries {
), ),
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'AU' => array( 'AU' => array(
@ -845,6 +847,7 @@ class WC_Countries {
), ),
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'BA' => array( 'BA' => array(
@ -871,7 +874,7 @@ class WC_Countries {
), ),
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'label' => __( 'Province', 'woocommerce' ), 'hidden' => true,
), ),
), ),
'BH' => array( 'BH' => array(
@ -880,11 +883,13 @@ class WC_Countries {
), ),
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'BI' => array( 'BI' => array(
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'BO' => array( 'BO' => array(
@ -949,6 +954,7 @@ class WC_Countries {
'CZ' => array( 'CZ' => array(
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'DE' => array( 'DE' => array(
@ -975,6 +981,7 @@ class WC_Countries {
), ),
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'FI' => array( 'FI' => array(
@ -983,6 +990,7 @@ class WC_Countries {
), ),
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'FR' => array( 'FR' => array(
@ -991,6 +999,7 @@ class WC_Countries {
), ),
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'GH' => array( 'GH' => array(
@ -1004,11 +1013,13 @@ class WC_Countries {
'GP' => array( 'GP' => array(
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'GF' => array( 'GF' => array(
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'GR' => array( 'GR' => array(
@ -1082,6 +1093,7 @@ class WC_Countries {
), ),
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'IL' => array( 'IL' => array(
@ -1090,11 +1102,13 @@ class WC_Countries {
), ),
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'IM' => array( 'IM' => array(
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'IN' => array( 'IN' => array(
@ -1158,11 +1172,13 @@ class WC_Countries {
'KR' => array( 'KR' => array(
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'KW' => array( 'KW' => array(
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'LV' => array( 'LV' => array(
@ -1174,16 +1190,19 @@ class WC_Countries {
'LB' => array( 'LB' => array(
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'MQ' => array( 'MQ' => array(
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'MT' => array( 'MT' => array(
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'MZ' => array( 'MZ' => array(
@ -1201,7 +1220,7 @@ class WC_Countries {
), ),
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'label' => __( 'Province', 'woocommerce' ), 'hidden' => true,
), ),
), ),
'NG' => array( 'NG' => array(
@ -1229,6 +1248,7 @@ class WC_Countries {
), ),
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'NP' => array( 'NP' => array(
@ -1245,6 +1265,7 @@ class WC_Countries {
), ),
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'PR' => array( 'PR' => array(
@ -1259,11 +1280,13 @@ class WC_Countries {
'PT' => array( 'PT' => array(
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'RE' => array( 'RE' => array(
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'RO' => array( 'RO' => array(
@ -1287,6 +1310,7 @@ class WC_Countries {
'SG' => array( 'SG' => array(
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
'city' => array( 'city' => array(
'required' => false, 'required' => false,
@ -1298,6 +1322,7 @@ class WC_Countries {
), ),
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'SI' => array( 'SI' => array(
@ -1306,6 +1331,7 @@ class WC_Countries {
), ),
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'SR' => array( 'SR' => array(
@ -1334,11 +1360,13 @@ class WC_Countries {
'LK' => array( 'LK' => array(
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'LU' => array( 'LU' => array(
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'MD' => array( 'MD' => array(
@ -1427,6 +1455,7 @@ class WC_Countries {
'YT' => array( 'YT' => array(
'state' => array( 'state' => array(
'required' => false, 'required' => false,
'hidden' => true,
), ),
), ),
'ZA' => array( 'ZA' => array(

View File

@ -84,7 +84,7 @@ class WC_Discounts {
$item->object = $cart_item; $item->object = $cart_item;
$item->product = $cart_item['data']; $item->product = $cart_item['data'];
$item->quantity = $cart_item['quantity']; $item->quantity = $cart_item['quantity'];
$item->price = wc_add_number_precision_deep( $item->product->get_price() * $item->quantity ); $item->price = wc_add_number_precision_deep( (float) $item->product->get_price() * (float) $item->quantity );
$this->items[ $key ] = $item; $this->items[ $key ] = $item;
} }

View File

@ -160,9 +160,6 @@ class WC_Tracker {
// Template overrides. // Template overrides.
$data['template_overrides'] = self::get_all_template_overrides(); $data['template_overrides'] = self::get_all_template_overrides();
// Template overrides.
$data['admin_user_agents'] = self::get_admin_user_agents();
// Cart & checkout tech (blocks or shortcodes). // Cart & checkout tech (blocks or shortcodes).
$data['cart_checkout'] = self::get_cart_checkout_info(); $data['cart_checkout'] = self::get_cart_checkout_info();
@ -619,6 +616,8 @@ class WC_Tracker {
'version' => WC()->version, 'version' => WC()->version,
'currency' => get_woocommerce_currency(), 'currency' => get_woocommerce_currency(),
'base_location' => WC()->countries->get_base_country(), 'base_location' => WC()->countries->get_base_country(),
'base_state' => WC()->countries->get_base_state(),
'base_postcode' => WC()->countries->get_base_postcode(),
'selling_locations' => WC()->countries->get_allowed_countries(), 'selling_locations' => WC()->countries->get_allowed_countries(),
'api_enabled' => get_option( 'woocommerce_api_enabled' ), 'api_enabled' => get_option( 'woocommerce_api_enabled' ),
'weight_unit' => get_option( 'woocommerce_weight_unit' ), 'weight_unit' => get_option( 'woocommerce_weight_unit' ),
@ -675,15 +674,6 @@ class WC_Tracker {
return $override_data; return $override_data;
} }
/**
* When an admin user logs in, there user agent is tracked in user meta and collected here.
*
* @return array
*/
private static function get_admin_user_agents() {
return array_filter( (array) get_option( 'woocommerce_tracker_ua', array() ) );
}
/** /**
* Search a specific post for text content. * Search a specific post for text content.
* *

View File

@ -1315,8 +1315,6 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
AND p.post_type = 'product' AND p.post_type = 'product'
", ",
'orderby' => '
ORDER BY RAND()',
'limits' => ' 'limits' => '
LIMIT ' . absint( $limit ) . ' LIMIT ' . absint( $limit ) . '
', ',

View File

@ -3532,9 +3532,9 @@
"dev": true "dev": true
}, },
"path-parse": { "path-parse": {
"version": "1.0.6", "version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true "dev": true
}, },
"performance-now": { "performance-now": {

View File

@ -8,6 +8,7 @@ import {
const config = require('config'); const config = require('config');
const { HTTPClientFactory } = require('@woocommerce/api'); const { HTTPClientFactory } = require('@woocommerce/api');
const { addConsoleSuppression, updateReadyPageStatus } = require( '@woocommerce/e2e-environment' ); const { addConsoleSuppression, updateReadyPageStatus } = require( '@woocommerce/e2e-environment' );
const { DEFAULT_TIMEOUT_OVERRIDE } = process.env;
// @todo: remove this once https://github.com/woocommerce/woocommerce-admin/issues/6992 has been addressed // @todo: remove this once https://github.com/woocommerce/woocommerce-admin/issues/6992 has been addressed
addConsoleSuppression( 'woocommerce_shared_settings' ); addConsoleSuppression( 'woocommerce_shared_settings' );
@ -38,6 +39,12 @@ async function trashExistingPosts() {
// other posts/comments/etc. aren't dirtying tests and tests don't depend on // other posts/comments/etc. aren't dirtying tests and tests don't depend on
// each other's side-effects. // each other's side-effects.
beforeAll(async () => { beforeAll(async () => {
if ( DEFAULT_TIMEOUT_OVERRIDE ) {
page.setDefaultNavigationTimeout( DEFAULT_TIMEOUT_OVERRIDE );
page.setDefaultTimeout( DEFAULT_TIMEOUT_OVERRIDE );
}
// Update the ready page to prevent concurrent test runs // Update the ready page to prevent concurrent test runs
await updateReadyPageStatus('draft'); await updateReadyPageStatus('draft');

View File

@ -1,9 +1,9 @@
/* eslint-disable jest/no-export, jest/no-disabled-tests, jest/expect-expect */
/** /**
* Internal dependencies * Internal dependencies
*/ */
const { const {
shopper, shopper,
withRestApi,
createSimpleProduct, createSimpleProduct,
uiUnblocked uiUnblocked
} = require( '@woocommerce/e2e-utils' ); } = require( '@woocommerce/e2e-utils' );
@ -26,6 +26,9 @@ const runCartPageTest = () => {
describe('Cart page', () => { describe('Cart page', () => {
beforeAll(async () => { beforeAll(async () => {
await createSimpleProduct(); await createSimpleProduct();
await withRestApi.resetSettingsGroupToDefault('general');
await withRestApi.resetSettingsGroupToDefault('products');
await withRestApi.resetSettingsGroupToDefault('tax');
}); });
it('should display no item in the cart', async () => { it('should display no item in the cart', async () => {

View File

@ -1,10 +1,10 @@
/* eslint-disable jest/no-export, jest/no-disabled-tests, jest/expect-expect, jest/no-standalone-expect */
/** /**
* Internal dependencies * Internal dependencies
*/ */
const { const {
shopper, shopper,
merchant, merchant,
withRestApi,
createSimpleProduct, createSimpleProduct,
setCheckbox, setCheckbox,
settingsPageSaveChanges, settingsPageSaveChanges,
@ -28,6 +28,9 @@ const runCheckoutPageTest = () => {
describe('Checkout page', () => { describe('Checkout page', () => {
beforeAll(async () => { beforeAll(async () => {
await createSimpleProduct(); await createSimpleProduct();
await withRestApi.resetSettingsGroupToDefault('general');
await withRestApi.resetSettingsGroupToDefault('products');
await withRestApi.resetSettingsGroupToDefault('tax');
// Set free shipping within California // Set free shipping within California
await merchant.login(); await merchant.login();

View File

@ -1,11 +1,13 @@
# Unreleased # Unreleased
- `updateReadyPageStatus` utility to update the status of the ready page - `updateReadyPageStatus` utility to update the status of the ready page.
- Added plugin upload functionality util that provides a method to pull a plugin zip from a remote location - Added plugin upload functionality util that provides a method to pull a plugin zip from a remote location:
- `getRemotePluginZip( fileUrl )` to get the remote zip. Returns the filepath of the zip location. - `getRemotePluginZip( fileUrl )` to get the remote zip. Returns the filepath of the zip location.
- Added plugin zip utility functions: - Added plugin zip utility functions:
- `checkNestedZip( zipFilePath, savePath )` checks a plugin zip file for any nested zip files. If one is found, it is extracted. Returns the path where the zip file is located. - `checkNestedZip( zipFilePath, savePath )` checks a plugin zip file for any nested zip files. If one is found, it is extracted. Returns the path where the zip file is located.
- `downloadZip( fileUrl, downloadPath )` downloads a plugin zip file from a remote location to the provided path. - `downloadZip( fileUrl, downloadPath )` downloads a plugin zip file from a remote location to the provided path.
- Added `getLatestReleaseZipUrl( owner, repository, getPrerelease, perPage )` util function to get the latest release zip from a GitHub repository.
- Added `DEFAULT_TIMEOUT_OVERRIDE` that allows passing in a time in milliseconds to override the default Jest and Puppeteer timeouts.
# 0.2.2 # 0.2.2

View File

@ -87,6 +87,21 @@ await takeScreenshotFor( 'name of current step' );
Screenshots will be saved to `tests/e2e/screenshots`. This folder is cleared at the beginning of each test run. Screenshots will be saved to `tests/e2e/screenshots`. This folder is cleared at the beginning of each test run.
### Override default test timeout
To override the default timeout for the tests, you can use the `DEFAULT_TIMEOUT_OVERRIDE` flag and pass in a maximum timeout in milliseconds. For example, you can pass it in when running the tests from the command line:
```bash
DEFAULT_TIMEOUT_OVERRIDE=35000 npx wc-e2e test:e2e
```
This value will override the default Jest timeout as well as pass the timeout to the following Puppeteer methods:
* page.setDefaultTimeout();
* page.setDefaultNavigationTimeout();
For a list of the methods that the above timeout affects, please see the Puppeteer documentation for [`page.setDefaultTimeout()`](https://pptr.dev/#?product=Puppeteer&version=v10.2.0&show=api-pagesetdefaulttimeouttimeout) and [`page.setDefaultNavigationTimeout`](https://pptr.dev/#?product=Puppeteer&version=v10.2.0&show=api-pagesetdefaultnavigationtimeouttimeout) for more information.
### Jest Puppeteer Config ### Jest Puppeteer Config
The test sequencer uses the following default Puppeteer configuration: The test sequencer uses the following default Puppeteer configuration:
@ -196,6 +211,14 @@ The above method also makes use of the following utility methods which can also
- `checkNestedZip( zipFilePath, savePath )` used to check a plugin zip file for any nested zip files. If one is found, it is extracted. Returns the path where the zip file is located. - `checkNestedZip( zipFilePath, savePath )` used to check a plugin zip file for any nested zip files. If one is found, it is extracted. Returns the path where the zip file is located.
- `downloadZip( fileUrl, downloadPath )` can be used to directly download a plugin zip file from a remote location to the provided path. - `downloadZip( fileUrl, downloadPath )` can be used to directly download a plugin zip file from a remote location to the provided path.
### Get the latest released zip URL
If you would like to get the latest release zip URL, which can be used in the methods mentioned above, you can use the following helper function to do so:
`getLatestReleaseZipUrl( owner, repository, getPrerelease, perPage )`
This will return a string with the latest release URL. Optionally, you can use the `getPrerelease` boolean flag, which defaults to false, on whether or not to get a prerelease instead. The `perPage` flag can be used to return more results when getting the list of releases. The default value is 3.
## Additional information ## Additional information
Refer to [`tests/e2e/core-tests`](https://github.com/woocommerce/woocommerce/tree/trunk/tests/e2e/core-tests) for some test examples, and [`tests/e2e`](https://github.com/woocommerce/woocommerce/tree/trunk/tests/e2e) for general information on e2e tests. Refer to [`tests/e2e/core-tests`](https://github.com/woocommerce/woocommerce/tree/trunk/tests/e2e/core-tests) for some test examples, and [`tests/e2e`](https://github.com/woocommerce/woocommerce/tree/trunk/tests/e2e) for general information on e2e tests.

View File

@ -5,7 +5,7 @@ const program = require( 'commander' );
const path = require( 'path' ); const path = require( 'path' );
const fs = require( 'fs' ); const fs = require( 'fs' );
const { getAppRoot } = require( '../utils' ); const { getAppRoot } = require( '../utils' );
const { WC_E2E_SCREENSHOTS, JEST_PUPPETEER_CONFIG } = process.env; const { WC_E2E_SCREENSHOTS, JEST_PUPPETEER_CONFIG, DEFAULT_TIMEOUT_OVERRIDE } = process.env;
program program
.usage( '<file ...> [options]' ) .usage( '<file ...> [options]' )
@ -43,6 +43,10 @@ let testEnvVars = {
jest_test_timeout: program.dev ? 120000 : 30000, jest_test_timeout: program.dev ? 120000 : 30000,
}; };
if ( DEFAULT_TIMEOUT_OVERRIDE ) {
testEnvVars.jest_test_timeout = DEFAULT_TIMEOUT_OVERRIDE;
}
if ( ! JEST_PUPPETEER_CONFIG ) { if ( ! JEST_PUPPETEER_CONFIG ) {
// Use local Puppeteer config if there is one. // Use local Puppeteer config if there is one.
// Load test configuration file into an object. // Load test configuration file into an object.

View File

@ -29,6 +29,52 @@ const getRemotePluginZip = async ( fileUrl ) => {
return filePath; return filePath;
}; };
/**
* Get the latest release zip for a plugin from a GiHub repository.
*
* @param {string} owner The owner of the plugin repository.
* @param {string} repository The repository name.
* @param {boolean} getPrerelease Flag on whether to get a prelease or not.
* @param {number} perPage Limit of entries returned from the latest releases list, defaults to 3.
* @returns {Promise<string>}} Returns the URL for the release zip file.
*/
const getLatestReleaseZipUrl = async ( owner, repository, getPrerelease = false, perPage = 3 ) => {
let requesturl;
if ( getPrerelease ) {
requesturl = `https://api.github.com/repos/${owner}/${repository}/releases?per_page=${perPage}`;
} else {
requesturl = `https://api.github.com/repos/${owner}/${repository}/releases/latest`;
}
const options = {
url: requesturl,
method: 'GET',
json: true,
headers: {'user-agent': 'node.js'}
};
// Wrap in a promise to make the request async
return new Promise( function( resolve, reject ) {
request.get(options, function( err, resp, body ) {
if ( err ) {
reject( err );
} else {
if ( getPrerelease ) {
// Loop until we find the first pre-release, then return it.
body.forEach(release => {
if ( release.prerelease ) {
resolve( release.zipball_url );
}
});
} else {
resolve( body.zipball_url );
}
}
})
});
}
/** /**
* Check the zip file for any nested zips. If one is found, extract it. * Check the zip file for any nested zips. If one is found, extract it.
* *
@ -80,6 +126,7 @@ const downloadZip = async ( fileUrl, downloadPath ) => {
module.exports = { module.exports = {
getRemotePluginZip, getRemotePluginZip,
getLatestReleaseZipUrl,
checkNestedZip, checkNestedZip,
downloadZip, downloadZip,
}; };

View File

@ -1,7 +1,7 @@
const getAppRoot = require( './app-root' ); const getAppRoot = require( './app-root' );
const { getAppName, getAppBase } = require( './app-name' ); const { getAppName, getAppBase } = require( './app-name' );
const { getTestConfig, getAdminConfig } = require( './test-config' ); const { getTestConfig, getAdminConfig } = require( './test-config' );
const { getRemotePluginZip } = require('./get-plugin-zip'); const { getRemotePluginZip, getLatestReleaseZipUrl } = require('./get-plugin-zip');
const takeScreenshotFor = require( './take-screenshot' ); const takeScreenshotFor = require( './take-screenshot' );
const updateReadyPageStatus = require('./update-ready-page'); const updateReadyPageStatus = require('./update-ready-page');
const consoleUtils = require( './filter-console' ); const consoleUtils = require( './filter-console' );
@ -13,6 +13,7 @@ module.exports = {
getTestConfig, getTestConfig,
getAdminConfig, getAdminConfig,
getRemotePluginZip, getRemotePluginZip,
getLatestReleaseZipUrl,
takeScreenshotFor, takeScreenshotFor,
updateReadyPageStatus, updateReadyPageStatus,
...consoleUtils, ...consoleUtils,

View File

@ -3,7 +3,7 @@
*/ */
const { merchant, utils } = require( '@woocommerce/e2e-utils' ); const { merchant, utils } = require( '@woocommerce/e2e-utils' );
const { getRemotePluginZip } = require( '@woocommerce/e2e-environment' ); const { getRemotePluginZip, getLatestReleaseZipUrl } = require( '@woocommerce/e2e-environment' );
/** /**
* External dependencies * External dependencies
@ -13,16 +13,23 @@ const {
beforeAll, beforeAll,
} = require( '@jest/globals' ); } = require( '@jest/globals' );
const { UPDATE_WC } = process.env; const { UPDATE_WC, TEST_RELEASE } = process.env;
const nightlyZip = 'https://github.com/woocommerce/woocommerce/releases/download/nightly/woocommerce-trunk-nightly.zip'; let zipUrl;
const pluginName = 'WooCommerce'; const pluginName = 'WooCommerce';
let pluginPath; let pluginPath;
utils.describeIf( UPDATE_WC )( 'WooCommerce plugin can be uploaded and activated', () => { utils.describeIf( UPDATE_WC )( 'WooCommerce plugin can be uploaded and activated', () => {
beforeAll( async () => { beforeAll( async () => {
pluginPath = await getRemotePluginZip( nightlyZip );
if ( TEST_RELEASE ) {
zipUrl = await getLatestReleaseZipUrl('woocommerce', 'woocommerce');
} else {
zipUrl = 'https://github.com/woocommerce/woocommerce/releases/download/nightly/woocommerce-trunk-nightly.zip';
}
pluginPath = await getRemotePluginZip( zipUrl );
await merchant.login(); await merchant.login();
}); });
@ -34,4 +41,9 @@ utils.describeIf( UPDATE_WC )( 'WooCommerce plugin can be uploaded and activated
await merchant.uploadAndActivatePlugin( pluginPath, pluginName ); await merchant.uploadAndActivatePlugin( pluginPath, pluginName );
}); });
it( 'can run the database update', async () => {
// Check for, and run, the database upgrade if needed
await merchant.runDatabaseUpdate();
});
}); });

View File

@ -12,6 +12,7 @@
- Added `describeIf()` to conditionally run a test suite - Added `describeIf()` to conditionally run a test suite
- Added `itIf()` to conditionally run a test case. - Added `itIf()` to conditionally run a test case.
- Added merchant workflows around plugins: `uploadAndActivatePlugin()`, `activatePlugin()`, `deactivatePlugin()`, `deletePlugin()` - Added merchant workflows around plugins: `uploadAndActivatePlugin()`, `activatePlugin()`, `deactivatePlugin()`, `deletePlugin()`
- Added merchant workflows checking for a database update and performing the update if needed: `runDatabaseUpdate()`
- Added `deleteAllOrders()` that goes through and deletes all orders - Added `deleteAllOrders()` that goes through and deletes all orders
- Added `deleteAllShippingClasses()` which permanently deletes all shipping classes using the API - Added `deleteAllShippingClasses()` which permanently deletes all shipping classes using the API
- Added `statuses` optional parameter to `deleteAllRepositoryObjects()` to delete on specific statuses - Added `statuses` optional parameter to `deleteAllRepositoryObjects()` to delete on specific statuses

View File

@ -109,6 +109,7 @@ This package provides support for enabling retries in tests:
| `updateWordPress` | | Install pending WordPress updates on Dashboard -> Updates| | `updateWordPress` | | Install pending WordPress updates on Dashboard -> Updates|
| `updatePlugins` | | Install all pending plugin updates on Dashboard -> Updates| | `updatePlugins` | | Install all pending plugin updates on Dashboard -> Updates|
| `updateThemes` | | Install all pending theme updates on Dashboard -> Updates| | `updateThemes` | | Install all pending theme updates on Dashboard -> Updates|
| `runDatabaseUpdate` || Runs the database update if needed |
### Shopper `shopper` ### Shopper `shopper`
@ -133,7 +134,7 @@ This package provides support for enabling retries in tests:
| `removeFromCart` | `productTitle` | Remove a product from the cart on the cart page | | `removeFromCart` | `productTitle` | Remove a product from the cart on the cart page |
| `setCartQuantity` | `productTitle, quantityValue` | Change the quantity of a product on the cart page | | `setCartQuantity` | `productTitle, quantityValue` | Change the quantity of a product on the cart page |
| `searchForProduct` | | Searching for a product name and landing on its detail page | | `searchForProduct` | | Searching for a product name and landing on its detail page |
| `emptyCart` | | Removes any products and coupons that are in the cart | | `emptyCart` | | Removes any products and coupons that are in the cart |
### REST API `withRestApi` ### REST API `withRestApi`

View File

@ -2,7 +2,7 @@
"name": "@woocommerce/e2e-utils", "name": "@woocommerce/e2e-utils",
"version": "0.1.5", "version": "0.1.5",
"description": "End-To-End (E2E) test utils for WooCommerce", "description": "End-To-End (E2E) test utils for WooCommerce",
"homepage": "https://github.com/woocommerce/woocommerce/tree/trunk/tests/e2e-utils/README.md", "homepage": "https://github.com/woocommerce/woocommerce/tree/trunk/tests/e2e/utils/README.md",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/woocommerce/woocommerce.git" "url": "https://github.com/woocommerce/woocommerce.git"

View File

@ -373,6 +373,34 @@ const merchant = {
// Wait for Ajax calls to finish // Wait for Ajax calls to finish
await page.waitForResponse( response => response.status() === 200 ); await page.waitForResponse( response => response.status() === 200 );
},
/**
* Runs the database update if needed. For example, after uploading the WooCommerce plugin or updating WooCommerce.
*/
runDatabaseUpdate: async () => {
if ( await page.$( '.updated.woocommerce-message.wc-connect' ) !== null ) {
await expect( page ).toMatchElement( 'a.wc-update-now', { text: 'Update WooCommerce Database' } );
await expect( page ).toClick( 'a.wc-update-now' );
await page.waitForNavigation( { waitUntil: 'networkidle0' } );
await merchant.checkDatabaseUpdateComplete();
}
},
/**
* Checks if the database update is complete, if not, refresh the page until it is.
*/
checkDatabaseUpdateComplete: async () => {
await page.reload( { waitUntil: [ 'networkidle0', 'domcontentloaded'] } );
const thanksButtonSelector = 'a.components-button.is-primary';
if ( await page.$( thanksButtonSelector ) !== null ) {
await expect( page ).toMatchElement( thanksButtonSelector, { text: 'Thanks!' } );
await expect( page ).toClick( thanksButtonSelector );
} else {
await merchant.checkDatabaseUpdateComplete();
}
}, },
}; };