This is an automated email from the ASF dual-hosted git repository. reiern70 pushed a commit to branch reiern70/issue-1618-master-palette-trapfocus-tests in repository https://gitbox.apache.org/repos/asf/wicket.git
commit 22ebf9253ddb43fc7ce58af228545f45634033d0 Author: reiern70 <[email protected]> AuthorDate: Sat Sep 26 09:33:40 2026 -0500 Add a regression test for Wicket.Palette's moveUp/moveDown wicket-10.x (10.9.0 through 10.11.0) had a bug where Wicket.Palette.moveUpHelper called box.trigger('focus') on a plain DOM <select> element, throwing a TypeError and silently discarding the reorder before the hidden recorder input was updated. See GitHub issue #1618, fixed on wicket-10.x in commit 432a7fee87. master was never affected - it dropped jQuery from palette.js and calls box.focus() directly - but had no test coverage of its own that would have caught the same class of regression. Adds a QUnit harness for wicket-extensions' client-side JavaScript (none existed; only wicket-core's was wired into the grunt/js-test build) and a regression test asserting that moveUp/moveDown reorder the selection and update the recorder without throwing, run against both the JQuery-based and the JQuery-free wicket-ajax engine. --- testing/wicket-js-tests/Gruntfile.js | 19 +++++- wicket-extensions/src/test/js/palette-test.js | 88 +++++++++++++++++++++++++++ wicket-extensions/src/test/js/palette.html | 67 ++++++++++++++++++++ 3 files changed, 172 insertions(+), 2 deletions(-) diff --git a/testing/wicket-js-tests/Gruntfile.js b/testing/wicket-js-tests/Gruntfile.js index db599c74b7..e8a255ffba 100644 --- a/testing/wicket-js-tests/Gruntfile.js +++ b/testing/wicket-js-tests/Gruntfile.js @@ -47,6 +47,9 @@ module.exports = function(grunt) { "../../wicket-core/src/test/js/event.js", "../../wicket-core/src/test/js/timer.js" ], + extensionsTestsJs = [ + "../../wicket-extensions/src/test/js/palette-test.js" + ], gymTestsJs = [ "../../wicket-examples/src/main/webapp/js-test/tests/ajax/form.js", "../../wicket-examples/src/main/webapp/js-test/tests/bean-validation/birthdate.js", @@ -74,6 +77,7 @@ module.exports = function(grunt) { extensions: extensionsJs, nativeWebSocket: nativeWebSocketJs, testsJs: testsJs, + extensionsTestsJs: extensionsTestsJs, gymTestsJs: gymTestsJs, grunt: gruntJs, @@ -108,7 +112,8 @@ module.exports = function(grunt) { all: { options: { urls: [ - 'http://localhost:38887/test/js/all.html?4.0.0' + 'http://localhost:38887/test/js/all.html?4.0.0', + 'http://localhost:38888/wicket-extensions/src/test/js/palette.html?4.0.0' ], puppeteer: { headless: true, @@ -124,7 +129,8 @@ module.exports = function(grunt) { vanilla: { options: { urls: [ - 'http://localhost:38887/test/js/all.html?vanilla' + 'http://localhost:38887/test/js/all.html?vanilla', + 'http://localhost:38888/wicket-extensions/src/test/js/palette.html?vanilla' ], puppeteer: { headless: true, @@ -152,6 +158,15 @@ module.exports = function(grunt) { }, base: '../../wicket-core/src' } + }, + // serves wicket-extensions' own JavaScript and its QUnit tests; a separate + // target because they live outside the wicket-core/src root above + extensions: { + options: { + port: 38888, + debug: true, + base: '../..' + } } } }); diff --git a/wicket-extensions/src/test/js/palette-test.js b/wicket-extensions/src/test/js/palette-test.js new file mode 100644 index 0000000000..2216658382 --- /dev/null +++ b/wicket-extensions/src/test/js/palette-test.js @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*global QUnit: true */ + +Wicket.Event.add(window, 'domready', function() { + "use strict"; + + const { module, test } = QUnit; + + function selectionOptionValues() { + var selection = document.getElementById('paletteSelection'); + return Array.prototype.map.call(selection.options, function(option) { + return option.value; + }); + } + + function selectOnly(selection, value) { + Array.prototype.forEach.call(selection.options, function(option) { + option.selected = (option.value === value); + }); + } + + module("Wicket.Palette", { + beforeEach: function() { + var selection = document.getElementById('paletteSelection'); + selection.innerHTML = + '<option value="1">one</option>' + + '<option value="2">two</option>' + + '<option value="3">three</option>'; + document.getElementById('paletteRecorder').value = '1,2,3'; + } + }); + + test("moveUp reorders the selected option without throwing", assert => { + var selection = document.getElementById('paletteSelection'); + selectOnly(selection, '3'); + + Wicket.Palette.moveUp('paletteChoices', 'paletteSelection', 'paletteRecorder'); + + assert.deepEqual(selectionOptionValues(), ['1', '3', '2'], + "moveUp did not move the selected option in front of its predecessor"); + }); + + test("moveUp updates the hidden recorder input", assert => { + var selection = document.getElementById('paletteSelection'); + selectOnly(selection, '3'); + + Wicket.Palette.moveUp('paletteChoices', 'paletteSelection', 'paletteRecorder'); + + assert.equal(document.getElementById('paletteRecorder').value, '1,3,2', + "the recorder was not updated, so the server never sees the new order"); + }); + + test("moveUp on the first option is a no-op and does not throw", assert => { + var selection = document.getElementById('paletteSelection'); + selectOnly(selection, '1'); + + Wicket.Palette.moveUp('paletteChoices', 'paletteSelection', 'paletteRecorder'); + + assert.deepEqual(selectionOptionValues(), ['1', '2', '3'], + "the order should not have changed"); + }); + + test("moveDown reorders the selected option without throwing", assert => { + var selection = document.getElementById('paletteSelection'); + selectOnly(selection, '1'); + + Wicket.Palette.moveDown('paletteChoices', 'paletteSelection', 'paletteRecorder'); + + assert.deepEqual(selectionOptionValues(), ['2', '1', '3'], + "moveDown did not move the selected option behind its successor"); + }); +}); diff --git a/wicket-extensions/src/test/js/palette.html b/wicket-extensions/src/test/js/palette.html new file mode 100644 index 0000000000..606c56358e --- /dev/null +++ b/wicket-extensions/src/test/js/palette.html @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<html> + +<head> + <title id="titleId">Wicket.Palette JavaScript tests</title> + <meta http-equiv="content-type" content="text/html; charset=UTF-8"> + <link rel="stylesheet" href="/wicket-core/src/test/js/qunit/qunit.css" type="text/css" media="screen" /> +</head> + +<body> + <div id="qunit"></div> + + <div id="qunit-fixture"> + <select id="paletteChoices" multiple="multiple"> + <option value="4">four</option> + </select> + <select id="paletteSelection" multiple="multiple"> + <option value="1">one</option> + <option value="2">two</option> + <option value="3">three</option> + </select> + <input type="hidden" id="paletteRecorder" value="1,2,3"/> + </div> + + <script> + // version lies between question mark and first ampersand (or end). + // 'vanilla' (palette.html?vanilla) selects the jQuery-free wicket-ajax.js + // engine instead of the default JQuery + wicket-ajax-jquery.js combination + // (palette.html?4.0.0, palette.html?3.7.1, ...). + var version = location.search.match(/\?(.*?)(&|$)/)[1]; + var useVanilla = (version === 'vanilla'); + + if (!useVanilla) { + document.write("<scr"+"ipt src='/wicket-core/src/main/java/org/apache/wicket/resource/jquery/jquery-"+version+".js'></scr"+"ipt>"); + } + </script> + <script> + if (!useVanilla) { + document.write("<scr"+"ipt src='/wicket-core/src/main/java/org/apache/wicket/ajax/res/js/wicket-ajax-jquery.js'></scr"+"ipt>"); + } else { + document.write("<scr"+"ipt src='/wicket-core/src/main/java/org/apache/wicket/ajax/res/js/wicket-ajax.js'></scr"+"ipt>"); + } + </script> + <script type="text/javascript" src="/wicket-core/src/test/js/qunit/qunit.js"></script> + + <!-- the module under test --> + <script type="text/javascript" src="/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/palette/palette.js"></script> + + <script type="text/javascript" src="palette-test.js"></script> +</body> +</html>
