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_CHANNEL: ${{ secrets.SMOKE_TEST_SLACK_CHANNEL }}
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

@ -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:
stale:
if: |
! contains(github.event.issue.labels.*.name, 'enhancement')
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v3

File diff suppressed because it is too large Load Diff

View File

@ -1,41 +1,27 @@
/*!
* SelectWoo 1.0.9
* https://github.com/woocommerce/selectWoo
* Select2 4.0.3
* https://select2.github.io
*
* 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) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['jquery'], factory);
} else if (typeof module === 'object' && module.exports) {
} else if (typeof exports === 'object') {
// Node/CommonJS
module.exports = function (root, 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;
};
factory(require('jquery'));
} else {
// Browser globals
factory(jQuery);
}
} (function (jQuery) {
}(function (jQuery) {
// 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
// returns the AMD loader references.
var S2 =(function () {
var S2 =
(function () {
// Restore the Select2 AMD loader so it can be used
// Needed mostly in the language files, where the loader is not inserted
if (jQuery && jQuery.fn && jQuery.fn.select2 && jQuery.fn.select2.amd) {
@ -44,11 +30,13 @@
var S2;(function () { if (!S2 || !S2.requirejs) {
if (!S2) { S2 = {}; } else { require = S2; }
/**
* @license almond 0.3.3 Copyright jQuery Foundation and other contributors.
* Released under MIT license, http://github.com/requirejs/almond/LICENSE
* @license almond 0.3.1 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved.
* 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
//be followed.
/*jslint sloppy: true */
/*global setTimeout: false */
var requirejs, require, define;
@ -76,58 +64,60 @@ var requirejs, require, define;
*/
function normalize(name, baseName) {
var nameParts, nameSegment, mapValue, foundMap, lastIndex,
foundI, foundStarMap, starI, i, j, part, normalizedBaseParts,
foundI, foundStarMap, starI, i, j, part,
baseParts = baseName && baseName.split("/"),
map = config.map,
starMap = (map && map['*']) || {};
//Adjust any relative paths.
if (name) {
name = name.split('/');
lastIndex = name.length - 1;
if (name && name.charAt(0) === ".") {
//If have a base name, try to normalize against it,
//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
// of IDs. Have to do this here, and not in nameToUrl
// because node allows either .js or non .js to map
// to same file.
if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
}
// Node .js allowance:
if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
}
// Starts with a '.' so need the baseName
if (name[0].charAt(0) === '.' && baseParts) {
//Convert baseName to array, and lop off the last part,
//so that . matches that 'directory' and not name of the baseName's
//module. For instance, baseName of 'one/two/three', maps to
//'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);
}
//Lop off the last part of baseParts, so that . matches the
//"directory" and not name of the baseName's module. For instance,
//baseName of "one/two/three", maps to "one/two/three.js", but we
//want the directory, "one/two" for this normalization.
name = baseParts.slice(0, baseParts.length - 1).concat(name);
//start trimDots
for (i = 0; i < name.length; i++) {
part = name[i];
if (part === '.') {
name.splice(i, 1);
i -= 1;
} else if (part === '..') {
// If at the start, or previous value is still ..,
// keep them so that when converted to a path it may
// still work when converted to a path, even though
// as an ID it is less than ideal. In larger point
// releases, may be better to just kick out an error.
if (i === 0 || (i === 1 && name[2] === '..') || name[i - 1] === '..') {
continue;
} else if (i > 0) {
name.splice(i - 1, 2);
i -= 2;
//start trimDots
for (i = 0; i < name.length; i += 1) {
part = name[i];
if (part === ".") {
name.splice(i, 1);
i -= 1;
} else if (part === "..") {
if (i === 1 && (name[2] === '..' || name[0] === '..')) {
//End of the line. Keep at least one non-dot
//path segment at the front so it can be mapped
//correctly to disk. Otherwise, there is likely
//no path mapping for a path starting with '..'.
//This can still fail, but catches the most reasonable
//uses of ..
break;
} else if (i > 0) {
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.
@ -240,39 +230,32 @@ var requirejs, require, define;
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
* for normalization if necessary. Grabs a ref to plugin
* too, as an optimization.
*/
makeMap = function (name, relParts) {
makeMap = function (name, relName) {
var plugin,
parts = splitPrefix(name),
prefix = parts[0],
relResourceName = relParts[1];
prefix = parts[0];
name = parts[1];
if (prefix) {
prefix = normalize(prefix, relResourceName);
prefix = normalize(prefix, relName);
plugin = callDep(prefix);
}
//Normalize according
if (prefix) {
if (plugin && plugin.normalize) {
name = plugin.normalize(name, makeNormalize(relResourceName));
name = plugin.normalize(name, makeNormalize(relName));
} else {
name = normalize(name, relResourceName);
name = normalize(name, relName);
}
} else {
name = normalize(name, relResourceName);
name = normalize(name, relName);
parts = splitPrefix(name);
prefix = parts[0];
name = parts[1];
@ -319,14 +302,13 @@ var requirejs, require, define;
};
main = function (name, deps, callback, relName) {
var cjsModule, depName, ret, map, i, relParts,
var cjsModule, depName, ret, map, i,
args = [],
callbackType = typeof callback,
usingExports;
//Use name if no relName
relName = relName || name;
relParts = makeRelParts(relName);
//Call the callback to define the module, if necessary.
if (callbackType === 'undefined' || callbackType === 'function') {
@ -335,7 +317,7 @@ var requirejs, require, define;
//Default to [require, exports, module] if no deps
deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps;
for (i = 0; i < deps.length; i += 1) {
map = makeMap(deps[i], relParts);
map = makeMap(deps[i], relName);
depName = map.f;
//Fast path CommonJS standard dependencies.
@ -391,7 +373,7 @@ var requirejs, require, define;
//deps arg is the module name, and second arg (if passed)
//is just the relName.
//Normalize module name, if it contains . or ..
return callDep(makeMap(deps, makeRelParts(callback)).f);
return callDep(makeMap(deps, callback).f);
} else if (!deps.splice) {
//deps is a config object, not an array.
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.
Utils.appendMany = function ($element, $nodes) {
// jQuery 1.7.x does not support $.fn.append() with an array
@ -778,14 +754,6 @@ S2.define('select2/utils',[
$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;
});
@ -805,7 +773,7 @@ S2.define('select2/results',[
Results.prototype.render = function () {
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')) {
@ -828,7 +796,7 @@ S2.define('select2/results',[
this.hideLoading();
var $message = $(
'<li role="alert" aria-live="assertive"' +
'<li role="treeitem" aria-live="assertive"' +
' class="select2-results__option"></li>'
);
@ -890,9 +858,9 @@ S2.define('select2/results',[
Results.prototype.highlightFirstItem = function () {
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
if ($selected.length > 0) {
@ -916,7 +884,7 @@ S2.define('select2/results',[
});
var $options = self.$results
.find('.select2-results__option[data-selected]');
.find('.select2-results__option[aria-selected]');
$options.each(function () {
var $option = $(this);
@ -928,9 +896,9 @@ S2.define('select2/results',[
if ((item.element != null && item.element.selected) ||
(item.element == null && $.inArray(id, selectedIds) > -1)) {
$option.attr('data-selected', 'true');
$option.attr('aria-selected', 'true');
} else {
$option.attr('data-selected', 'false');
$option.attr('aria-selected', 'false');
}
});
@ -962,18 +930,17 @@ S2.define('select2/results',[
option.className = 'select2-results__option';
var attrs = {
'role': 'option',
'data-selected': 'false',
'tabindex': -1
'role': 'treeitem',
'aria-selected': 'false'
};
if (data.disabled) {
delete attrs['data-selected'];
delete attrs['aria-selected'];
attrs['aria-disabled'] = 'true';
}
if (data.id == null) {
delete attrs['data-selected'];
delete attrs['aria-selected'];
}
if (data._resultId != null) {
@ -985,8 +952,9 @@ S2.define('select2/results',[
}
if (data.children) {
attrs.role = 'group';
attrs['aria-label'] = data.text;
delete attrs['data-selected'];
delete attrs['aria-selected'];
}
for (var attr in attrs) {
@ -1003,7 +971,6 @@ S2.define('select2/results',[
var $label = $(label);
this.template(data, label);
$label.attr('role', 'presentation');
var $children = [];
@ -1016,11 +983,10 @@ S2.define('select2/results',[
}
var $childrenContainer = $('<ul></ul>', {
'class': 'select2-results__options select2-results__options--nested',
'role': 'listbox'
'class': 'select2-results__options select2-results__options--nested'
});
$childrenContainer.append($children);
$option.attr('role', 'list');
$option.append(label);
$option.append($childrenContainer);
@ -1116,7 +1082,7 @@ S2.define('select2/results',[
var data = $highlighted.data('data');
if ($highlighted.attr('data-selected') == 'true') {
if ($highlighted.attr('aria-selected') == 'true') {
self.trigger('close', {});
} else {
self.trigger('select', {
@ -1128,7 +1094,7 @@ S2.define('select2/results',[
container.on('results:previous', function () {
var $highlighted = self.getHighlightedResults();
var $options = self.$results.find('[data-selected]');
var $options = self.$results.find('[aria-selected]');
var currentIndex = $options.index($highlighted);
@ -1162,7 +1128,7 @@ S2.define('select2/results',[
container.on('results:next', function () {
var $highlighted = self.getHighlightedResults();
var $options = self.$results.find('[data-selected]');
var $options = self.$results.find('[aria-selected]');
var currentIndex = $options.index($highlighted);
@ -1190,8 +1156,7 @@ S2.define('select2/results',[
});
container.on('results:focus', function (params) {
params.element.addClass('select2-results__option--highlighted').attr('aria-selected', 'true');
self.$results.attr('aria-activedescendant', params.element.attr('id'));
params.element.addClass('select2-results__option--highlighted');
});
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) {
var $this = $(this);
var data = $this.data('data');
if ($this.attr('data-selected') === 'true') {
if ($this.attr('aria-selected') === 'true') {
if (self.options.get('multiple')) {
self.trigger('unselect', {
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) {
var data = $(this).data('data');
self.getHighlightedResults()
.removeClass('select2-results__option--highlighted')
.attr('aria-selected', 'false');
.removeClass('select2-results__option--highlighted');
self.trigger('results:focus', {
data: data,
@ -1281,7 +1245,7 @@ S2.define('select2/results',[
return;
}
var $options = this.$results.find('[data-selected]');
var $options = this.$results.find('[aria-selected]');
var currentIndex = $options.index($highlighted);
@ -1359,7 +1323,7 @@ S2.define('select2/selection/base',[
BaseSelection.prototype.render = function () {
var $selection = $(
'<span class="select2-selection" ' +
'<span class="select2-selection" role="combobox" ' +
' aria-haspopup="true" aria-expanded="false">' +
'</span>'
);
@ -1385,7 +1349,6 @@ S2.define('select2/selection/base',[
var id = container.id + '-container';
var resultsId = container.id + '-results';
var searchHidden = this.options.get('minimumResultsForSearch') === Infinity;
this.container = container;
@ -1427,11 +1390,7 @@ S2.define('select2/selection/base',[
self.$selection.removeAttr('aria-activedescendant');
self.$selection.removeAttr('aria-owns');
// This needs to be delayed as the active element is the body when the
// key is pressed.
window.setTimeout(function () {
self.$selection.trigger( 'focus' );
}, 1);
self.$selection.focus();
self._detachCloseHandler(container);
});
@ -1481,14 +1440,8 @@ S2.define('select2/selection/base',[
}
var $element = $this.data('element');
$element.select2('close');
// Remove any focus when dropdown is closed by clicking outside the select area.
// Timeout of 1 required for close to finish wrapping up.
setTimeout(function(){
$this.find('*:focus').blur();
$target.focus();
}, 1);
$element.select2('close');
});
});
};
@ -1547,21 +1500,8 @@ S2.define('select2/selection/single',[
var id = container.id + '-container';
this.$selection.find('.select2-selection__rendered')
.attr('id', 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.find('.select2-selection__rendered').attr('id', id);
this.$selection.attr('aria-labelledby', id);
this.$selection.on('mousedown', function (evt) {
// Only respond to left clicks
@ -1578,20 +1518,13 @@ S2.define('select2/selection/single',[
// 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) {
// User exits the container
});
container.on('focus', function (evt) {
if (!container.isOpen()) {
self.$selection.trigger( 'focus' );
self.$selection.focus();
}
});
@ -1624,9 +1557,9 @@ S2.define('select2/selection/single',[
var selection = data[0];
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);
};
@ -1650,7 +1583,7 @@ S2.define('select2/selection/multiple',[
$selection.addClass('select2-selection--multiple');
$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;
@ -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 () {
@ -1715,7 +1636,7 @@ S2.define('select2/selection/multiple',[
MultipleSelection.prototype.selectionContainer = function () {
var $container = $(
'<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;' +
'</span>' +
'</li>'
@ -1724,24 +1645,6 @@ S2.define('select2/selection/multiple',[
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) {
this.clear();
@ -1755,14 +1658,9 @@ S2.define('select2/selection/multiple',[
var selection = data[d];
var $selection = this.selectionContainer();
var removeItemTag = $selection.html();
var formatted = this.display(selection, $selection);
if ('string' === typeof formatted) {
formatted = Utils.entityDecode(formatted.trim());
}
$selection.text(formatted);
$selection.prepend(removeItemTag);
$selection.append(formatted);
$selection.prop('title', selection.title || selection.text);
$selection.data('data', selection);
@ -1801,7 +1699,7 @@ S2.define('select2/selection/placeholder',[
Placeholder.prototype.createPlaceholder = function (decorated, placeholder) {
var $placeholder = this.selectionContainer();
$placeholder.text(Utils.entityDecode(this.display(placeholder)));
$placeholder.html(this.display(placeholder));
$placeholder.addClass('select2-selection__placeholder')
.removeClass('select2-selection__choice');
@ -1938,8 +1836,8 @@ S2.define('select2/selection/search',[
Search.prototype.render = function (decorated) {
var $search = $(
'<li class="select2-search select2-search--inline">' +
'<input class="select2-search__field" type="text" tabindex="-1"' +
' autocomplete="off" autocorrect="off" autocapitalize="none"' +
'<input class="select2-search__field" type="search" tabindex="-1"' +
' autocomplete="off" autocorrect="off" autocapitalize="off"' +
' spellcheck="false" role="textbox" aria-autocomplete="list" />' +
'</li>'
);
@ -1956,19 +1854,16 @@ S2.define('select2/selection/search',[
Search.prototype.bind = function (decorated, container, $container) {
var self = this;
var resultsId = container.id + '-results';
decorated.call(this, container, $container);
container.on('open', function () {
self.$search.attr('aria-owns', resultsId);
self.$search.trigger('focus');
});
container.on('close', function () {
self.$search.val('');
self.$search.removeAttr('aria-activedescendant');
self.$search.removeAttr('aria-owns');
self.$search.trigger('focus');
});
@ -1987,7 +1882,7 @@ S2.define('select2/selection/search',[
});
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) {
@ -2018,9 +1913,6 @@ S2.define('select2/selection/search',[
evt.preventDefault();
}
} else if (evt.which === KEYS.ENTER) {
container.open();
evt.preventDefault();
}
});
@ -2109,7 +2001,7 @@ S2.define('select2/selection/search',[
this.resizeSearch();
if (searchHadFocus) {
this.$search.trigger( 'focus' );
this.$search.focus();
}
};
@ -3112,15 +3004,8 @@ S2.define('select2/data/base',[
};
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);
if (data.id != null) {
@ -3306,7 +3191,7 @@ S2.define('select2/data/select',[
}
}
if (data.id !== undefined) {
if (data.id) {
option.value = data.id;
}
@ -3404,7 +3289,7 @@ S2.define('select2/data/select',[
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);
}
@ -3547,7 +3432,7 @@ S2.define('select2/data/ajax',[
if (this._request != null) {
// JSONP requests cannot always be aborted
if ( typeof this._request.abort === 'function' ) {
if ($.isFunction(this._request.abort)) {
this._request.abort();
}
@ -3572,7 +3457,7 @@ S2.define('select2/data/ajax',[
if (self.options.get('debug') && window.console && console.error) {
// 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(
'Select2: The AJAX results did not return an array in the ' +
'`results` key of the response.'
@ -3581,7 +3466,6 @@ S2.define('select2/data/ajax',[
}
callback(results);
self.container.focusOnActiveElement();
}, function () {
// Attempt to detect if a request was aborted
// Only works if the transport exposes a status property
@ -3631,7 +3515,7 @@ S2.define('select2/data/tags',[
decorated.call(this, $element, options);
if ( Array.isArray( tags ) ) {
if ($.isArray(tags)) {
for (var t = 0; t < tags.length; t++) {
var tag = tags[t];
var item = this._normalizeItem(tag);
@ -3666,10 +3550,7 @@ S2.define('select2/data/tags',[
}, true)
);
var optionText = (option.text || '').toUpperCase();
var paramsTerm = (params.term || '').toUpperCase();
var checkText = optionText === paramsTerm;
var checkText = option.text === params.term;
if (checkText || checkChildren) {
if (child) {
@ -3707,7 +3588,7 @@ S2.define('select2/data/tags',[
};
Tags.prototype.createTag = function (decorated, params) {
var term = ( params.term || 0 == params.term ) ? params.term.toString().trim() : '';
var term = $.trim(params.term);
if (term === '') {
return null;
@ -3800,7 +3681,7 @@ S2.define('select2/data/tokenizer',[
// Replace the search term if we have the search box
if (this.$search.length) {
this.$search.val(tokenData.term);
this.$search.trigger( 'focus' );
this.$search.focus();
}
params.term = tokenData.term;
@ -4006,9 +3887,9 @@ S2.define('select2/dropdown/search',[
var $search = $(
'<span class="select2-search select2-search--dropdown">' +
'<input class="select2-search__field" type="text" tabindex="-1"' +
' autocomplete="off" autocorrect="off" autocapitalize="none"' +
' spellcheck="false" role="combobox" aria-autocomplete="list" aria-expanded="true" />' +
'<input class="select2-search__field" type="search" tabindex="-1"' +
' autocomplete="off" autocorrect="off" autocapitalize="off"' +
' spellcheck="false" role="textbox" />' +
'</span>'
);
@ -4022,7 +3903,6 @@ S2.define('select2/dropdown/search',[
Search.prototype.bind = function (decorated, container, $container) {
var self = this;
var resultsId = container.id + '-results';
decorated.call(this, container, $container);
@ -4046,24 +3926,23 @@ S2.define('select2/dropdown/search',[
container.on('open', function () {
self.$search.attr('tabindex', 0);
self.$search.attr('aria-owns', resultsId);
self.$search.trigger( 'focus' );
self.$search.focus();
window.setTimeout(function () {
self.$search.trigger( 'focus' );
self.$search.focus();
}, 0);
});
container.on('close', function () {
self.$search.attr('tabindex', -1);
self.$search.removeAttr('aria-activedescendant');
self.$search.removeAttr('aria-owns');
self.$search.val('');
});
container.on('focus', function () {
if (!container.isOpen()) {
self.$search.trigger( 'focus' );
if (container.isOpen()) {
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) {
@ -4223,7 +4098,7 @@ S2.define('select2/dropdown/infiniteScroll',[
var $option = $(
'<li ' +
'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');
@ -4878,7 +4753,7 @@ S2.define('select2/defaults',[
}
}
if ( Array.isArray( options.language ) ) {
if ($.isArray(options.language)) {
var languages = new Translation();
options.language.push('en');
@ -4941,7 +4816,7 @@ S2.define('select2/defaults',[
function matcher (params, data) {
// Always return the object if there is nothing to compare
if ( params.term == null || params.term.toString().trim() === '' ) {
if ($.trim(params.term) === '') {
return data;
}
@ -5469,22 +5344,16 @@ S2.define('select2/core',[
});
});
this.on('open', function(){
// 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) {
this.on('keypress', function (evt) {
var key = evt.which;
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();
evt.preventDefault();
} else if (key === KEYS.ENTER || key === KEYS.TAB) {
} else if (key === KEYS.ENTER) {
self.trigger('results:select', {});
evt.preventDefault();
@ -5501,42 +5370,17 @@ S2.define('select2/core',[
evt.preventDefault();
}
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()) {
} else {
if (key === KEYS.ENTER || key === KEYS.SPACE ||
key === KEYS.DOWN) {
(key === KEYS.DOWN && evt.altKey)) {
self.open();
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 () {
this.options.set('disabled', this.$element.prop('disabled'));
@ -5724,7 +5568,7 @@ S2.define('select2/core',[
var newVal = args[0];
if ( Array.isArray( newVal ) ) {
if ($.isArray(newVal)) {
newVal = $.map(newVal, function (obj) {
return obj.toString();
});
@ -5809,11 +5653,11 @@ S2.define('jquery.select2',[
'./select2/core',
'./select2/defaults'
], function ($, _, Select2, Defaults) {
if ($.fn.selectWoo == null) {
if ($.fn.select2 == null) {
// All methods that should return the element
var thisMethods = ['open', 'close', 'destroy'];
$.fn.selectWoo = function (options) {
$.fn.select2 = function (options) {
options = options || {};
if (typeof options === 'object') {
@ -5853,17 +5697,10 @@ S2.define('jquery.select2',[
};
}
if ($.fn.select2 != null && $.fn.select2.defaults != null) {
$.fn.selectWoo.defaults = $.fn.select2.defaults;
if ($.fn.select2.defaults == null) {
$.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;
});
@ -5882,7 +5719,6 @@ S2.define('jquery.select2',[
// This allows Select2 to use the internal loader outside of this file, such
// as in the language files.
jQuery.fn.select2.amd = S2;
jQuery.fn.selectWoo.amd = S2;
// Return the Select2 instance for anyone who is importing it.
return select2;

View File

@ -68,16 +68,16 @@
},
{
"name": "league/flysystem",
"version": "1.1.4",
"version": "1.1.5",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem.git",
"reference": "f3ad69181b8afed2c9edf7be5a2918144ff4ea32"
"reference": "18634df356bfd4119fe3d6156bdb990c414c14ea"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/f3ad69181b8afed2c9edf7be5a2918144ff4ea32",
"reference": "f3ad69181b8afed2c9edf7be5a2918144ff4ea32",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/18634df356bfd4119fe3d6156bdb990c414c14ea",
"reference": "18634df356bfd4119fe3d6156bdb990c414c14ea",
"shasum": ""
},
"require": {
@ -150,7 +150,7 @@
],
"support": {
"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": [
{
@ -158,7 +158,7 @@
"type": "other"
}
],
"time": "2021-06-23T21:56:05+00:00"
"time": "2021-08-17T13:49:42+00:00"
},
{
"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 - 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 unit tests around extended payment gateway controller #7133
@ -79,6 +79,7 @@
- Fix - Fix analytics overview re-arrangement on initial load. #7475
- Fix - Fixes action button mis-alignment within card footer. #7412
- 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 - Change event name when installing Google Listings and Ads. #7276
- Tweak - Removed unused feature flags #7233 and #7273

View File

@ -21,7 +21,7 @@
"pelago/emogrifier": "3.1.0",
"psr/container": "1.0.0",
"woocommerce/action-scheduler": "3.2.1",
"woocommerce/woocommerce-admin": "2.5.0",
"woocommerce/woocommerce-admin": "2.5.1",
"woocommerce/woocommerce-blocks": "5.5.1"
},
"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",
"This file is @generated automatically"
],
"content-hash": "e66790f6451aac2d4cb8f418bb26268e",
"content-hash": "f1450dbdfedc77735b411ae8a019b534",
"packages": [
{
"name": "automattic/jetpack-autoloader",
@ -532,16 +532,16 @@
},
{
"name": "woocommerce/woocommerce-admin",
"version": "2.5.0",
"version": "2.5.1",
"source": {
"type": "git",
"url": "https://github.com/woocommerce/woocommerce-admin.git",
"reference": "99a0bd3a48bf33a054efd3b0df29bf42766761f8"
"reference": "093d698d770f49d41df8abe89a716dc897bb9e96"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/woocommerce/woocommerce-admin/zipball/99a0bd3a48bf33a054efd3b0df29bf42766761f8",
"reference": "99a0bd3a48bf33a054efd3b0df29bf42766761f8",
"url": "https://api.github.com/repos/woocommerce/woocommerce-admin/zipball/093d698d770f49d41df8abe89a716dc897bb9e96",
"reference": "093d698d770f49d41df8abe89a716dc897bb9e96",
"shasum": ""
},
"require": {
@ -596,9 +596,9 @@
"homepage": "https://github.com/woocommerce/woocommerce-admin",
"support": {
"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",

View File

@ -226,7 +226,7 @@ final class WC_Cart_Totals {
$item->taxable = 'taxable' === $cart_item['data']->get_tax_status();
$item->price_includes_tax = wc_prices_include_tax();
$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->tax_rates = $this->get_item_tax_rates( $item );
$this->items[ $cart_item_key ] = $item;

View File

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

View File

@ -84,7 +84,7 @@ class WC_Discounts {
$item->object = $cart_item;
$item->product = $cart_item['data'];
$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;
}

View File

@ -160,9 +160,6 @@ class WC_Tracker {
// 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).
$data['cart_checkout'] = self::get_cart_checkout_info();
@ -619,6 +616,8 @@ class WC_Tracker {
'version' => WC()->version,
'currency' => get_woocommerce_currency(),
'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(),
'api_enabled' => get_option( 'woocommerce_api_enabled' ),
'weight_unit' => get_option( 'woocommerce_weight_unit' ),
@ -675,15 +674,6 @@ class WC_Tracker {
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.
*

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'
",
'orderby' => '
ORDER BY RAND()',
'limits' => '
LIMIT ' . absint( $limit ) . '
',

View File

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

View File

@ -8,6 +8,7 @@ import {
const config = require('config');
const { HTTPClientFactory } = require('@woocommerce/api');
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
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
// each other's side-effects.
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
await updateReadyPageStatus('draft');

View File

@ -1,9 +1,9 @@
/* eslint-disable jest/no-export, jest/no-disabled-tests, jest/expect-expect */
/**
* Internal dependencies
*/
const {
shopper,
withRestApi,
createSimpleProduct,
uiUnblocked
} = require( '@woocommerce/e2e-utils' );
@ -26,6 +26,9 @@ const runCartPageTest = () => {
describe('Cart page', () => {
beforeAll(async () => {
await createSimpleProduct();
await withRestApi.resetSettingsGroupToDefault('general');
await withRestApi.resetSettingsGroupToDefault('products');
await withRestApi.resetSettingsGroupToDefault('tax');
});
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
*/
const {
shopper,
merchant,
withRestApi,
createSimpleProduct,
setCheckbox,
settingsPageSaveChanges,
@ -28,6 +28,9 @@ const runCheckoutPageTest = () => {
describe('Checkout page', () => {
beforeAll(async () => {
await createSimpleProduct();
await withRestApi.resetSettingsGroupToDefault('general');
await withRestApi.resetSettingsGroupToDefault('products');
await withRestApi.resetSettingsGroupToDefault('tax');
// Set free shipping within California
await merchant.login();

View File

@ -1,11 +1,13 @@
# Unreleased
- `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
- `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:
- `getRemotePluginZip( fileUrl )` to get the remote zip. Returns the filepath of the zip location.
- 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.
- `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

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.
### 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
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.
- `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
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 fs = require( 'fs' );
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
.usage( '<file ...> [options]' )
@ -43,6 +43,10 @@ let testEnvVars = {
jest_test_timeout: program.dev ? 120000 : 30000,
};
if ( DEFAULT_TIMEOUT_OVERRIDE ) {
testEnvVars.jest_test_timeout = DEFAULT_TIMEOUT_OVERRIDE;
}
if ( ! JEST_PUPPETEER_CONFIG ) {
// Use local Puppeteer config if there is one.
// Load test configuration file into an object.

View File

@ -29,6 +29,52 @@ const getRemotePluginZip = async ( fileUrl ) => {
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.
*
@ -80,6 +126,7 @@ const downloadZip = async ( fileUrl, downloadPath ) => {
module.exports = {
getRemotePluginZip,
getLatestReleaseZipUrl,
checkNestedZip,
downloadZip,
};

View File

@ -1,7 +1,7 @@
const getAppRoot = require( './app-root' );
const { getAppName, getAppBase } = require( './app-name' );
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 updateReadyPageStatus = require('./update-ready-page');
const consoleUtils = require( './filter-console' );
@ -13,6 +13,7 @@ module.exports = {
getTestConfig,
getAdminConfig,
getRemotePluginZip,
getLatestReleaseZipUrl,
takeScreenshotFor,
updateReadyPageStatus,
...consoleUtils,

View File

@ -3,7 +3,7 @@
*/
const { merchant, utils } = require( '@woocommerce/e2e-utils' );
const { getRemotePluginZip } = require( '@woocommerce/e2e-environment' );
const { getRemotePluginZip, getLatestReleaseZipUrl } = require( '@woocommerce/e2e-environment' );
/**
* External dependencies
@ -13,16 +13,23 @@ const {
beforeAll,
} = 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';
let pluginPath;
utils.describeIf( UPDATE_WC )( 'WooCommerce plugin can be uploaded and activated', () => {
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();
});
@ -34,4 +41,9 @@ utils.describeIf( UPDATE_WC )( 'WooCommerce plugin can be uploaded and activated
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 `itIf()` to conditionally run a test case.
- 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 `deleteAllShippingClasses()` which permanently deletes all shipping classes using the API
- 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|
| `updatePlugins` | | Install all pending plugin updates on Dashboard -> Updates|
| `updateThemes` | | Install all pending theme updates on Dashboard -> Updates|
| `runDatabaseUpdate` || Runs the database update if needed |
### 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 |
| `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 |
| `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`

View File

@ -2,7 +2,7 @@
"name": "@woocommerce/e2e-utils",
"version": "0.1.5",
"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": {
"type": "git",
"url": "https://github.com/woocommerce/woocommerce.git"

View File

@ -373,6 +373,34 @@ const merchant = {
// Wait for Ajax calls to finish
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();
}
},
};