Merge branch 'update/marketplace-1' into update/layout-tweaks

This commit is contained in:
And Finally 2021-08-12 09:15:13 +01:00
commit 4272ca3a3e
11 changed files with 411 additions and 621 deletions

View File

@ -0,0 +1,46 @@
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
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

@ -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

@ -6,6 +6,7 @@
- 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
# 0.2.2

View File

@ -196,6 +196,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

@ -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();
});