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 79c5857f095a3588279c949b23989c14cb665c75
Author: reiern70 <[email protected]>
AuthorDate: Sat Sep 26 09:34:31 2026 -0500

    Add a regression test for Wicket.trapFocus's Tab-key wrapping
    
    wicket-10.x had a bug where the keydown handler in trap-focus.js
    called .trigger('focus') on plain DOM elements returned by
    $focusable.get(...), throwing a TypeError instead of wrapping focus
    around the trap's boundary. See GitHub issue #1620, fixed on
    wicket-10.x in commit cc29182a0a.
    
    master was never affected - it dropped jQuery from this file and
    calls .focus() directly on native DOM elements - but had no test
    coverage of its own that would have caught the same class of
    regression. Adds a QUnit regression test asserting that Tab/Shift+Tab
    at the trap's boundaries move focus to the other end without
    throwing, run against both the JQuery-based and the JQuery-free
    wicket-ajax engine.
---
 testing/wicket-js-tests/Gruntfile.js            |  9 ++-
 wicket-extensions/src/test/js/trap-focus.html   | 62 ++++++++++++++++++++
 wicket-extensions/src/test/js/trapfocus-test.js | 78 +++++++++++++++++++++++++
 3 files changed, 146 insertions(+), 3 deletions(-)

diff --git a/testing/wicket-js-tests/Gruntfile.js 
b/testing/wicket-js-tests/Gruntfile.js
index e8a255ffba..1a6893b9da 100644
--- a/testing/wicket-js-tests/Gruntfile.js
+++ b/testing/wicket-js-tests/Gruntfile.js
@@ -48,7 +48,8 @@ module.exports = function(grunt) {
                        "../../wicket-core/src/test/js/timer.js"
                ],
                extensionsTestsJs = [
-                       "../../wicket-extensions/src/test/js/palette-test.js"
+                       "../../wicket-extensions/src/test/js/palette-test.js",
+                       "../../wicket-extensions/src/test/js/trapfocus-test.js"
                ],
                gymTestsJs = [
                        
"../../wicket-examples/src/main/webapp/js-test/tests/ajax/form.js",
@@ -113,7 +114,8 @@ module.exports = function(grunt) {
                                options: {
                                        urls: [
                                                
'http://localhost:38887/test/js/all.html?4.0.0',
-                                               
'http://localhost:38888/wicket-extensions/src/test/js/palette.html?4.0.0'
+                                               
'http://localhost:38888/wicket-extensions/src/test/js/palette.html?4.0.0',
+                                               
'http://localhost:38888/wicket-extensions/src/test/js/trap-focus.html?4.0.0'
                                        ],
                                        puppeteer: {
                                                headless: true,
@@ -130,7 +132,8 @@ module.exports = function(grunt) {
                                options: {
                                        urls: [
                                                
'http://localhost:38887/test/js/all.html?vanilla',
-                                               
'http://localhost:38888/wicket-extensions/src/test/js/palette.html?vanilla'
+                                               
'http://localhost:38888/wicket-extensions/src/test/js/palette.html?vanilla',
+                                               
'http://localhost:38888/wicket-extensions/src/test/js/trap-focus.html?vanilla'
                                        ],
                                        puppeteer: {
                                                headless: true,
diff --git a/wicket-extensions/src/test/js/trap-focus.html 
b/wicket-extensions/src/test/js/trap-focus.html
new file mode 100644
index 0000000000..6dcbc2d819
--- /dev/null
+++ b/wicket-extensions/src/test/js/trap-focus.html
@@ -0,0 +1,62 @@
+<?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.trapFocus 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">
+               <div id="trapFocusContainer">
+                       <a id="trapFocusFirst" href="#first">first</a>
+                       <a id="trapFocusLast" href="#last">last</a>
+               </div>
+       </div>
+
+       <script>
+               // version lies between question mark and first ampersand (or 
end).
+               // 'vanilla' (trap-focus.html?vanilla) selects the jQuery-free 
wicket-ajax.js
+               // engine instead of the default JQuery + wicket-ajax-jquery.js 
combination
+               // (trap-focus.html?4.0.0, trap-focus.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/ajax/markup/html/modal/trap-focus.js"></script>
+
+       <script type="text/javascript" src="trapfocus-test.js"></script>
+</body>
+</html>
diff --git a/wicket-extensions/src/test/js/trapfocus-test.js 
b/wicket-extensions/src/test/js/trapfocus-test.js
new file mode 100644
index 0000000000..36d45763fa
--- /dev/null
+++ b/wicket-extensions/src/test/js/trapfocus-test.js
@@ -0,0 +1,78 @@
+/*
+ * 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, KeyboardEvent: true */
+
+Wicket.Event.add(window, 'domready', function() {
+       "use strict";
+
+       const { module, test } = QUnit;
+
+       const TAB_KEYCODE = 9;
+
+       function tab(target, shiftKey) {
+               target.dispatchEvent(new KeyboardEvent('keydown', {
+                       key: 'Tab',
+                       code: 'Tab',
+                       keyCode: TAB_KEYCODE,
+                       which: TAB_KEYCODE,
+                       shiftKey: !!shiftKey,
+                       bubbles: true,
+                       cancelable: true
+               }));
+       }
+
+       module("Wicket.trapFocus");
+
+       test("Tab on the last focusable element wraps focus to the first, 
without throwing", assert => {
+               window.Wicket.trapFocus('trapFocusContainer', 
'wicket-trap-focus-test');
+
+               var first = document.getElementById('trapFocusFirst');
+               var last = document.getElementById('trapFocusLast');
+               last.focus();
+
+               var threw = false;
+               try {
+                       tab(last, false);
+               } catch (e) {
+                       threw = true;
+               }
+
+               assert.notOk(threw, "tabbing past the last focusable element 
threw an exception");
+               assert.equal(document.activeElement, first,
+                       "focus did not wrap around to the first focusable 
element");
+       });
+
+       test("Shift+Tab on the first focusable element wraps focus to the last, 
without throwing", assert => {
+               window.Wicket.trapFocus('trapFocusContainer', 
'wicket-trap-focus-test');
+
+               var first = document.getElementById('trapFocusFirst');
+               var last = document.getElementById('trapFocusLast');
+               first.focus();
+
+               var threw = false;
+               try {
+                       tab(first, true);
+               } catch (e) {
+                       threw = true;
+               }
+
+               assert.notOk(threw, "shift-tabbing past the first focusable 
element threw an exception");
+               assert.equal(document.activeElement, last,
+                       "focus did not wrap around to the last focusable 
element");
+       });
+});

Reply via email to