From 261debdb83a2c012154ba1125b6c9607e131d25e Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Mon, 18 Jul 2022 10:54:59 +0700 Subject: [PATCH 01/29] feat(web): initial Karma setup for gesture-host browser testing --- common/web/gesture-recognizer/package.json | 21 ++- .../src/test/auto/browser/CI.conf.js | 161 ++++++++++++++++++ .../src/test/auto/browser/base.conf.js | 93 ++++++++++ .../src/test/auto/browser/cases/canary.js | 25 +++ .../src/test/auto/browser/manual.conf.js | 26 +++ .../test/auto/browser/timeoutConfiguration.js | 21 +++ .../web/gesture-recognizer/src/test/test.sh | 98 +++++++++++ 7 files changed, 441 insertions(+), 4 deletions(-) create mode 100644 common/web/gesture-recognizer/src/test/auto/browser/CI.conf.js create mode 100644 common/web/gesture-recognizer/src/test/auto/browser/base.conf.js create mode 100644 common/web/gesture-recognizer/src/test/auto/browser/cases/canary.js create mode 100644 common/web/gesture-recognizer/src/test/auto/browser/manual.conf.js create mode 100644 common/web/gesture-recognizer/src/test/auto/browser/timeoutConfiguration.js create mode 100755 common/web/gesture-recognizer/src/test/test.sh diff --git a/common/web/gesture-recognizer/package.json b/common/web/gesture-recognizer/package.json index 60620aeb7c..139c2498fc 100644 --- a/common/web/gesture-recognizer/package.json +++ b/common/web/gesture-recognizer/package.json @@ -5,20 +5,33 @@ "@keymanapp/resources-gosh": "*", "@types/node": "^11.9.4", "chai": "^4.3.4", + "karma": "^6.3.4", + "karma-browserstack-launcher": "^1.6.0", + "karma-chai": "^0.1.0", + "karma-chrome-launcher": "^2.2.0", + "karma-firefox-launcher": "^1.1.0", + "karma-fixture": "^0.2.6", + "karma-html2js-preprocessor": "^1.1.0", + "karma-ie-launcher": "^1.0.0", + "karma-json-fixtures-preprocessor": "0.0.6", + "karma-mocha": "^2.0.1", + "karma-mocha-reporter": "^2.2.5", + "karma-safari-launcher": "^1.0.0", + "karma-teamcity-reporter": "^1.1.0", "mocha": "^8.4.0", "mocha-teamcity-reporter": "^4.0.0", + "sinon": "^7.1.1", "ts-node": "^9.1.1", "typescript": "^4.5.4" }, "scripts": { "build": "echo 'Building @keymanapp/gesture-recognizer' && tsc -b ./tsconfig.json", "clean": "tsc -b --clean", - "tsc": "tsc", - "mocha": "mocha" + "karma": "karma", + "mocha": "mocha", + "tsc": "tsc" }, "dependencies": { - "@keymanapp/keyman-version": "*", - "@keymanapp/web-utils": "*", "eventemitter3": "^4.0.0" } } diff --git a/common/web/gesture-recognizer/src/test/auto/browser/CI.conf.js b/common/web/gesture-recognizer/src/test/auto/browser/CI.conf.js new file mode 100644 index 0000000000..d937a8406e --- /dev/null +++ b/common/web/gesture-recognizer/src/test/auto/browser/CI.conf.js @@ -0,0 +1,161 @@ +module.exports = function(config) { + var base = require("./base.conf.js"); + + var timeouts = base.client.args[0]; + var browserStackModifier = 10; + + for(key in timeouts) { + if(typeof timeouts[key] == 'number') { + timeouts[key] = timeouts[key] * browserStackModifier; + } + } + + /* + * Definition of utility functions for managing our browser lists. + */ + var mergeLaunchers = function() { + var mergedDefs = {}; + var i; + for(i=0; i < arguments.length; i++) { + for(var name in arguments[i]) { + mergedDefs[name] = arguments[i][name]; + + // Necessary for Karma to process it properly. + mergedDefs[name].base = 'BrowserStack'; + } + } + + return mergedDefs; + } + + var toBrowserList = function(mergedSet) { + var list = []; + for(var name in mergedSet) { + list.push(name); + } + + return list; + } + + /* + * Definition of browser sets possibly relevant for testing. + */ + var CURRENT_MAC_LAUNCHERS = { + bs_firefox_mac: { + browser: 'firefox', + browser_version: '62', + os: 'OS X', + os_version: 'Mojave' + }, + bs_safari_mac_m: { + browser: 'safari', + browser_version: '13', + os: 'OS X', + os_version: 'Catalina' + }, + bs_chrome_mac: { + browser: 'chrome', + browser_version: '70.0', + os: 'OS X', + os_version: 'Mojave' + } + }; + + // BrowserStack does not appear to properly support automated JavaScript testing at this time. + // See https://www.browserstack.com/list-of-browsers-and-platforms?product=js_testing for the reference. + var CURRENT_IOS_LAUNCHERS = {}; /*{ + bs_iphoneX: { + device: 'iPhone X', // Ideally, we'd use 'iPhone X', but BrowserStack's version is being problematic lately. + real_mobile: true, + os: 'ios', + os_version: '11.0' + }, + bs_ipad6: { + device: 'iPad 5th', + real_mobile: true, + os: 'ios', + os_version: '11.0' + } + };*/ + + var CURRENT_WIN_LAUNCHERS = { + bs_firefox_win: { + os: 'Windows', + os_version: '10', + browser: 'firefox', + browser_version: '62.0' + }, + bs_chrome_win: { + os: 'Windows', + os_version: '10', + browser: 'chrome', + browser_version: '70.0' + } + } + + var CURRENT_ANDROID_LAUNCHERS = { + bs_chrome_android: { + os: 'android', + os_version: '7.1', + browser: 'chrome', + real_mobile: true, + device: 'Samsung Galaxy Note 8' + } + } + + // Sadly, legacy IE isn't very testable with Mocha. One of its dependencies requires a feature that is IE11+. + + /* + * Final selection of the sets to be used for BrowserStack testing. + */ + var FINAL_LAUNCHER_DEFS = mergeLaunchers( CURRENT_ANDROID_LAUNCHERS, + CURRENT_IOS_LAUNCHERS, + CURRENT_WIN_LAUNCHERS, + CURRENT_MAC_LAUNCHERS); + + var FINAL_BROWSER_LIST = toBrowserList(FINAL_LAUNCHER_DEFS); + + /* + * Final definition of our BrowserStack testing Karma configuration. + */ + + var specifics = { + // BrowserStack configuration options + browserStack: { + video: true, + browserDisconnectTimeout: 6e4, // 1 minute (60s => 60,000ms) + retryLimit: 3, // 0 is ignored. + startTunnel: true, + }, + + // Attempts to avoid generating a 'fail' exit code if one of our selected browsers on BrowserStack goes poof. + failOnEmptyTestSuite: false, + + captureTimeout: 1.2e5, // in milliseconds + browserNoActivityTimeout: 6e4, + browserDisconnectTimeout: 6e4, + browserDisconnectTolerance: 3, + + // Avoids generating a 'fail' exit code if one of our selected browsers on BrowserStack goes poof. + failOnEmptyTestSuite: false, + + // test results reporter to use + // possible values: 'dots', 'progress' + // available reporters: https://npmjs.org/browse/keyword/karma-reporter + reporters: ['teamcity', 'BrowserStack'], + + // level of logging + // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + logLevel: config.LOG_DEBUG, + + // Concurrency level + // For CI, it really helps to keep a nice, clean set of output logs. + concurrency: 5, + + customLaunchers: FINAL_LAUNCHER_DEFS, + + browsers: FINAL_BROWSER_LIST + }; + + config.set(Object.assign(specifics, base)); +} diff --git a/common/web/gesture-recognizer/src/test/auto/browser/base.conf.js b/common/web/gesture-recognizer/src/test/auto/browser/base.conf.js new file mode 100644 index 0000000000..b8a6e0e3ea --- /dev/null +++ b/common/web/gesture-recognizer/src/test/auto/browser/base.conf.js @@ -0,0 +1,93 @@ +// Karma configuration +// Generated on Mon Jan 29 2018 11:58:53 GMT+0700 (SE Asia Standard Time) + +// Super-useful! http://www.bradoncode.com/blog/2015/02/27/karma-tutorial/#handling-html-fixtures + +module.exports = { + // base path that will be used to resolve all patterns (eg. files, exclude), + // set to this module's base folder. + basePath: '../../../..', + + // Safeguards against disconnecting after starting tests. + browserNoActivityTimeout: 60000, + + client: { + /* `client.args` here is passed to the test runner page as `__karma__.config.args`. + * + * Karma doc type spec says "array", so we use an array. It also gives us room to add alternate + * configuration details later if we need to, though on a CI vs local basis only. + * + * Timeouts below are in milliseconds + */ + args: [{ + type: "timeouts", // This base is designed for local machine testing. + eventDelay: 60, // Designed for small delays to allow time for event handling to occur before proceeding. + // Make sure this stays under 1/4 of 'standard', as multiple eventDelays may occur within a test. + standard: 5000, + scriptLoad: 8000, + uiLoad: 30000, // Loads two scripts + includes internal setup/timeout time requirements. + // At this time of writing this, UI script loading is one of the longest checks. + }] + }, + + // frameworks to use + // available frameworks: https://npmjs.org/browse/keyword/karma-adapter + frameworks: ['mocha', 'chai', 'fixture'], + + // list of files / patterns to load in the browser + files: [ + 'build/index.js', // The primary gesture-recognizer script. + {pattern: 'build/index.js.map', watched: true, served: true, included: false}, + 'build/tools/unit-test-resources.js', // The primary unit-test-resources script. + {pattern: 'build/tools/unit-test-resources.js.map', watched: true, served: true, included: false}, + + 'src/test/auto/browser/timeoutConfiguration.js', // Handles timeout configuration for local vs BrowserStack-based testing + + 'src/test/auto/browser/cases/**/*.js', // Where the tests actually reside. + + 'build/tools/host-fixture.html', // The primary test fixture's build output location + 'build/tools/gestureHost.css', // The primary test fixture's backing CSS stylesheet. Note: Karma will auto-link it! + 'src/test/resources/json/**/*.json', // Where pre-loaded JSON resides. + ], + + proxies: { + "/resources/": "/src/test/resources/", + }, + + + // list of files / patterns to exclude + exclude: [ + ], + + // preprocess matching files before serving them to the browser + // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor + preprocessors: { + 'build/tools/host-fixture.html': ['html2js'], + 'src/test/resources/fixtures/**/*.html' : ['html2js'], + 'src/test/resources/json/**/*.json' : ['json_fixtures'] + }, + + html2JsPreprocessor: { + stripPrefix: 'build/tools' + }, + + // Settings to properly configure how JSON fixtures are automatically loaded by Karma. + jsonFixturesPreprocessor: { + stripPrefix: 'src/test/resources/json', + variableName: '__json__' + }, + + // web server port + port: 9876, + + // enable / disable colors in the output (reporters and logs) + colors: true, + + // enable / disable watching file and executing tests whenever any file changes + autoWatch: true, + + // Continuous Integration mode + // if true, Karma captures browsers, runs the tests and exits + // if false, it generates the pages and acts as a persistent server. + singleRun: true, +} diff --git a/common/web/gesture-recognizer/src/test/auto/browser/cases/canary.js b/common/web/gesture-recognizer/src/test/auto/browser/cases/canary.js new file mode 100644 index 0000000000..f1bb343573 --- /dev/null +++ b/common/web/gesture-recognizer/src/test/auto/browser/cases/canary.js @@ -0,0 +1,25 @@ +let assert = chai.assert; + +describe("'Canary' checks", function() { + this.timeout(testconfig.timeouts.standard); + + before(function() { + fixture.setBase(''); + }) + + it('host-fixture.html + gestureHost.css', function() { + fixture.setBase(''); + let element = fixture.load('host-fixture.html')[0]; + + // Ensure that not only did we get an element, we got the expected element. + assert.isNotNull(element); + assert.isDefined(element); + assert.equal(element.id, 'host-fixture'); + // If the CSS is missing, the element will default to zero height. + assert.notEqual(element.getBoundingClientRect().height, 0); + }) + + after(function() { + fixture.cleanup(); + }) +}); \ No newline at end of file diff --git a/common/web/gesture-recognizer/src/test/auto/browser/manual.conf.js b/common/web/gesture-recognizer/src/test/auto/browser/manual.conf.js new file mode 100644 index 0000000000..f903999d15 --- /dev/null +++ b/common/web/gesture-recognizer/src/test/auto/browser/manual.conf.js @@ -0,0 +1,26 @@ +module.exports = function(config) { + var base = require("./base.conf.js"); + + var specifics = { + // test results reporter to use + // possible values: 'dots', 'progress' + // available reporters: https://npmjs.org/browse/keyword/karma-reporter + reporters: ['mocha'], + + // start these browsers + // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher + browsers: ['Firefox', 'Chrome'], // Can be specified at run-time instead! + // Future note for us: https://www.npmjs.com/package/karma-browserstack-launcher + + // Concurrency level + // how many browser should be started simultaneous + concurrency: Infinity, + + // level of logging + // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + // Can't set in base.conf.js b/c of constant's definition style. + logLevel: config.LOG_INFO + }; + + config.set(Object.assign(specifics, base)); +} diff --git a/common/web/gesture-recognizer/src/test/auto/browser/timeoutConfiguration.js b/common/web/gesture-recognizer/src/test/auto/browser/timeoutConfiguration.js new file mode 100644 index 0000000000..75629fd8ec --- /dev/null +++ b/common/web/gesture-recognizer/src/test/auto/browser/timeoutConfiguration.js @@ -0,0 +1,21 @@ +// Preprocessing of the Karma configuration's client.args parameter. + +com = com || {}; +com.keyman = com.keyman || {}; +com.keyman.karma = com.keyman.karma || {}; + +(function() { + var kmwconfig = window['testconfig'] = {}; + + var configArgs = window['__karma__'].config.args; // Where Karma gives us our custom args. + for(var i = 0; i < configArgs.length; configArgs++) { + switch(configArgs[i].type) { + case 'timeouts': + var timeouts = JSON.parse(JSON.stringify(configArgs[i])); + delete timeouts.type; + + kmwconfig['timeouts'] = timeouts; + break; + } + } +})(); diff --git a/common/web/gesture-recognizer/src/test/test.sh b/common/web/gesture-recognizer/src/test/test.sh new file mode 100755 index 0000000000..12f2a8d6da --- /dev/null +++ b/common/web/gesture-recognizer/src/test/test.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +# +# Builds the include script for the current Keyman version. +# + +# Exit on command failure and when using unset variables: +set -eu + +## START STANDARD BUILD SCRIPT INCLUDE +# adjust relative paths as necessary +THIS_SCRIPT="$(greadlink -f "${BASH_SOURCE[0]}" 2>/dev/null || readlink -f "${BASH_SOURCE[0]}")" +. "$(dirname "$THIS_SCRIPT")/../../../../../resources/build/build-utils.sh" +## END STANDARD BUILD SCRIPT INCLUDE + +# This script runs from its own folder +cd "$(dirname $THIS_SCRIPT)" + +display_usage ( ) { + echo "Usage: $0 [configure] [clean] [build] [test]" + echo " [--verbose|-v]" + echo " $0 -h|--help" + echo + echo " headless runs all headless unit tests (CI or local)" + echo " browser runs all browser-based unit tests in CI mode" + echo " all runs all unit test sets in CI mode" + echo " ci runs selected unit tests via BrowserStack with CI-oriented reporting" + echo " local runs selected unit test sets locally with local-oriented reporting" +} + +test-headless ( ) { + echo "Headless unit tests for this module have not yet been defined." +} + +test-browser ( ) { + if [ $REPORT_STYLE == "local" ]; then + KARMA_CONFIG="manual.conf.js" + else + KARMA_CONFIG="CI.conf.js" + fi + + npm run karma -- start src/test/auto/browser/$KARMA_CONFIG +} + +################################ Main script ################################ + +# Yes, this is a test script, not a build script. But why not use the new +# style anyway? + +builder_init "headless browser all ci local" "$@" + +# TODO: build if out-of-date if test is specified +# TODO: configure if npm has not been run, and build is specified + +# START - Script parameter configuration + +if builder_has_action build >/dev/null; then + builder_chosen_actions+=("all"); +fi + +if builder_has_action all >/dev/null; then + builder_chosen_actions+=("headless"); + builder_chosen_actions+=("browser"); +fi + +if builder_has_action all-local >/dev/null; then + builder_chosen_actions+=("headless"); + builder_chosen_actions+=("browser"); +fi + +if builder_has_action local >/dev/null; then + REPORT_STYLE="local" +elif builder_has_action ci >/dev/null; then + REPORT_STYLE="ci" +else + # Attempt a TC-auto-detect. + echo "Auto-detecting optimal testing style..." + if [ ! -z "${TEAMCITY_VERSION-}" ]; then + echo "TeamCity detected: running in CI mode." + builder_chosen_actions+=("ci"); + REPORT_STYLE="ci" + else + echo "TeamCity not detected: running in local mode." + builder_chosen_actions+=("local"); + REPORT_STYLE="local" + fi +fi + +# END - Script parameter configuration + +if builder_has_action headless; then + test-headless + builder_report headless success +fi + +if builder_has_action browser; then + test-browser + builder_report browser success +fi \ No newline at end of file From 506905719a86c65817bed81090c786cf0dc17e35 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Mon, 18 Jul 2022 12:54:51 +0700 Subject: [PATCH 02/29] feat(web): more canary tests, partial read of recorded obj --- common/web/gesture-recognizer/package.json | 1 + .../src/test/auto/browser/base.conf.js | 2 +- .../src/test/auto/browser/cases/canary.js | 25 +- .../test/resources/json/canaryRecording.json | 213 ++++++++++++++++++ .../web/gesture-recognizer/src/test/test.sh | 16 +- .../src/fixtureLayoutConfiguration.ts | 19 +- 6 files changed, 269 insertions(+), 7 deletions(-) create mode 100644 common/web/gesture-recognizer/src/test/resources/json/canaryRecording.json diff --git a/common/web/gesture-recognizer/package.json b/common/web/gesture-recognizer/package.json index 139c2498fc..290398abee 100644 --- a/common/web/gesture-recognizer/package.json +++ b/common/web/gesture-recognizer/package.json @@ -29,6 +29,7 @@ "clean": "tsc -b --clean", "karma": "karma", "mocha": "mocha", + "test": "gosh ./src/test/test.sh", "tsc": "tsc" }, "dependencies": { diff --git a/common/web/gesture-recognizer/src/test/auto/browser/base.conf.js b/common/web/gesture-recognizer/src/test/auto/browser/base.conf.js index b8a6e0e3ea..3cc27895c0 100644 --- a/common/web/gesture-recognizer/src/test/auto/browser/base.conf.js +++ b/common/web/gesture-recognizer/src/test/auto/browser/base.conf.js @@ -73,7 +73,7 @@ module.exports = { // Settings to properly configure how JSON fixtures are automatically loaded by Karma. jsonFixturesPreprocessor: { - stripPrefix: 'src/test/resources/json', + stripPrefix: 'src/test/resources/json/', variableName: '__json__' }, diff --git a/common/web/gesture-recognizer/src/test/auto/browser/cases/canary.js b/common/web/gesture-recognizer/src/test/auto/browser/cases/canary.js index f1bb343573..d5e76b5eb1 100644 --- a/common/web/gesture-recognizer/src/test/auto/browser/cases/canary.js +++ b/common/web/gesture-recognizer/src/test/auto/browser/cases/canary.js @@ -8,7 +8,6 @@ describe("'Canary' checks", function() { }) it('host-fixture.html + gestureHost.css', function() { - fixture.setBase(''); let element = fixture.load('host-fixture.html')[0]; // Ensure that not only did we get an element, we got the expected element. @@ -17,6 +16,30 @@ describe("'Canary' checks", function() { assert.equal(element.id, 'host-fixture'); // If the CSS is missing, the element will default to zero height. assert.notEqual(element.getBoundingClientRect().height, 0); + }); + + it('canaryRecording.json', function() { + let jsonObject = window['__json__'].canaryRecording; + + assert.isNotNull(jsonObject); + assert.isDefined(jsonObject); + + let config = new Testing.FixtureLayoutConfiguration(jsonObject.config); + assert.equal(config.deviceStyle, 'screen4'); + }); + + it('Testing.HostFixtureLayoutController', function(done) { + let targetRoot = fixture.load('host-fixture.html')[0]; + let jsonObject = window['__json__'].canaryRecording; + + let controller = new Testing.HostFixtureLayoutController(); + // Note: this is set BEFORE the controller is configured (in the following line). + // The class is designed to support this. + controller.layoutConfiguration = new Testing.FixtureLayoutConfiguration(jsonObject.config); + controller.connect().then(() => { + assert.isTrue(targetRoot.className.indexOf('screen4') > -1, "Could not apply configuration spec from recorded JSON!"); + done(); + }); }) after(function() { diff --git a/common/web/gesture-recognizer/src/test/resources/json/canaryRecording.json b/common/web/gesture-recognizer/src/test/resources/json/canaryRecording.json new file mode 100644 index 0000000000..ed1e29f5c7 --- /dev/null +++ b/common/web/gesture-recognizer/src/test/resources/json/canaryRecording.json @@ -0,0 +1,213 @@ +{ + "set": [ + { + "sequence": { + "samples": [ + { + "targetX": 230.9000244140625, + "targetY": 21.166671752929688, + "t": 14809878 + }, + { + "targetX": 229.9000244140625, + "targetY": 22.166671752929688, + "t": 14810141 + }, + { + "targetX": 228.9000244140625, + "targetY": 22.166671752929688, + "t": 14810161 + }, + { + "targetX": 227.9000244140625, + "targetY": 23.166671752929688, + "t": 14810191 + }, + { + "targetX": 226.9000244140625, + "targetY": 23.166671752929688, + "t": 14810208 + }, + { + "targetX": 226.9000244140625, + "targetY": 24.166671752929688, + "t": 14810243 + }, + { + "targetX": 225.9000244140625, + "targetY": 25.166671752929688, + "t": 14810276 + }, + { + "targetX": 224.9000244140625, + "targetY": 26.166671752929688, + "t": 14810359 + }, + { + "targetX": 224.9000244140625, + "targetY": 27.166671752929688, + "t": 14810415 + }, + { + "targetX": 223.9000244140625, + "targetY": 27.166671752929688, + "t": 14810425 + }, + { + "targetX": 223.9000244140625, + "targetY": 28.166671752929688, + "t": 14810478 + }, + { + "targetX": 223.9000244140625, + "targetY": 29.166671752929688, + "t": 14810509 + }, + { + "targetX": 223.9000244140625, + "targetY": 30.166671752929688, + "t": 14810543 + }, + { + "targetX": 223.9000244140625, + "targetY": 31.166671752929688, + "t": 14810558 + }, + { + "targetX": 222.9000244140625, + "targetY": 31.166671752929688, + "t": 14810575 + }, + { + "targetX": 222.9000244140625, + "targetY": 32.16667175292969, + "t": 14810591 + }, + { + "targetX": 222.9000244140625, + "targetY": 33.16667175292969, + "t": 14810608 + }, + { + "targetX": 222.9000244140625, + "targetY": 34.16667175292969, + "t": 14810624 + }, + { + "targetX": 222.9000244140625, + "targetY": 35.16667175292969, + "t": 14810642 + }, + { + "targetX": 222.9000244140625, + "targetY": 36.16667175292969, + "t": 14810675 + }, + { + "targetX": 222.9000244140625, + "targetY": 37.16667175292969, + "t": 14810728 + }, + { + "targetX": 222.9000244140625, + "targetY": 38.16667175292969, + "t": 14810759 + }, + { + "targetX": 223.9000244140625, + "targetY": 38.16667175292969, + "t": 14810775 + }, + { + "targetX": 224.9000244140625, + "targetY": 38.16667175292969, + "t": 14810792 + }, + { + "targetX": 225.9000244140625, + "targetY": 38.16667175292969, + "t": 14810825 + }, + { + "targetX": 225.9000244140625, + "targetY": 39.16667175292969, + "t": 14810842 + }, + { + "targetX": 226.9000244140625, + "targetY": 39.16667175292969, + "t": 14810875 + }, + { + "targetX": 227.9000244140625, + "targetY": 39.16667175292969, + "t": 14810909 + }, + { + "targetX": 228.9000244140625, + "targetY": 39.16667175292969, + "t": 14810925 + }, + { + "targetX": 229.9000244140625, + "targetY": 39.16667175292969, + "t": 14810962 + }, + { + "targetX": 229.9000244140625, + "targetY": 40.16667175292969, + "t": 14810975 + }, + { + "targetX": 230.9000244140625, + "targetY": 40.16667175292969, + "t": 14811010 + }, + { + "targetX": 231.9000244140625, + "targetY": 40.16667175292969, + "t": 14811050 + }, + { + "targetX": 232.9000244140625, + "targetY": 40.16667175292969, + "t": 14811075 + }, + { + "targetX": 233.9000244140625, + "targetY": 40.16667175292969, + "t": 14811115 + }, + { + "targetX": 234.9000244140625, + "targetY": 40.16667175292969, + "t": 14811172 + }, + { + "targetX": 235.9000244140625, + "targetY": 40.16667175292969, + "t": 14811203 + }, + { + "targetX": 236.9000244140625, + "targetY": 40.16667175292969, + "t": 14811227 + }, + { + "targetX": 237.9000244140625, + "targetY": 40.16667175292969, + "t": 14811325 + } + ], + "isFromTouch": false + }, + "terminationEvent": "end" + } + ], + "config": { + "deviceStyle": "screen4", + "roamingStyle": "bounds1", + "receiverStyle": "full", + "safeZoneStyle": "safe-loose" + } +} \ No newline at end of file diff --git a/common/web/gesture-recognizer/src/test/test.sh b/common/web/gesture-recognizer/src/test/test.sh index 12f2a8d6da..1dbb600cd9 100755 --- a/common/web/gesture-recognizer/src/test/test.sh +++ b/common/web/gesture-recognizer/src/test/test.sh @@ -25,6 +25,7 @@ display_usage ( ) { echo " all runs all unit test sets in CI mode" echo " ci runs selected unit tests via BrowserStack with CI-oriented reporting" echo " local runs selected unit test sets locally with local-oriented reporting" + echo " browser-debug runs all browser-based unit tests in persistent local mode." } test-headless ( ) { @@ -32,13 +33,17 @@ test-headless ( ) { } test-browser ( ) { - if [ $REPORT_STYLE == "local" ]; then + KARMA_FLAGS= + if [[ $# -eq 1 && $1 == "debug" ]]; then + KARMA_CONFIG="manual.conf.js" + KARMA_FLAGS="--no-single-run" + elif [ $REPORT_STYLE == "local" ]; then KARMA_CONFIG="manual.conf.js" else KARMA_CONFIG="CI.conf.js" fi - npm run karma -- start src/test/auto/browser/$KARMA_CONFIG + npm run karma -- start src/test/auto/browser/$KARMA_CONFIG "$KARMA_FLAGS" } ################################ Main script ################################ @@ -46,7 +51,7 @@ test-browser ( ) { # Yes, this is a test script, not a build script. But why not use the new # style anyway? -builder_init "headless browser all ci local" "$@" +builder_init "headless browser all ci local browser-debug" "$@" # TODO: build if out-of-date if test is specified # TODO: configure if npm has not been run, and build is specified @@ -92,7 +97,10 @@ if builder_has_action headless; then builder_report headless success fi -if builder_has_action browser; then +if builder_has_action browser-debug; then + test-browser debug + builder_report browser-debug success +elif builder_has_action browser; then test-browser builder_report browser success fi \ No newline at end of file diff --git a/common/web/gesture-recognizer/src/tools/unit-test-resources/src/fixtureLayoutConfiguration.ts b/common/web/gesture-recognizer/src/tools/unit-test-resources/src/fixtureLayoutConfiguration.ts index fb6921a4c2..22f1548522 100644 --- a/common/web/gesture-recognizer/src/tools/unit-test-resources/src/fixtureLayoutConfiguration.ts +++ b/common/web/gesture-recognizer/src/tools/unit-test-resources/src/fixtureLayoutConfiguration.ts @@ -1,3 +1,5 @@ +/// + namespace Testing { /* * Cross-reference all enum definitions in this class with those of host-fixture/gestureHost.css. @@ -35,14 +37,29 @@ namespace Testing { public safeZoneStyle: SafeLayoutClass; constructor() + constructor(recordedConfig: JSONObject) constructor(deviceStyle?: DeviceLayoutClass, roamingStyle?: RoamingLayoutClass, receiverStyle?: ReceiverLayoutClass, safeZoneStyle?: SafeLayoutClass); - constructor(deviceStyle?: DeviceLayoutClass, + constructor(param1?: DeviceLayoutClass | JSONObject, roamingStyle?: RoamingLayoutClass, receiverStyle?: ReceiverLayoutClass, safeZoneStyle?: SafeLayoutClass) { + let deviceStyle: DeviceLayoutClass; + + // Determine constructor form utilized & disambiguate. + if(param1 && typeof param1 != "string") { + const recordedConfig = param1; + + deviceStyle = recordedConfig.deviceStyle; + roamingStyle = recordedConfig.roamingStyle; + receiverStyle = recordedConfig.receiverStyle; + safeZoneStyle = recordedConfig.safeZoneStyle; + } else { + deviceStyle = param1 as DeviceLayoutClass; + } + this.deviceStyle = deviceStyle ?? DeviceLayoutClass.MOBILE_PHONE_PAGE; this.roamingStyle = roamingStyle ?? RoamingLayoutClass.TOP_OVERFLOW; this.receiverStyle = receiverStyle ?? ReceiverLayoutClass.KEYBOARD_STYLE; From 9e6d3c560fa70565f02494fddf173b7f31645685 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Mon, 18 Jul 2022 15:18:43 +0700 Subject: [PATCH 03/29] feat(web): first input-sequence playback --- .../src/gestureRecognizer.ts | 2 +- .../src/test/auto/browser/cases/canary.js | 2 +- .../src/test/auto/browser/cases/layerOne.js | 27 ++++ .../src/inputSequenceSimulator.ts | 142 ++++++++++++++++++ 4 files changed, 171 insertions(+), 2 deletions(-) create mode 100644 common/web/gesture-recognizer/src/test/auto/browser/cases/layerOne.js create mode 100644 common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts diff --git a/common/web/gesture-recognizer/src/gestureRecognizer.ts b/common/web/gesture-recognizer/src/gestureRecognizer.ts index 8f2b8298c9..cb1ce65cd7 100644 --- a/common/web/gesture-recognizer/src/gestureRecognizer.ts +++ b/common/web/gesture-recognizer/src/gestureRecognizer.ts @@ -13,7 +13,7 @@ namespace com.keyman.osk { export class GestureRecognizer extends EventEmitter { public static readonly TRACKED_INPUT_UPDATE_EVENT_NAME = "inputstart"; - protected readonly config: Nonoptional; + public readonly config: Nonoptional; private readonly mouseEngine: MouseEventEngine; private readonly touchEngine: TouchEventEngine; diff --git a/common/web/gesture-recognizer/src/test/auto/browser/cases/canary.js b/common/web/gesture-recognizer/src/test/auto/browser/cases/canary.js index d5e76b5eb1..61cfaa368a 100644 --- a/common/web/gesture-recognizer/src/test/auto/browser/cases/canary.js +++ b/common/web/gesture-recognizer/src/test/auto/browser/cases/canary.js @@ -1,4 +1,4 @@ -let assert = chai.assert; +var assert = chai.assert; describe("'Canary' checks", function() { this.timeout(testconfig.timeouts.standard); diff --git a/common/web/gesture-recognizer/src/test/auto/browser/cases/layerOne.js b/common/web/gesture-recognizer/src/test/auto/browser/cases/layerOne.js new file mode 100644 index 0000000000..ddb45f56b7 --- /dev/null +++ b/common/web/gesture-recognizer/src/test/auto/browser/cases/layerOne.js @@ -0,0 +1,27 @@ +var assert = chai.assert; + +describe("Layer one - DOM -> InputSequence", function() { + this.timeout(testconfig.timeouts.standard); + + before(function() { + fixture.setBase(''); + }); + + beforeEach(function(done) { + fixture.load('host-fixture.html'); + this.controller = new Testing.HostFixtureLayoutController(); + this.controller.connect().then(() => done()); + }); + + afterEach(function() { + this.controller.destroy(); + fixture.cleanup(); + }); + + it('Initial attempt', function() { + let playbackEngine = new Testing.InputSequenceSimulator(this.controller); + let testObj = __json__.canaryRecording; // See src/test/resources/json/canaryRecording.json + + playbackEngine.replay(testObj.set, testObj.config); + }); +}); \ No newline at end of file diff --git a/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts b/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts new file mode 100644 index 0000000000..d206ed21ca --- /dev/null +++ b/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts @@ -0,0 +1,142 @@ +namespace Testing { + /** + * This class is designed to 'replay' a recorded InputSequence's DOM events to ensure + * that the recognizer's DOM layer is working correctly. + */ + export class InputSequenceSimulator { + private controller: HostFixtureLayoutController; + + constructor(controller: HostFixtureLayoutController) { + this.controller = controller; + } + + getSampleClientPos(sample: JSONObject): {clientX: number, clientY: number} { + let targetRoot = this.controller.recognizer.config.targetRoot; + let targetRect = targetRoot.getBoundingClientRect(); + + return { + clientX: sample.targetX + targetRect.left, + clientY: sample.targetY + targetRect.top + } + } + + replayTouchSample(sample: JSONObject, state: string, identifier: number, otherTouches: Touch[]): Touch { + let config = this.controller.recognizer.config; + + let event: TouchEvent; + const mappedSample = this.getSampleClientPos(sample); + + let touch = new Touch({identifier: identifier, + target: config.targetRoot, + ...mappedSample}); + + let touchEventDict = { + touches: [touch], + changedTouches: [touch] + } + + switch(state) { + case 'start': + event = new TouchEvent('touchstart', touchEventDict); + break; + case 'move': + event = new TouchEvent('touchmove', touchEventDict); + break; + case 'end': + event = new TouchEvent('touchend', touchEventDict); + break; + } + + config.touchEventRoot.dispatchEvent(event); + + return touch; + } + + replayMouseSample(sample: JSONObject, state: string) { + let eventRoot = this.controller.recognizer.config.mouseEventRoot; + + let event: MouseEvent; + let eventDict = {...this.getSampleClientPos(sample)}; + switch(state) { + case 'start': + event = new MouseEvent('mousedown', eventDict); + break; + case 'move': + event = new MouseEvent('mousemove', eventDict); + break; + case 'end': + event = new MouseEvent('mouseup', eventDict); + break; + } + + eventRoot.dispatchEvent(event); + } + + replay(set: RecordedSequence[], config: JSONObject) { + this.controller.layoutConfiguration = new FixtureLayoutConfiguration(config); + + /** + * For each corresponding recorded sequence, notes the index of the sequence's + * next sample to be 'replayed'. If the full sequence has already been + * replayed, its corresponding entry will be set to Number.MAX_VALUE instead. + */ + let sequenceProgress: number[] = new Array(set.length).fill(0); + let sequenceTouches: Touch[] = new Array(set.length).fill(null); + + while(sequenceProgress.find((number) => number != Number.MAX_VALUE)) { + // Determine the sequence that has the chronologically next-in-line sample + // to reproduce. + let minTimestamp = Number.MAX_VALUE; + + /** + * Pulls double-duty as the index of the sequence owning said sample & as + * the sequence's common 'identifier' value for emulated Touches. + */ + let selectedSequence = -1; + + for(let index=0; index < set.length; index++) { + const sequence = set[index].sequence; + const indexInSequence = sequenceProgress[index]; + + if(sequence.samples[indexInSequence].t < minTimestamp) { + minTimestamp = sequence.samples[indexInSequence].t; + selectedSequence = index; + } + } + + const sequence = set[selectedSequence].sequence; + const indexInSequence = sequenceProgress[selectedSequence]; + let state: string = "move"; + + let appendEndEvent = false; + if(indexInSequence + 1 >= sequence.samples.length) { + sequenceProgress[selectedSequence] = Number.MAX_VALUE; + appendEndEvent = true; + } else { + sequenceProgress[selectedSequence]++; + } + + if(indexInSequence == 0) { + state = "start" + } + + const sample = sequence.samples[indexInSequence]; + if(sequence.isFromTouch) { + let otherTouches = [...sequenceTouches]; + otherTouches.splice(selectedSequence, 1); + + sequenceTouches[selectedSequence] = this.replayTouchSample(sample, state, selectedSequence, otherTouches); + if(appendEndEvent) { + this.replayTouchSample(sample, "end", selectedSequence, otherTouches); + sequenceTouches[selectedSequence] = null; + } + } else { + this.replayMouseSample(sample, state); + if(appendEndEvent) { + this.replayMouseSample(sample, "end"); + } + } + } + } + } +} \ No newline at end of file From 9db4913331240e21947678c23a0e3b9de02d0175 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Tue, 19 Jul 2022 09:59:12 +0700 Subject: [PATCH 04/29] feat(web): a fully-functional unit test --- .../src/test/auto/browser/cases/canary.js | 2 +- .../src/test/auto/browser/cases/layerOne.js | 23 +- .../test/resources/json/canaryRecording.json | 224 +++++++----------- .../src/inputSequenceSimulator.ts | 47 +++- 4 files changed, 149 insertions(+), 147 deletions(-) diff --git a/common/web/gesture-recognizer/src/test/auto/browser/cases/canary.js b/common/web/gesture-recognizer/src/test/auto/browser/cases/canary.js index 61cfaa368a..d63c78de35 100644 --- a/common/web/gesture-recognizer/src/test/auto/browser/cases/canary.js +++ b/common/web/gesture-recognizer/src/test/auto/browser/cases/canary.js @@ -39,7 +39,7 @@ describe("'Canary' checks", function() { controller.connect().then(() => { assert.isTrue(targetRoot.className.indexOf('screen4') > -1, "Could not apply configuration spec from recorded JSON!"); done(); - }); + }).finally(() => controller.destroy()); }) after(function() { diff --git a/common/web/gesture-recognizer/src/test/auto/browser/cases/layerOne.js b/common/web/gesture-recognizer/src/test/auto/browser/cases/layerOne.js index ddb45f56b7..0e9f8c7105 100644 --- a/common/web/gesture-recognizer/src/test/auto/browser/cases/layerOne.js +++ b/common/web/gesture-recognizer/src/test/auto/browser/cases/layerOne.js @@ -1,4 +1,5 @@ var assert = chai.assert; +var expect = chai.expect; describe("Layer one - DOM -> InputSequence", function() { this.timeout(testconfig.timeouts.standard); @@ -18,10 +19,28 @@ describe("Layer one - DOM -> InputSequence", function() { fixture.cleanup(); }); - it('Initial attempt', function() { + it('First true unit test', function() { let playbackEngine = new Testing.InputSequenceSimulator(this.controller); let testObj = __json__.canaryRecording; // See src/test/resources/json/canaryRecording.json - playbackEngine.replay(testObj.set, testObj.config); + let result = playbackEngine.replay(testObj); + assert.equal(testObj.set.length, result.set.length); + + // Removes the timestamp element; we know that this component won't match, as the playback engine + // doesn't care about it. + let sampleCleaner = (sample) => { return {targetX: sample.targetX, targetY: sample.targetY} }; + + // Returns just the observed, cleaned samples for a sequence object. The recorded coordinates + // should match perfectly. + let seqCleaner = (seq) => { return seq.sequence.samples.map(sampleCleaner) }; + let cleanOriginalSet = testObj.set.map(seqCleaner); + let cleanResultSet = result .set.map(seqCleaner); + + expect(cleanResultSet).to.deep.equal(cleanOriginalSet); + + // The 'terminationEvent' property should match. Any sequence that was "canceled" should still + // cancel; that's a pretty critical detail! + let terminationEventMapper = (seq) => seq.terminationEvent; + expect(result.set.map(terminationEventMapper)).to.deep.equal(testObj.set.map(terminationEventMapper)); }); }); \ No newline at end of file diff --git a/common/web/gesture-recognizer/src/test/resources/json/canaryRecording.json b/common/web/gesture-recognizer/src/test/resources/json/canaryRecording.json index ed1e29f5c7..5eecbf1e20 100644 --- a/common/web/gesture-recognizer/src/test/resources/json/canaryRecording.json +++ b/common/web/gesture-recognizer/src/test/resources/json/canaryRecording.json @@ -4,199 +4,149 @@ "sequence": { "samples": [ { - "targetX": 230.9000244140625, - "targetY": 21.166671752929688, - "t": 14809878 + "targetX": 231, + "targetY": 32, + "t": 388913 }, { - "targetX": 229.9000244140625, - "targetY": 22.166671752929688, - "t": 14810141 + "targetX": 230, + "targetY": 32, + "t": 389101 }, { - "targetX": 228.9000244140625, - "targetY": 22.166671752929688, - "t": 14810161 + "targetX": 229, + "targetY": 32, + "t": 389131 }, { - "targetX": 227.9000244140625, - "targetY": 23.166671752929688, - "t": 14810191 + "targetX": 228, + "targetY": 32, + "t": 389164 }, { - "targetX": 226.9000244140625, - "targetY": 23.166671752929688, - "t": 14810208 + "targetX": 228, + "targetY": 33, + "t": 389173 }, { - "targetX": 226.9000244140625, - "targetY": 24.166671752929688, - "t": 14810243 + "targetX": 227, + "targetY": 33, + "t": 389206 }, { - "targetX": 225.9000244140625, - "targetY": 25.166671752929688, - "t": 14810276 + "targetX": 226, + "targetY": 34, + "t": 389240 }, { - "targetX": 224.9000244140625, - "targetY": 26.166671752929688, - "t": 14810359 + "targetX": 225, + "targetY": 34, + "t": 389275 }, { - "targetX": 224.9000244140625, - "targetY": 27.166671752929688, - "t": 14810415 + "targetX": 225, + "targetY": 35, + "t": 389306 }, { - "targetX": 223.9000244140625, - "targetY": 27.166671752929688, - "t": 14810425 + "targetX": 225, + "targetY": 36, + "t": 389390 }, { - "targetX": 223.9000244140625, - "targetY": 28.166671752929688, - "t": 14810478 + "targetX": 225, + "targetY": 37, + "t": 389479 }, { - "targetX": 223.9000244140625, - "targetY": 29.166671752929688, - "t": 14810509 + "targetX": 225, + "targetY": 38, + "t": 389609 }, { - "targetX": 223.9000244140625, - "targetY": 30.166671752929688, - "t": 14810543 + "targetX": 226, + "targetY": 38, + "t": 389851 }, { - "targetX": 223.9000244140625, - "targetY": 31.166671752929688, - "t": 14810558 + "targetX": 226, + "targetY": 39, + "t": 389856 }, { - "targetX": 222.9000244140625, - "targetY": 31.166671752929688, - "t": 14810575 + "targetX": 227, + "targetY": 39, + "t": 389872 }, { - "targetX": 222.9000244140625, - "targetY": 32.16667175292969, - "t": 14810591 + "targetX": 228, + "targetY": 39, + "t": 389906 }, { - "targetX": 222.9000244140625, - "targetY": 33.16667175292969, - "t": 14810608 + "targetX": 228, + "targetY": 40, + "t": 389923 }, { - "targetX": 222.9000244140625, - "targetY": 34.16667175292969, - "t": 14810624 + "targetX": 229, + "targetY": 40, + "t": 389957 }, { - "targetX": 222.9000244140625, - "targetY": 35.16667175292969, - "t": 14810642 + "targetX": 230, + "targetY": 40, + "t": 389993 }, { - "targetX": 222.9000244140625, - "targetY": 36.16667175292969, - "t": 14810675 + "targetX": 231, + "targetY": 40, + "t": 390025 }, { - "targetX": 222.9000244140625, - "targetY": 37.16667175292969, - "t": 14810728 + "targetX": 232, + "targetY": 40, + "t": 390056 }, { - "targetX": 222.9000244140625, - "targetY": 38.16667175292969, - "t": 14810759 + "targetX": 233, + "targetY": 39, + "t": 390098 }, { - "targetX": 223.9000244140625, - "targetY": 38.16667175292969, - "t": 14810775 + "targetX": 234, + "targetY": 39, + "t": 390164 }, { - "targetX": 224.9000244140625, - "targetY": 38.16667175292969, - "t": 14810792 + "targetX": 234, + "targetY": 38, + "t": 390213 }, { - "targetX": 225.9000244140625, - "targetY": 38.16667175292969, - "t": 14810825 + "targetX": 235, + "targetY": 38, + "t": 390223 }, { - "targetX": 225.9000244140625, - "targetY": 39.16667175292969, - "t": 14810842 + "targetX": 236, + "targetY": 38, + "t": 390257 }, { - "targetX": 226.9000244140625, - "targetY": 39.16667175292969, - "t": 14810875 + "targetX": 236, + "targetY": 37, + "t": 390325 }, { - "targetX": 227.9000244140625, - "targetY": 39.16667175292969, - "t": 14810909 + "targetX": 237, + "targetY": 37, + "t": 390654 }, { - "targetX": 228.9000244140625, - "targetY": 39.16667175292969, - "t": 14810925 - }, - { - "targetX": 229.9000244140625, - "targetY": 39.16667175292969, - "t": 14810962 - }, - { - "targetX": 229.9000244140625, - "targetY": 40.16667175292969, - "t": 14810975 - }, - { - "targetX": 230.9000244140625, - "targetY": 40.16667175292969, - "t": 14811010 - }, - { - "targetX": 231.9000244140625, - "targetY": 40.16667175292969, - "t": 14811050 - }, - { - "targetX": 232.9000244140625, - "targetY": 40.16667175292969, - "t": 14811075 - }, - { - "targetX": 233.9000244140625, - "targetY": 40.16667175292969, - "t": 14811115 - }, - { - "targetX": 234.9000244140625, - "targetY": 40.16667175292969, - "t": 14811172 - }, - { - "targetX": 235.9000244140625, - "targetY": 40.16667175292969, - "t": 14811203 - }, - { - "targetX": 236.9000244140625, - "targetY": 40.16667175292969, - "t": 14811227 - }, - { - "targetX": 237.9000244140625, - "targetY": 40.16667175292969, - "t": 14811325 + "targetX": 237, + "targetY": 36, + "t": 390776 } ], "isFromTouch": false diff --git a/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts b/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts index d206ed21ca..088926b957 100644 --- a/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts +++ b/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts @@ -30,7 +30,8 @@ namespace Testing { target: config.targetRoot, ...mappedSample}); - let touchEventDict = { + let touchEventDict: TouchEventInit = { + bubbles: true, touches: [touch], changedTouches: [touch] } @@ -47,16 +48,21 @@ namespace Testing { break; } - config.touchEventRoot.dispatchEvent(event); + config.targetRoot.dispatchEvent(event); return touch; } replayMouseSample(sample: JSONObject, state: string) { - let eventRoot = this.controller.recognizer.config.mouseEventRoot; + let config = this.controller.recognizer.config; let event: MouseEvent; - let eventDict = {...this.getSampleClientPos(sample)}; + let eventDict: MouseEventInit = { + bubbles: true, + buttons: state == 'end' ? 0 : 1, + ...this.getSampleClientPos(sample) + }; + switch(state) { case 'start': event = new MouseEvent('mousedown', eventDict); @@ -69,10 +75,28 @@ namespace Testing { break; } - eventRoot.dispatchEvent(event); + config.targetRoot.dispatchEvent(event); } - replay(set: RecordedSequence[], config: JSONObject) { + /** + * Given a previously-recorded sequence of sampled input coordinates, this function + * reproduces their corresponding events and runs them through this instance's linked + * gesture-recognizer instance. + * + * It will re-use the original fixture-layout configuration, **but** it will ignore + * all timestamp information, as that is nigh-impossible to perfectly reproduce without + * busy-waiting. + * @param sequenceTestSpec A previously-recorded sequence of sampled input coordinates. + * @returns A recording of the reproduced version of that sequence. + */ + replay(sequenceTestSpec: RecordedCoordSequenceSet): RecordedCoordSequenceSet { + const set = sequenceTestSpec.set; + const config = sequenceTestSpec.config; + + // We're going to record the test spec as we replay it, eventually returning the + // final result. + const recorder = new Testing.SequenceRecorder(this.controller); + this.controller.layoutConfiguration = new FixtureLayoutConfiguration(config); /** @@ -83,7 +107,7 @@ namespace Testing { let sequenceProgress: number[] = new Array(set.length).fill(0); let sequenceTouches: Touch[] = new Array(set.length).fill(null); - while(sequenceProgress.find((number) => number != Number.MAX_VALUE)) { + while(sequenceProgress.find((number) => number != Number.MAX_VALUE) !== undefined) { // Determine the sequence that has the chronologically next-in-line sample // to reproduce. let minTimestamp = Number.MAX_VALUE; @@ -98,6 +122,10 @@ namespace Testing { const sequence = set[index].sequence; const indexInSequence = sequenceProgress[index]; + if(indexInSequence == Number.MAX_VALUE) { + continue; + } + if(sequence.samples[indexInSequence].t < minTimestamp) { minTimestamp = sequence.samples[indexInSequence].t; selectedSequence = index; @@ -137,6 +165,11 @@ namespace Testing { } } } + + // We technically don't have access to 'samples' for the actual recorded object. + // Obtaining a 'deep copy' (though methodless) works around this nicely and + // matches the original `sequenceTestSpec`'s format to boot. + return JSON.parse(recorder.recordingsToJSON()) as RecordedCoordSequenceSet; } } } \ No newline at end of file From f67d51bccfce6618c81ca323ebec0ba8ff683cc8 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Tue, 19 Jul 2022 10:04:05 +0700 Subject: [PATCH 05/29] feat(web): links gesture-recognizer unit test for CI --- web/unit_tests/test.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/web/unit_tests/test.sh b/web/unit_tests/test.sh index be2076b3be..95f3aa7de5 100755 --- a/web/unit_tests/test.sh +++ b/web/unit_tests/test.sh @@ -131,9 +131,15 @@ cd ../tools/recorder # First: Web-core tests. pushd "$KEYMAN_ROOT/common/web/input-processor" ./test.sh $HEADLESS_FLAGS || fail "Tests failed by dependencies; aborting integration tests." -# Once done, now we run the integrated (KeymanWeb) tests. popd +# For now, we'll also link in the gesture-recognizer unit tests here. +echo_heading "Running gesture-recognizer test suite" +pushd "$KEYMAN_ROOT/common/web/gesture-recognizer" +./build.sh tools test +popd + +# Once done, now we run the integrated (KeymanWeb) tests. echo_heading "Running KeymanWeb integration test suite" npm --no-color run modernizr -- -c unit_tests/modernizr.config.json -d unit_tests/modernizr.js npm --no-color run karma -- start $FLAGS $BROWSERS unit_tests/$CONFIG From 3763f3f546259a5f6f6dffedc27e9231d3219192 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Tue, 19 Jul 2022 10:51:42 +0700 Subject: [PATCH 06/29] fix(web): missing mkdir in tools/build.sh --- common/web/gesture-recognizer/src/tools/recorder/build.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/common/web/gesture-recognizer/src/tools/recorder/build.sh b/common/web/gesture-recognizer/src/tools/recorder/build.sh index 83945c94a8..67dafe8193 100644 --- a/common/web/gesture-recognizer/src/tools/recorder/build.sh +++ b/common/web/gesture-recognizer/src/tools/recorder/build.sh @@ -28,6 +28,7 @@ display_usage ( ) { } build_recorder_page ( ) { + mkdir build cp src/pageStyle.css build/pageStyle.css cp src/recorder.js build/recorder.js From 0694ca459b7383c934939d1b9f996ef5943f7c42 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Tue, 19 Jul 2022 11:16:44 +0700 Subject: [PATCH 07/29] fix(web): executable flags --- common/web/gesture-recognizer/src/tools/recorder/build.sh | 0 .../web/gesture-recognizer/src/tools/unit-test-resources/build.sh | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 common/web/gesture-recognizer/src/tools/recorder/build.sh mode change 100644 => 100755 common/web/gesture-recognizer/src/tools/unit-test-resources/build.sh diff --git a/common/web/gesture-recognizer/src/tools/recorder/build.sh b/common/web/gesture-recognizer/src/tools/recorder/build.sh old mode 100644 new mode 100755 diff --git a/common/web/gesture-recognizer/src/tools/unit-test-resources/build.sh b/common/web/gesture-recognizer/src/tools/unit-test-resources/build.sh old mode 100644 new mode 100755 From 71cb4fe1a716e18072a7a4858c18300795feb926 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Tue, 19 Jul 2022 12:08:01 +0700 Subject: [PATCH 08/29] fix(web): another executable flag --- .../gesture-recognizer/src/tools/host-fixture/extract-fixture.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 common/web/gesture-recognizer/src/tools/host-fixture/extract-fixture.sh diff --git a/common/web/gesture-recognizer/src/tools/host-fixture/extract-fixture.sh b/common/web/gesture-recognizer/src/tools/host-fixture/extract-fixture.sh old mode 100644 new mode 100755 From 4b9a99207161044de94440b8d82683b213881673 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Tue, 19 Jul 2022 13:02:35 +0700 Subject: [PATCH 09/29] fix(web): mkdir -p use --- common/web/gesture-recognizer/src/tools/recorder/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/web/gesture-recognizer/src/tools/recorder/build.sh b/common/web/gesture-recognizer/src/tools/recorder/build.sh index 67dafe8193..bfb2d3cbce 100755 --- a/common/web/gesture-recognizer/src/tools/recorder/build.sh +++ b/common/web/gesture-recognizer/src/tools/recorder/build.sh @@ -28,7 +28,7 @@ display_usage ( ) { } build_recorder_page ( ) { - mkdir build + mkdir -p build cp src/pageStyle.css build/pageStyle.css cp src/recorder.js build/recorder.js From 5cbe959bc0634b0ca9ed0a66d5d820a1deef4a7d Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Tue, 19 Jul 2022 13:40:51 +0700 Subject: [PATCH 10/29] fix(web): realign after changing config --- .../src/tools/recorder/src/recorder.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/common/web/gesture-recognizer/src/tools/recorder/src/recorder.js b/common/web/gesture-recognizer/src/tools/recorder/src/recorder.js index 18cb160b13..185b7a5753 100644 --- a/common/web/gesture-recognizer/src/tools/recorder/src/recorder.js +++ b/common/web/gesture-recognizer/src/tools/recorder/src/recorder.js @@ -120,6 +120,21 @@ function updateConfig() { ); controller.layoutConfiguration = layoutSpec; + + // Check sub-pixel alignment of the main fixture element; we can't do exact repros for unit testing + // if the recorded sequence has sub-pixel coordinates. This may happen otherwise due to the page's + // formatting. + let aligner = document.getElementById('aligner'); + let hostFixture = document.getElementById('host-fixture'); + aligner.style.left = 0; + aligner.style.top = 0; + + let clientRect = hostFixture.getBoundingClientRect(); + let roundX = Math.round(clientRect.x); + let roundY = Math.round(clientRect.y); + + aligner.style.left = roundX - clientRect.x; + aligner.style.top = roundY - clientRect.y; } // END: Layout-simulation setup & handling \ No newline at end of file From bacfe6d5079b2bb3d088ae8b4f40fee6f1bb5bf0 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Tue, 19 Jul 2022 14:49:31 +0700 Subject: [PATCH 11/29] fix(web): identified sequence-repro issues --- .../src/inputEventEngine.ts | 8 +- .../src/mouseEventEngine.ts | 7 +- .../src/test/auto/browser/cases/layerOne.js | 46 --- .../browser/cases/recordedCoordSequences.js | 73 +++++ .../resources/json/desktopRoamAndReturn.json | 288 ++++++++++++++++++ .../resources/json/mobileSafeZoneCancel.json | 38 +++ .../src/tools/recorder/src/index.html | 6 + .../src/inputSequenceSimulator.ts | 49 ++- .../src/touchEventEngine.ts | 6 +- 9 files changed, 456 insertions(+), 65 deletions(-) delete mode 100644 common/web/gesture-recognizer/src/test/auto/browser/cases/layerOne.js create mode 100644 common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js create mode 100644 common/web/gesture-recognizer/src/test/resources/json/desktopRoamAndReturn.json create mode 100644 common/web/gesture-recognizer/src/test/resources/json/mobileSafeZoneCancel.json diff --git a/common/web/gesture-recognizer/src/inputEventEngine.ts b/common/web/gesture-recognizer/src/inputEventEngine.ts index d52985fae7..f62b7bc176 100644 --- a/common/web/gesture-recognizer/src/inputEventEngine.ts +++ b/common/web/gesture-recognizer/src/inputEventEngine.ts @@ -77,23 +77,27 @@ namespace com.keyman.osk { sequenceWrapper.signalUpdate(sample); } - onInputMoveCancel(identifier: number) { + onInputMoveCancel(identifier: number, sample: InputSample) { let sequenceWrapper = this.getSequenceWrapperWithId(identifier); if(!sequenceWrapper) { return; } + sequenceWrapper.item.addSample(sample); + sequenceWrapper.signalUpdate(sample); sequenceWrapper.cancel(); } - onInputEnd(identifier: number, sample: InputSample) { + onInputEnd(identifier: number) { let sequenceWrapper = this.getSequenceWrapperWithId(identifier); if(!sequenceWrapper) { return; } + // We do not add a sample here because any 'end' event immediately follows a + // 'move' if it occurred simultaneously. sequenceWrapper.end(); } } diff --git a/common/web/gesture-recognizer/src/mouseEventEngine.ts b/common/web/gesture-recognizer/src/mouseEventEngine.ts index c0450cd287..cbbc8af4a8 100644 --- a/common/web/gesture-recognizer/src/mouseEventEngine.ts +++ b/common/web/gesture-recognizer/src/mouseEventEngine.ts @@ -118,7 +118,7 @@ namespace com.keyman.osk { if(!event.buttons) { if(this.hasActiveClick) { this.hasActiveClick = false; - this.onInputMoveCancel(this.activeIdentifier); + this.onInputMoveCancel(this.activeIdentifier, sample); } return; } @@ -128,7 +128,7 @@ namespace com.keyman.osk { if(!ZoneBoundaryChecker.inputMoveCancellationCheck(sample, this.config, this.disabledSafeBounds)) { this.onInputMove(this.activeIdentifier, sample); } else { - this.onInputMoveCancel(this.activeIdentifier); + this.onInputMoveCancel(this.activeIdentifier, sample); } } @@ -141,8 +141,7 @@ namespace com.keyman.osk { this.hasActiveClick = false; } - const sample = this.buildSampleFromEvent(event); - this.onInputEnd(this.activeIdentifier, sample); + this.onInputEnd(this.activeIdentifier); } } } \ No newline at end of file diff --git a/common/web/gesture-recognizer/src/test/auto/browser/cases/layerOne.js b/common/web/gesture-recognizer/src/test/auto/browser/cases/layerOne.js deleted file mode 100644 index 0e9f8c7105..0000000000 --- a/common/web/gesture-recognizer/src/test/auto/browser/cases/layerOne.js +++ /dev/null @@ -1,46 +0,0 @@ -var assert = chai.assert; -var expect = chai.expect; - -describe("Layer one - DOM -> InputSequence", function() { - this.timeout(testconfig.timeouts.standard); - - before(function() { - fixture.setBase(''); - }); - - beforeEach(function(done) { - fixture.load('host-fixture.html'); - this.controller = new Testing.HostFixtureLayoutController(); - this.controller.connect().then(() => done()); - }); - - afterEach(function() { - this.controller.destroy(); - fixture.cleanup(); - }); - - it('First true unit test', function() { - let playbackEngine = new Testing.InputSequenceSimulator(this.controller); - let testObj = __json__.canaryRecording; // See src/test/resources/json/canaryRecording.json - - let result = playbackEngine.replay(testObj); - assert.equal(testObj.set.length, result.set.length); - - // Removes the timestamp element; we know that this component won't match, as the playback engine - // doesn't care about it. - let sampleCleaner = (sample) => { return {targetX: sample.targetX, targetY: sample.targetY} }; - - // Returns just the observed, cleaned samples for a sequence object. The recorded coordinates - // should match perfectly. - let seqCleaner = (seq) => { return seq.sequence.samples.map(sampleCleaner) }; - let cleanOriginalSet = testObj.set.map(seqCleaner); - let cleanResultSet = result .set.map(seqCleaner); - - expect(cleanResultSet).to.deep.equal(cleanOriginalSet); - - // The 'terminationEvent' property should match. Any sequence that was "canceled" should still - // cancel; that's a pretty critical detail! - let terminationEventMapper = (seq) => seq.terminationEvent; - expect(result.set.map(terminationEventMapper)).to.deep.equal(testObj.set.map(terminationEventMapper)); - }); -}); \ No newline at end of file diff --git a/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js b/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js new file mode 100644 index 0000000000..cd1413e91a --- /dev/null +++ b/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js @@ -0,0 +1,73 @@ +var assert = chai.assert; +var expect = chai.expect; + +describe("Layer one - DOM -> InputSequence", function() { + this.timeout(testconfig.timeouts.standard); + + before(function() { + fixture.setBase(''); + }); + + beforeEach(function(done) { + fixture.load('host-fixture.html'); + this.controller = new Testing.HostFixtureLayoutController(); + this.controller.connect().then(() => done()); + }); + + afterEach(function() { + this.controller.destroy(); + fixture.cleanup(); + fixture.cleanup(); + }); + + describe('recorded input sequences', function() { + // We rely on this function to have the same context as `it` - the test-definition function. + let replayAndCompare = function(testObj) { + // Everything else can be extracted to a common test function... probably. + let playbackEngine = new Testing.InputSequenceSimulator(this.controller); + let result = playbackEngine.replay(testObj); + + assert.equal(result.set.length, testObj.set.length); + + // Removes the timestamp element; we know that this component won't match, as the playback engine + // doesn't care about it. + let sampleCleaner = (sample) => { return {targetX: sample.targetX, targetY: sample.targetY} }; + + // Returns just the observed, cleaned samples for a sequence object. The recorded coordinates + // should match perfectly. + let seqCleaner = (seq) => { return seq.sequence.samples.map(sampleCleaner) }; + let cleanOriginalSet = testObj.set.map(seqCleaner); + let cleanResultSet = result .set.map(seqCleaner); + + expect(cleanResultSet).to.deep.equal(cleanOriginalSet); + + // The 'terminationEvent' property should match. Any sequence that was "canceled" should still + // cancel; that's a pretty critical detail! + let terminationEventMapper = (seq) => seq.terminationEvent; + expect(result.set.map(terminationEventMapper)).to.deep.equal(testObj.set.map(terminationEventMapper)); + } + + // List all relevant fixtures in src/test/resources/json. + let testRecordings = [ + 'canaryRecording', + 'desktopRoamAndReturn', + 'mobileSafeZoneCancel', + 'mobileProximityApproach', + 'embeddedBorderCancel', + 'hardBorderCancel', + 'popupLongRoamingEnd', + 'popupShimCancel' + ]; + + for(let recordingID of testRecordings) { + it(`${recordingID}.json`, function() { + let testObj = __json__[recordingID]; + + console.log(`expected terminator: ${testObj.set[0].terminationEvent}`) + + // 'describe' has a notably different `this` reference than `it`, `before`, etc. + replayAndCompare.call(this, testObj); + }); + } + }); +}); \ No newline at end of file diff --git a/common/web/gesture-recognizer/src/test/resources/json/desktopRoamAndReturn.json b/common/web/gesture-recognizer/src/test/resources/json/desktopRoamAndReturn.json new file mode 100644 index 0000000000..336a13c1d1 --- /dev/null +++ b/common/web/gesture-recognizer/src/test/resources/json/desktopRoamAndReturn.json @@ -0,0 +1,288 @@ +{ + "set": [ + { + "sequence": { + "samples": [ + { + "targetX": 119, + "targetY": 27, + "t": 670652.6999999881 + }, + { + "targetX": 119, + "targetY": 26, + "t": 670770.8999999762 + }, + { + "targetX": 119, + "targetY": 25, + "t": 670783.5 + }, + { + "targetX": 119, + "targetY": 24, + "t": 670792.5 + }, + { + "targetX": 119, + "targetY": 24, + "t": 670815.2999999523 + }, + { + "targetX": 119, + "targetY": 23, + "t": 670825.8999999762 + }, + { + "targetX": 119, + "targetY": 22, + "t": 670848.1999999881 + }, + { + "targetX": 119, + "targetY": 21, + "t": 670859.0999999642 + }, + { + "targetX": 119, + "targetY": 20, + "t": 670875.8999999762 + }, + { + "targetX": 119, + "targetY": 20, + "t": 670892.6999999881 + }, + { + "targetX": 119, + "targetY": 19, + "t": 670909.1999999881 + }, + { + "targetX": 119, + "targetY": 18, + "t": 670926.5 + }, + { + "targetX": 119, + "targetY": 16, + "t": 670942.5 + }, + { + "targetX": 118, + "targetY": 15, + "t": 670959.1999999881 + }, + { + "targetX": 118, + "targetY": 12, + "t": 670975.7999999523 + }, + { + "targetX": 118, + "targetY": 12, + "t": 670993.1999999881 + }, + { + "targetX": 118, + "targetY": 10, + "t": 671009.1999999881 + }, + { + "targetX": 118, + "targetY": 8, + "t": 671025.8999999762 + }, + { + "targetX": 118, + "targetY": 6, + "t": 671042.5999999642 + }, + { + "targetX": 118, + "targetY": 5, + "t": 671059.2999999523 + }, + { + "targetX": 118, + "targetY": 4, + "t": 671075.8999999762 + }, + { + "targetX": 118, + "targetY": 2, + "t": 671092.5999999642 + }, + { + "targetX": 118, + "targetY": 0, + "t": 671109.1999999881 + }, + { + "targetX": 118, + "targetY": 0, + "t": 671125.8999999762 + }, + { + "targetX": 118, + "targetY": -2, + "t": 671142.5999999642 + }, + { + "targetX": 118, + "targetY": -3, + "t": 671159.0999999642 + }, + { + "targetX": 118, + "targetY": -4, + "t": 671175.7999999523 + }, + { + "targetX": 118, + "targetY": -4, + "t": 671192.5 + }, + { + "targetX": 118, + "targetY": -5, + "t": 671244.3999999762 + }, + { + "targetX": 118, + "targetY": -6, + "t": 671260 + }, + { + "targetX": 119, + "targetY": -7, + "t": 671275.8999999762 + }, + { + "targetX": 120, + "targetY": -7, + "t": 671326.1999999881 + }, + { + "targetX": 120, + "targetY": -8, + "t": 671342.5 + }, + { + "targetX": 120, + "targetY": -8, + "t": 671359.8999999762 + }, + { + "targetX": 120, + "targetY": -8, + "t": 671376 + }, + { + "targetX": 121, + "targetY": -8, + "t": 671395.1999999881 + }, + { + "targetX": 121, + "targetY": -9, + "t": 671420.5999999642 + }, + { + "targetX": 122, + "targetY": -9, + "t": 671460 + }, + { + "targetX": 123, + "targetY": -9, + "t": 671475.8999999762 + }, + { + "targetX": 124, + "targetY": -9, + "t": 671525.8999999762 + }, + { + "targetX": 125, + "targetY": -8, + "t": 671542.5999999642 + }, + { + "targetX": 126, + "targetY": -8, + "t": 671559.2999999523 + }, + { + "targetX": 127, + "targetY": -6, + "t": 671575.7999999523 + }, + { + "targetX": 128, + "targetY": -4, + "t": 671592.5999999642 + }, + { + "targetX": 128, + "targetY": -3, + "t": 671609.2999999523 + }, + { + "targetX": 129, + "targetY": -1, + "t": 671625.8999999762 + }, + { + "targetX": 129, + "targetY": 0, + "t": 671642.5999999642 + }, + { + "targetX": 129, + "targetY": 0, + "t": 671659.1999999881 + }, + { + "targetX": 129, + "targetY": 1, + "t": 671675.8999999762 + }, + { + "targetX": 130, + "targetY": 1, + "t": 671692.8999999762 + }, + { + "targetX": 130, + "targetY": 3, + "t": 671709.2999999523 + }, + { + "targetX": 130, + "targetY": 4, + "t": 671734.8999999762 + }, + { + "targetX": 130, + "targetY": 4, + "t": 671749.5 + }, + { + "targetX": 130, + "targetY": 5, + "t": 671793 + } + ], + "isFromTouch": false + }, + "terminationEvent": "end" + } + ], + "config": { + "deviceStyle": "screen3", + "roamingStyle": "bounds1", + "receiverStyle": "full", + "safeZoneStyle": "safe-loose" + } +} \ No newline at end of file diff --git a/common/web/gesture-recognizer/src/test/resources/json/mobileSafeZoneCancel.json b/common/web/gesture-recognizer/src/test/resources/json/mobileSafeZoneCancel.json new file mode 100644 index 0000000000..508b28be0c --- /dev/null +++ b/common/web/gesture-recognizer/src/test/resources/json/mobileSafeZoneCancel.json @@ -0,0 +1,38 @@ +{ + "set": [ + { + "sequence": { + "samples": [ + { + "targetX": 226, + "targetY": 38, + "t": 61290 + }, + { + "targetX": 234, + "targetY": 38, + "t": 61676 + }, + { + "targetX": 235, + "targetY": 37, + "t": 61729 + }, + { + "targetX": 237, + "targetY": 37, + "t": 61829 + } + ], + "isFromTouch": true + }, + "terminationEvent": "cancel" + } + ], + "config": { + "deviceStyle": "screen2", + "roamingStyle": "bounds1", + "receiverStyle": "full", + "safeZoneStyle": "safe-loose" + } +} \ No newline at end of file diff --git a/common/web/gesture-recognizer/src/tools/recorder/src/index.html b/common/web/gesture-recognizer/src/tools/recorder/src/index.html index 9438072dee..4584984ec0 100644 --- a/common/web/gesture-recognizer/src/tools/recorder/src/index.html +++ b/common/web/gesture-recognizer/src/tools/recorder/src/index.html @@ -25,6 +25,12 @@

The area below represents a simulated page with its corresponding screen, page, and gesture-reception area.

+

+ NOTE: For setting up touch-based sequences from a normal computer, use of Firefox's touch-emulation mode is + advised. Chrome's version tends to produce touch events with subpixel coordinates, whereas Firefox's doesn't. + + You may also need to manually tweak the final coordinate a little bit for cross-browser consistency. +

diff --git a/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts b/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts index 088926b957..901b8e3469 100644 --- a/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts +++ b/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts @@ -20,31 +20,60 @@ namespace Testing { } } + private buildSyntheticTouchEvent(name: string, dict: TouchEventInit): TouchEvent { + let config = this.controller.recognizer.config; + + let fullDict = { + srcElement: config.touchEventRoot, + view: window, + ...dict + } as any; + + return new Event('touchstart', fullDict) as TouchEvent; + } + replayTouchSample(sample: JSONObject, state: string, identifier: number, otherTouches: Touch[]): Touch { let config = this.controller.recognizer.config; let event: TouchEvent; const mappedSample = this.getSampleClientPos(sample); - let touch = new Touch({identifier: identifier, - target: config.targetRoot, - ...mappedSample}); + let touch: Touch; + let touchDict = {identifier: identifier, + target: config.targetRoot, + ...mappedSample}; + if(window['Touch'] !== undefined) { + touch = new Touch(touchDict); + } else { + // When not performing touch-emulation, some desktop browsers will leave `Touch` undefined. + touch = touchDict as any; + } + + otherTouches = otherTouches || []; let touchEventDict: TouchEventInit = { bubbles: true, - touches: [touch], - changedTouches: [touch] + touches: [touch, ...otherTouches], + changedTouches: [touch], + } + + let buildEvent = (type: string, dict: TouchEventInit) => { + if(window['TouchEvent'] !== undefined) { + return new TouchEvent(type, dict); + } else { + return this.buildSyntheticTouchEvent(type, dict); + } } switch(state) { case 'start': - event = new TouchEvent('touchstart', touchEventDict); + event = buildEvent('touchstart', touchEventDict); break; case 'move': - event = new TouchEvent('touchmove', touchEventDict); + event = buildEvent('touchmove', touchEventDict); break; case 'end': - event = new TouchEvent('touchend', touchEventDict); + event = buildEvent('touchend', touchEventDict); break; } @@ -169,7 +198,9 @@ namespace Testing { // We technically don't have access to 'samples' for the actual recorded object. // Obtaining a 'deep copy' (though methodless) works around this nicely and // matches the original `sequenceTestSpec`'s format to boot. - return JSON.parse(recorder.recordingsToJSON()) as RecordedCoordSequenceSet; + let recording = JSON.parse(recorder.recordingsToJSON()) as RecordedCoordSequenceSet; + recorder.clear(); + return recording; } } } \ No newline at end of file diff --git a/common/web/gesture-recognizer/src/touchEventEngine.ts b/common/web/gesture-recognizer/src/touchEventEngine.ts index a67f292d5b..a05b07ce1d 100644 --- a/common/web/gesture-recognizer/src/touchEventEngine.ts +++ b/common/web/gesture-recognizer/src/touchEventEngine.ts @@ -119,7 +119,7 @@ namespace com.keyman.osk { if(!ZoneBoundaryChecker.inputMoveCancellationCheck(sample, this.config, this.safeBoundMaskMap[touch.identifier])) { this.onInputMove(touch.identifier, sample); } else { - this.onInputMoveCancel(touch.identifier); + this.onInputMoveCancel(touch.identifier, sample); } } } @@ -134,9 +134,7 @@ namespace com.keyman.osk { this.preventPropagation(event); - const sample = this.buildSampleFromTouch(touch); - - this.onInputEnd(touch.identifier, sample); + this.onInputEnd(touch.identifier); } } } From 27fd6559a3dae59ba802882c823b374addda63dc Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Tue, 19 Jul 2022 14:55:06 +0700 Subject: [PATCH 12/29] fix(web): recorder - sub-pixel alignment on scrolls --- .../gesture-recognizer/src/tools/recorder/src/recorder.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/common/web/gesture-recognizer/src/tools/recorder/src/recorder.js b/common/web/gesture-recognizer/src/tools/recorder/src/recorder.js index 185b7a5753..6be45767d1 100644 --- a/common/web/gesture-recognizer/src/tools/recorder/src/recorder.js +++ b/common/web/gesture-recognizer/src/tools/recorder/src/recorder.js @@ -86,6 +86,8 @@ window.addEventListener('load', function(ev) { } } + document.addEventListener('scroll', updateAlignment); + for(entry of safeGroup) { entry.addEventListener('change', updateConfig); } @@ -121,6 +123,10 @@ function updateConfig() { controller.layoutConfiguration = layoutSpec; + updateAlignment(); +} + +function updateAlignment() { // Check sub-pixel alignment of the main fixture element; we can't do exact repros for unit testing // if the recorded sequence has sub-pixel coordinates. This may happen otherwise due to the page's // formatting. From b2e9c830bf8b8c6aabfeaffb7b2ccd281b26a3ed Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Tue, 19 Jul 2022 15:15:30 +0700 Subject: [PATCH 13/29] feat(web): unit tests --- .../browser/cases/recordedCoordSequences.js | 5 +- .../resources/json/embeddedBorderCancel.json | 163 ++++ .../test/resources/json/hardBorderCancel.json | 28 + .../json/mobileProximityApproach.json | 278 ++++++ .../resources/json/popupLongRoamingEnd.json | 808 ++++++++++++++++++ .../resources/json/popupSafePersistence.json | 598 +++++++++++++ .../test/resources/json/popupShimCancel.json | 163 ++++ 7 files changed, 2040 insertions(+), 3 deletions(-) create mode 100644 common/web/gesture-recognizer/src/test/resources/json/embeddedBorderCancel.json create mode 100644 common/web/gesture-recognizer/src/test/resources/json/hardBorderCancel.json create mode 100644 common/web/gesture-recognizer/src/test/resources/json/mobileProximityApproach.json create mode 100644 common/web/gesture-recognizer/src/test/resources/json/popupLongRoamingEnd.json create mode 100644 common/web/gesture-recognizer/src/test/resources/json/popupSafePersistence.json create mode 100644 common/web/gesture-recognizer/src/test/resources/json/popupShimCancel.json diff --git a/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js b/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js index cd1413e91a..e7b16d0568 100644 --- a/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js +++ b/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js @@ -56,15 +56,14 @@ describe("Layer one - DOM -> InputSequence", function() { 'embeddedBorderCancel', 'hardBorderCancel', 'popupLongRoamingEnd', - 'popupShimCancel' + 'popupShimCancel', + 'popupSafePersistence' ]; for(let recordingID of testRecordings) { it(`${recordingID}.json`, function() { let testObj = __json__[recordingID]; - console.log(`expected terminator: ${testObj.set[0].terminationEvent}`) - // 'describe' has a notably different `this` reference than `it`, `before`, etc. replayAndCompare.call(this, testObj); }); diff --git a/common/web/gesture-recognizer/src/test/resources/json/embeddedBorderCancel.json b/common/web/gesture-recognizer/src/test/resources/json/embeddedBorderCancel.json new file mode 100644 index 0000000000..4081a9ba90 --- /dev/null +++ b/common/web/gesture-recognizer/src/test/resources/json/embeddedBorderCancel.json @@ -0,0 +1,163 @@ +{ + "set": [ + { + "sequence": { + "samples": [ + { + "targetX": 118, + "targetY": 79, + "t": 110727 + }, + { + "targetX": 118, + "targetY": 71, + "t": 110877 + }, + { + "targetX": 118, + "targetY": 68, + "t": 110909 + }, + { + "targetX": 118, + "targetY": 65, + "t": 110926 + }, + { + "targetX": 118, + "targetY": 62, + "t": 110943 + }, + { + "targetX": 118, + "targetY": 58, + "t": 110959 + }, + { + "targetX": 118, + "targetY": 55, + "t": 110976 + }, + { + "targetX": 118, + "targetY": 54, + "t": 110993 + }, + { + "targetX": 118, + "targetY": 52, + "t": 111009 + }, + { + "targetX": 118, + "targetY": 50, + "t": 111026 + }, + { + "targetX": 118, + "targetY": 45, + "t": 111043 + }, + { + "targetX": 118, + "targetY": 44, + "t": 111060 + }, + { + "targetX": 118, + "targetY": 42, + "t": 111076 + }, + { + "targetX": 118, + "targetY": 38, + "t": 111093 + }, + { + "targetX": 118, + "targetY": 34, + "t": 111109 + }, + { + "targetX": 118, + "targetY": 31, + "t": 111126 + }, + { + "targetX": 118, + "targetY": 28, + "t": 111143 + }, + { + "targetX": 118, + "targetY": 25, + "t": 111160 + }, + { + "targetX": 118, + "targetY": 22, + "t": 111176 + }, + { + "targetX": 118, + "targetY": 19, + "t": 111193 + }, + { + "targetX": 118, + "targetY": 17, + "t": 111209 + }, + { + "targetX": 118, + "targetY": 16, + "t": 111226 + }, + { + "targetX": 118, + "targetY": 15, + "t": 111243 + }, + { + "targetX": 118, + "targetY": 13, + "t": 111259 + }, + { + "targetX": 118, + "targetY": 10, + "t": 111276 + }, + { + "targetX": 118, + "targetY": 8, + "t": 111293 + }, + { + "targetX": 118, + "targetY": 6, + "t": 111309 + }, + { + "targetX": 118, + "targetY": 4, + "t": 111325 + }, + { + "targetX": 118, + "targetY": 2, + "t": 111343 + } + ], + "isFromTouch": true + }, + "terminationEvent": "cancel" + } + ], + "config": { + "deviceStyle": "screen4", + "roamingStyle": "bounds1", + "receiverStyle": "full", + "safeZoneStyle": "safe-loose" + } +} \ No newline at end of file diff --git a/common/web/gesture-recognizer/src/test/resources/json/hardBorderCancel.json b/common/web/gesture-recognizer/src/test/resources/json/hardBorderCancel.json new file mode 100644 index 0000000000..f4178206e3 --- /dev/null +++ b/common/web/gesture-recognizer/src/test/resources/json/hardBorderCancel.json @@ -0,0 +1,28 @@ +{ + "set": [ + { + "sequence": { + "samples": [ + { + "targetX": 231, + "targetY": 82, + "t": 131762 + }, + { + "targetX": 242, + "targetY": 81, + "t": 131976 + } + ], + "isFromTouch": true + }, + "terminationEvent": "cancel" + } + ], + "config": { + "deviceStyle": "screen4", + "roamingStyle": "bounds1", + "receiverStyle": "full", + "safeZoneStyle": "safe-loose" + } +} \ No newline at end of file diff --git a/common/web/gesture-recognizer/src/test/resources/json/mobileProximityApproach.json b/common/web/gesture-recognizer/src/test/resources/json/mobileProximityApproach.json new file mode 100644 index 0000000000..1141e8e538 --- /dev/null +++ b/common/web/gesture-recognizer/src/test/resources/json/mobileProximityApproach.json @@ -0,0 +1,278 @@ +{ + "set": [ + { + "sequence": { + "samples": [ + { + "targetX": 115, + "targetY": 79, + "t": 14808 + }, + { + "targetX": 124, + "targetY": 76, + "t": 14945 + }, + { + "targetX": 128, + "targetY": 76, + "t": 14977 + }, + { + "targetX": 132, + "targetY": 75, + "t": 14995 + }, + { + "targetX": 136, + "targetY": 75, + "t": 15012 + }, + { + "targetX": 140, + "targetY": 75, + "t": 15028 + }, + { + "targetX": 146, + "targetY": 75, + "t": 15045 + }, + { + "targetX": 150, + "targetY": 75, + "t": 15061 + }, + { + "targetX": 154, + "targetY": 75, + "t": 15078 + }, + { + "targetX": 158, + "targetY": 75, + "t": 15095 + }, + { + "targetX": 160, + "targetY": 75, + "t": 15111 + }, + { + "targetX": 162, + "targetY": 75, + "t": 15128 + }, + { + "targetX": 164, + "targetY": 75, + "t": 15144 + }, + { + "targetX": 166, + "targetY": 75, + "t": 15162 + }, + { + "targetX": 167, + "targetY": 75, + "t": 15177 + }, + { + "targetX": 170, + "targetY": 75, + "t": 15195 + }, + { + "targetX": 172, + "targetY": 75, + "t": 15211 + }, + { + "targetX": 175, + "targetY": 75, + "t": 15227 + }, + { + "targetX": 178, + "targetY": 75, + "t": 15244 + }, + { + "targetX": 180, + "targetY": 75, + "t": 15261 + }, + { + "targetX": 183, + "targetY": 75, + "t": 15278 + }, + { + "targetX": 187, + "targetY": 75, + "t": 15294 + }, + { + "targetX": 190, + "targetY": 75, + "t": 15311 + }, + { + "targetX": 192, + "targetY": 75, + "t": 15328 + }, + { + "targetX": 195, + "targetY": 75, + "t": 15344 + }, + { + "targetX": 198, + "targetY": 75, + "t": 15361 + }, + { + "targetX": 198, + "targetY": 75, + "t": 15378 + }, + { + "targetX": 199, + "targetY": 75, + "t": 15394 + }, + { + "targetX": 203, + "targetY": 75, + "t": 15411 + }, + { + "targetX": 207, + "targetY": 75, + "t": 15427 + }, + { + "targetX": 210, + "targetY": 75, + "t": 15444 + }, + { + "targetX": 212, + "targetY": 75, + "t": 15461 + }, + { + "targetX": 214, + "targetY": 75, + "t": 15477 + }, + { + "targetX": 215, + "targetY": 75, + "t": 15494 + }, + { + "targetX": 216, + "targetY": 75, + "t": 15511 + }, + { + "targetX": 218, + "targetY": 75, + "t": 15544 + }, + { + "targetX": 219, + "targetY": 75, + "t": 15561 + }, + { + "targetX": 220, + "targetY": 75, + "t": 15578 + }, + { + "targetX": 222, + "targetY": 75, + "t": 15594 + }, + { + "targetX": 222, + "targetY": 75, + "t": 15611 + }, + { + "targetX": 223, + "targetY": 75, + "t": 15628 + }, + { + "targetX": 223, + "targetY": 74, + "t": 15744 + }, + { + "targetX": 223, + "targetY": 73, + "t": 15845 + }, + { + "targetX": 224, + "targetY": 73, + "t": 15861 + }, + { + "targetX": 226, + "targetY": 73, + "t": 15894 + }, + { + "targetX": 226, + "targetY": 72, + "t": 15911 + }, + { + "targetX": 228, + "targetY": 71, + "t": 15927 + }, + { + "targetX": 230, + "targetY": 71, + "t": 15961 + }, + { + "targetX": 230, + "targetY": 71, + "t": 15978 + }, + { + "targetX": 231, + "targetY": 71, + "t": 15995 + }, + { + "targetX": 232, + "targetY": 71, + "t": 16044 + }, + { + "targetX": 232, + "targetY": 70, + "t": 16078 + } + ], + "isFromTouch": true + }, + "terminationEvent": "end" + } + ], + "config": { + "deviceStyle": "screen2", + "roamingStyle": "bounds1", + "receiverStyle": "full", + "safeZoneStyle": "safe-loose" + } +} \ No newline at end of file diff --git a/common/web/gesture-recognizer/src/test/resources/json/popupLongRoamingEnd.json b/common/web/gesture-recognizer/src/test/resources/json/popupLongRoamingEnd.json new file mode 100644 index 0000000000..6b60ef61ad --- /dev/null +++ b/common/web/gesture-recognizer/src/test/resources/json/popupLongRoamingEnd.json @@ -0,0 +1,808 @@ +{ + "set": [ + { + "sequence": { + "samples": [ + { + "targetX": 83, + "targetY": 25, + "t": 512159 + }, + { + "targetX": 83, + "targetY": 33, + "t": 512303 + }, + { + "targetX": 83, + "targetY": 34, + "t": 512335 + }, + { + "targetX": 83, + "targetY": 37, + "t": 512351 + }, + { + "targetX": 82, + "targetY": 38, + "t": 512368 + }, + { + "targetX": 82, + "targetY": 40, + "t": 512385 + }, + { + "targetX": 82, + "targetY": 41, + "t": 512401 + }, + { + "targetX": 82, + "targetY": 44, + "t": 512418 + }, + { + "targetX": 82, + "targetY": 45, + "t": 512435 + }, + { + "targetX": 82, + "targetY": 46, + "t": 512451 + }, + { + "targetX": 80, + "targetY": 46, + "t": 512468 + }, + { + "targetX": 79, + "targetY": 46, + "t": 512484 + }, + { + "targetX": 79, + "targetY": 48, + "t": 512501 + }, + { + "targetX": 79, + "targetY": 48, + "t": 512518 + }, + { + "targetX": 79, + "targetY": 49, + "t": 512535 + }, + { + "targetX": 78, + "targetY": 49, + "t": 512551 + }, + { + "targetX": 78, + "targetY": 50, + "t": 512568 + }, + { + "targetX": 78, + "targetY": 52, + "t": 512584 + }, + { + "targetX": 75, + "targetY": 52, + "t": 512601 + }, + { + "targetX": 75, + "targetY": 53, + "t": 512618 + }, + { + "targetX": 74, + "targetY": 53, + "t": 512635 + }, + { + "targetX": 74, + "targetY": 54, + "t": 512651 + }, + { + "targetX": 71, + "targetY": 54, + "t": 512668 + }, + { + "targetX": 70, + "targetY": 54, + "t": 512685 + }, + { + "targetX": 70, + "targetY": 53, + "t": 512702 + }, + { + "targetX": 68, + "targetY": 53, + "t": 512718 + }, + { + "targetX": 66, + "targetY": 54, + "t": 512735 + }, + { + "targetX": 63, + "targetY": 53, + "t": 512751 + }, + { + "targetX": 62, + "targetY": 53, + "t": 512768 + }, + { + "targetX": 62, + "targetY": 53, + "t": 512785 + }, + { + "targetX": 59, + "targetY": 53, + "t": 512802 + }, + { + "targetX": 56, + "targetY": 53, + "t": 512818 + }, + { + "targetX": 54, + "targetY": 53, + "t": 512835 + }, + { + "targetX": 51, + "targetY": 53, + "t": 512851 + }, + { + "targetX": 48, + "targetY": 53, + "t": 512868 + }, + { + "targetX": 46, + "targetY": 53, + "t": 512885 + }, + { + "targetX": 42, + "targetY": 53, + "t": 512902 + }, + { + "targetX": 39, + "targetY": 53, + "t": 512918 + }, + { + "targetX": 38, + "targetY": 53, + "t": 512935 + }, + { + "targetX": 35, + "targetY": 53, + "t": 512951 + }, + { + "targetX": 32, + "targetY": 53, + "t": 512968 + }, + { + "targetX": 30, + "targetY": 53, + "t": 512984 + }, + { + "targetX": 30, + "targetY": 53, + "t": 513002 + }, + { + "targetX": 28, + "targetY": 53, + "t": 513018 + }, + { + "targetX": 27, + "targetY": 53, + "t": 513035 + }, + { + "targetX": 24, + "targetY": 53, + "t": 513051 + }, + { + "targetX": 23, + "targetY": 53, + "t": 513068 + }, + { + "targetX": 22, + "targetY": 53, + "t": 513084 + }, + { + "targetX": 20, + "targetY": 53, + "t": 513102 + }, + { + "targetX": 19, + "targetY": 53, + "t": 513118 + }, + { + "targetX": 16, + "targetY": 53, + "t": 513135 + }, + { + "targetX": 14, + "targetY": 53, + "t": 513152 + }, + { + "targetX": 12, + "targetY": 53, + "t": 513168 + }, + { + "targetX": 10, + "targetY": 53, + "t": 513184 + }, + { + "targetX": 10, + "targetY": 53, + "t": 513201 + }, + { + "targetX": 8, + "targetY": 53, + "t": 513218 + }, + { + "targetX": 7, + "targetY": 53, + "t": 513234 + }, + { + "targetX": 6, + "targetY": 53, + "t": 513251 + }, + { + "targetX": 6, + "targetY": 53, + "t": 513285 + }, + { + "targetX": 4, + "targetY": 53, + "t": 513319 + }, + { + "targetX": 3, + "targetY": 53, + "t": 513368 + }, + { + "targetX": 2, + "targetY": 52, + "t": 513385 + }, + { + "targetX": -1, + "targetY": 52, + "t": 513401 + }, + { + "targetX": -2, + "targetY": 50, + "t": 513418 + }, + { + "targetX": -5, + "targetY": 50, + "t": 513434 + }, + { + "targetX": -6, + "targetY": 49, + "t": 513452 + }, + { + "targetX": -6, + "targetY": 48, + "t": 513468 + }, + { + "targetX": -8, + "targetY": 48, + "t": 513485 + }, + { + "targetX": -10, + "targetY": 46, + "t": 513518 + }, + { + "targetX": -10, + "targetY": 45, + "t": 513535 + }, + { + "targetX": -12, + "targetY": 44, + "t": 513551 + }, + { + "targetX": -13, + "targetY": 44, + "t": 513568 + }, + { + "targetX": -13, + "targetY": 44, + "t": 513585 + }, + { + "targetX": -13, + "targetY": 42, + "t": 513618 + }, + { + "targetX": -13, + "targetY": 41, + "t": 513635 + }, + { + "targetX": -14, + "targetY": 40, + "t": 513652 + }, + { + "targetX": -14, + "targetY": 40, + "t": 513668 + }, + { + "targetX": -14, + "targetY": 38, + "t": 513685 + }, + { + "targetX": -14, + "targetY": 37, + "t": 513701 + }, + { + "targetX": -14, + "targetY": 36, + "t": 513719 + }, + { + "targetX": -14, + "targetY": 36, + "t": 513735 + }, + { + "targetX": -14, + "targetY": 34, + "t": 513752 + }, + { + "targetX": -14, + "targetY": 32, + "t": 513768 + }, + { + "targetX": -14, + "targetY": 29, + "t": 513802 + }, + { + "targetX": -14, + "targetY": 28, + "t": 513835 + }, + { + "targetX": -14, + "targetY": 26, + "t": 513851 + }, + { + "targetX": -14, + "targetY": 24, + "t": 513868 + }, + { + "targetX": -14, + "targetY": 22, + "t": 513885 + }, + { + "targetX": -14, + "targetY": 21, + "t": 513901 + }, + { + "targetX": -14, + "targetY": 20, + "t": 513919 + }, + { + "targetX": -14, + "targetY": 20, + "t": 513934 + }, + { + "targetX": -14, + "targetY": 18, + "t": 513952 + }, + { + "targetX": -14, + "targetY": 17, + "t": 513968 + }, + { + "targetX": -14, + "targetY": 16, + "t": 513985 + }, + { + "targetX": -14, + "targetY": 14, + "t": 514001 + }, + { + "targetX": -16, + "targetY": 13, + "t": 514018 + }, + { + "targetX": -17, + "targetY": 12, + "t": 514035 + }, + { + "targetX": -17, + "targetY": 10, + "t": 514051 + }, + { + "targetX": -17, + "targetY": 8, + "t": 514068 + }, + { + "targetX": -17, + "targetY": 8, + "t": 514085 + }, + { + "targetX": -17, + "targetY": 5, + "t": 514101 + }, + { + "targetX": -17, + "targetY": 4, + "t": 514118 + }, + { + "targetX": -17, + "targetY": 2, + "t": 514135 + }, + { + "targetX": -17, + "targetY": 0, + "t": 514151 + }, + { + "targetX": -17, + "targetY": 0, + "t": 514168 + }, + { + "targetX": -17, + "targetY": -2, + "t": 514184 + }, + { + "targetX": -17, + "targetY": -3, + "t": 514201 + }, + { + "targetX": -17, + "targetY": -4, + "t": 514219 + }, + { + "targetX": -17, + "targetY": -4, + "t": 514235 + }, + { + "targetX": -17, + "targetY": -6, + "t": 514251 + }, + { + "targetX": -17, + "targetY": -7, + "t": 514268 + }, + { + "targetX": -17, + "targetY": -8, + "t": 514284 + }, + { + "targetX": -17, + "targetY": -10, + "t": 514335 + }, + { + "targetX": -17, + "targetY": -11, + "t": 514351 + }, + { + "targetX": -17, + "targetY": -12, + "t": 514368 + }, + { + "targetX": -17, + "targetY": -12, + "t": 514485 + }, + { + "targetX": -16, + "targetY": -12, + "t": 514518 + }, + { + "targetX": -13, + "targetY": -14, + "t": 514535 + }, + { + "targetX": -13, + "targetY": -15, + "t": 514551 + }, + { + "targetX": -12, + "targetY": -15, + "t": 514602 + }, + { + "targetX": -10, + "targetY": -15, + "t": 514618 + }, + { + "targetX": -6, + "targetY": -15, + "t": 514635 + }, + { + "targetX": -5, + "targetY": -15, + "t": 514652 + }, + { + "targetX": -2, + "targetY": -15, + "t": 514701 + }, + { + "targetX": -1, + "targetY": -15, + "t": 514718 + }, + { + "targetX": 0, + "targetY": -15, + "t": 514735 + }, + { + "targetX": 2, + "targetY": -15, + "t": 514751 + }, + { + "targetX": 3, + "targetY": -15, + "t": 514768 + }, + { + "targetX": 6, + "targetY": -15, + "t": 514785 + }, + { + "targetX": 7, + "targetY": -15, + "t": 514801 + }, + { + "targetX": 10, + "targetY": -15, + "t": 514818 + }, + { + "targetX": 11, + "targetY": -15, + "t": 514834 + }, + { + "targetX": 14, + "targetY": -15, + "t": 514869 + }, + { + "targetX": 15, + "targetY": -15, + "t": 514885 + }, + { + "targetX": 16, + "targetY": -15, + "t": 514918 + }, + { + "targetX": 18, + "targetY": -15, + "t": 514934 + }, + { + "targetX": 19, + "targetY": -15, + "t": 514952 + }, + { + "targetX": 20, + "targetY": -15, + "t": 514968 + }, + { + "targetX": 23, + "targetY": -15, + "t": 515018 + }, + { + "targetX": 27, + "targetY": -15, + "t": 515034 + }, + { + "targetX": 31, + "targetY": -15, + "t": 515052 + }, + { + "targetX": 35, + "targetY": -14, + "t": 515068 + }, + { + "targetX": 36, + "targetY": -15, + "t": 515085 + }, + { + "targetX": 38, + "targetY": -15, + "t": 515101 + }, + { + "targetX": 39, + "targetY": -15, + "t": 515118 + }, + { + "targetX": 40, + "targetY": -15, + "t": 515134 + }, + { + "targetX": 42, + "targetY": -15, + "t": 515168 + }, + { + "targetX": 43, + "targetY": -15, + "t": 515185 + }, + { + "targetX": 44, + "targetY": -15, + "t": 515251 + }, + { + "targetX": 46, + "targetY": -15, + "t": 515268 + }, + { + "targetX": 46, + "targetY": -15, + "t": 515285 + }, + { + "targetX": 47, + "targetY": -15, + "t": 515301 + }, + { + "targetX": 48, + "targetY": -15, + "t": 515335 + }, + { + "targetX": 50, + "targetY": -15, + "t": 515352 + }, + { + "targetX": 50, + "targetY": -15, + "t": 515368 + }, + { + "targetX": 51, + "targetY": -15, + "t": 515385 + }, + { + "targetX": 52, + "targetY": -15, + "t": 515535 + }, + { + "targetX": 51, + "targetY": -15, + "t": 515552 + } + ], + "isFromTouch": true + }, + "terminationEvent": "end" + } + ], + "config": { + "deviceStyle": "screen4", + "roamingStyle": "bounds4", + "receiverStyle": "popup", + "safeZoneStyle": "safe-default" + } +} \ No newline at end of file diff --git a/common/web/gesture-recognizer/src/test/resources/json/popupSafePersistence.json b/common/web/gesture-recognizer/src/test/resources/json/popupSafePersistence.json new file mode 100644 index 0000000000..04910afb3c --- /dev/null +++ b/common/web/gesture-recognizer/src/test/resources/json/popupSafePersistence.json @@ -0,0 +1,598 @@ +{ + "set": [ + { + "sequence": { + "samples": [ + { + "targetX": 133, + "targetY": 19, + "t": 601934 + }, + { + "targetX": 134, + "targetY": 19, + "t": 603068 + }, + { + "targetX": 135, + "targetY": 19, + "t": 603093 + }, + { + "targetX": 135, + "targetY": 19, + "t": 603109 + }, + { + "targetX": 136, + "targetY": 19, + "t": 603172 + }, + { + "targetX": 136, + "targetY": 18, + "t": 603193 + }, + { + "targetX": 137, + "targetY": 18, + "t": 603226 + }, + { + "targetX": 137, + "targetY": 18, + "t": 603278 + }, + { + "targetX": 138, + "targetY": 18, + "t": 603309 + }, + { + "targetX": 138, + "targetY": 17, + "t": 603326 + }, + { + "targetX": 139, + "targetY": 17, + "t": 603360 + }, + { + "targetX": 139, + "targetY": 16, + "t": 603376 + }, + { + "targetX": 140, + "targetY": 16, + "t": 603410 + }, + { + "targetX": 141, + "targetY": 16, + "t": 603426 + }, + { + "targetX": 141, + "targetY": 16, + "t": 603486 + }, + { + "targetX": 142, + "targetY": 16, + "t": 603509 + }, + { + "targetX": 143, + "targetY": 16, + "t": 603543 + }, + { + "targetX": 144, + "targetY": 16, + "t": 603584 + }, + { + "targetX": 145, + "targetY": 16, + "t": 603610 + }, + { + "targetX": 145, + "targetY": 16, + "t": 603646 + }, + { + "targetX": 146, + "targetY": 16, + "t": 603679 + }, + { + "targetX": 147, + "targetY": 16, + "t": 603692 + }, + { + "targetX": 148, + "targetY": 16, + "t": 603735 + }, + { + "targetX": 149, + "targetY": 16, + "t": 603759 + }, + { + "targetX": 149, + "targetY": 17, + "t": 603776 + }, + { + "targetX": 150, + "targetY": 17, + "t": 603809 + }, + { + "targetX": 151, + "targetY": 18, + "t": 603826 + }, + { + "targetX": 151, + "targetY": 19, + "t": 603873 + }, + { + "targetX": 152, + "targetY": 19, + "t": 603897 + }, + { + "targetX": 153, + "targetY": 19, + "t": 603954 + }, + { + "targetX": 153, + "targetY": 19, + "t": 603994 + }, + { + "targetX": 153, + "targetY": 20, + "t": 604076 + }, + { + "targetX": 153, + "targetY": 21, + "t": 604558 + }, + { + "targetX": 153, + "targetY": 22, + "t": 607143 + }, + { + "targetX": 153, + "targetY": 23, + "t": 607250 + }, + { + "targetX": 153, + "targetY": 23, + "t": 607276 + }, + { + "targetX": 153, + "targetY": 25, + "t": 607292 + }, + { + "targetX": 153, + "targetY": 26, + "t": 609118 + }, + { + "targetX": 153, + "targetY": 27, + "t": 609148 + }, + { + "targetX": 152, + "targetY": 27, + "t": 609176 + }, + { + "targetX": 152, + "targetY": 27, + "t": 609193 + }, + { + "targetX": 152, + "targetY": 28, + "t": 610651 + }, + { + "targetX": 152, + "targetY": 29, + "t": 610659 + }, + { + "targetX": 152, + "targetY": 30, + "t": 610676 + }, + { + "targetX": 152, + "targetY": 31, + "t": 610709 + }, + { + "targetX": 152, + "targetY": 31, + "t": 611036 + }, + { + "targetX": 152, + "targetY": 32, + "t": 611042 + }, + { + "targetX": 152, + "targetY": 33, + "t": 611059 + }, + { + "targetX": 152, + "targetY": 34, + "t": 611076 + }, + { + "targetX": 152, + "targetY": 35, + "t": 611092 + }, + { + "targetX": 152, + "targetY": 35, + "t": 611126 + }, + { + "targetX": 152, + "targetY": 36, + "t": 611142 + }, + { + "targetX": 152, + "targetY": 37, + "t": 611159 + }, + { + "targetX": 152, + "targetY": 38, + "t": 611192 + }, + { + "targetX": 151, + "targetY": 39, + "t": 611208 + }, + { + "targetX": 151, + "targetY": 40, + "t": 611226 + }, + { + "targetX": 151, + "targetY": 41, + "t": 611242 + }, + { + "targetX": 150, + "targetY": 43, + "t": 611275 + }, + { + "targetX": 150, + "targetY": 43, + "t": 611292 + }, + { + "targetX": 150, + "targetY": 45, + "t": 611309 + }, + { + "targetX": 149, + "targetY": 47, + "t": 611342 + }, + { + "targetX": 149, + "targetY": 47, + "t": 611359 + }, + { + "targetX": 149, + "targetY": 49, + "t": 611376 + }, + { + "targetX": 149, + "targetY": 51, + "t": 611409 + }, + { + "targetX": 149, + "targetY": 51, + "t": 611426 + }, + { + "targetX": 149, + "targetY": 52, + "t": 611442 + }, + { + "targetX": 149, + "targetY": 53, + "t": 611459 + }, + { + "targetX": 148, + "targetY": 55, + "t": 611475 + }, + { + "targetX": 148, + "targetY": 55, + "t": 611493 + }, + { + "targetX": 148, + "targetY": 56, + "t": 611509 + }, + { + "targetX": 147, + "targetY": 57, + "t": 611526 + }, + { + "targetX": 147, + "targetY": 59, + "t": 611543 + }, + { + "targetX": 146, + "targetY": 59, + "t": 611559 + }, + { + "targetX": 145, + "targetY": 60, + "t": 611576 + }, + { + "targetX": 145, + "targetY": 61, + "t": 611592 + }, + { + "targetX": 145, + "targetY": 63, + "t": 611609 + }, + { + "targetX": 144, + "targetY": 64, + "t": 611626 + }, + { + "targetX": 143, + "targetY": 65, + "t": 611642 + }, + { + "targetX": 141, + "targetY": 67, + "t": 611659 + }, + { + "targetX": 141, + "targetY": 68, + "t": 611676 + }, + { + "targetX": 139, + "targetY": 70, + "t": 611693 + }, + { + "targetX": 137, + "targetY": 71, + "t": 611709 + }, + { + "targetX": 136, + "targetY": 72, + "t": 611726 + }, + { + "targetX": 134, + "targetY": 74, + "t": 611743 + }, + { + "targetX": 133, + "targetY": 75, + "t": 611759 + }, + { + "targetX": 131, + "targetY": 76, + "t": 611776 + }, + { + "targetX": 130, + "targetY": 77, + "t": 611793 + }, + { + "targetX": 129, + "targetY": 78, + "t": 611809 + }, + { + "targetX": 127, + "targetY": 79, + "t": 611825 + }, + { + "targetX": 125, + "targetY": 79, + "t": 611842 + }, + { + "targetX": 123, + "targetY": 80, + "t": 611859 + }, + { + "targetX": 121, + "targetY": 81, + "t": 611876 + }, + { + "targetX": 121, + "targetY": 82, + "t": 611892 + }, + { + "targetX": 118, + "targetY": 82, + "t": 611909 + }, + { + "targetX": 117, + "targetY": 82, + "t": 611926 + }, + { + "targetX": 115, + "targetY": 82, + "t": 611942 + }, + { + "targetX": 113, + "targetY": 82, + "t": 611959 + }, + { + "targetX": 111, + "targetY": 82, + "t": 611975 + }, + { + "targetX": 109, + "targetY": 82, + "t": 611992 + }, + { + "targetX": 106, + "targetY": 81, + "t": 612009 + }, + { + "targetX": 102, + "targetY": 79, + "t": 612025 + }, + { + "targetX": 97, + "targetY": 78, + "t": 612042 + }, + { + "targetX": 92, + "targetY": 76, + "t": 612058 + }, + { + "targetX": 87, + "targetY": 75, + "t": 612076 + }, + { + "targetX": 83, + "targetY": 73, + "t": 612092 + }, + { + "targetX": 79, + "targetY": 72, + "t": 612109 + }, + { + "targetX": 75, + "targetY": 71, + "t": 612125 + }, + { + "targetX": 73, + "targetY": 71, + "t": 612142 + }, + { + "targetX": 71, + "targetY": 70, + "t": 612158 + }, + { + "targetX": 70, + "targetY": 70, + "t": 612175 + }, + { + "targetX": 69, + "targetY": 69, + "t": 612192 + }, + { + "targetX": 69, + "targetY": 68, + "t": 612244 + }, + { + "targetX": 68, + "targetY": 68, + "t": 612258 + }, + { + "targetX": 67, + "targetY": 68, + "t": 612276 + }, + { + "targetX": 66, + "targetY": 67, + "t": 612292 + }, + { + "targetX": 65, + "targetY": 67, + "t": 612309 + } + ], + "isFromTouch": false + }, + "terminationEvent": "end" + } + ], + "config": { + "deviceStyle": "screen1", + "roamingStyle": "bounds4", + "receiverStyle": "popup", + "safeZoneStyle": "safe-generous" + } +} \ No newline at end of file diff --git a/common/web/gesture-recognizer/src/test/resources/json/popupShimCancel.json b/common/web/gesture-recognizer/src/test/resources/json/popupShimCancel.json new file mode 100644 index 0000000000..764f91e9d2 --- /dev/null +++ b/common/web/gesture-recognizer/src/test/resources/json/popupShimCancel.json @@ -0,0 +1,163 @@ +{ + "set": [ + { + "sequence": { + "samples": [ + { + "targetX": 68, + "targetY": 22, + "t": 511067 + }, + { + "targetX": 67, + "targetY": 22, + "t": 511180 + }, + { + "targetX": 65, + "targetY": 24, + "t": 511211 + }, + { + "targetX": 63, + "targetY": 25, + "t": 511228 + }, + { + "targetX": 62, + "targetY": 25, + "t": 511244 + }, + { + "targetX": 58, + "targetY": 26, + "t": 511261 + }, + { + "targetX": 55, + "targetY": 26, + "t": 511277 + }, + { + "targetX": 51, + "targetY": 27, + "t": 511294 + }, + { + "targetX": 46, + "targetY": 28, + "t": 511310 + }, + { + "targetX": 42, + "targetY": 29, + "t": 511327 + }, + { + "targetX": 38, + "targetY": 29, + "t": 511345 + }, + { + "targetX": 34, + "targetY": 29, + "t": 511361 + }, + { + "targetX": 30, + "targetY": 29, + "t": 511377 + }, + { + "targetX": 25, + "targetY": 29, + "t": 511394 + }, + { + "targetX": 20, + "targetY": 29, + "t": 511411 + }, + { + "targetX": 15, + "targetY": 29, + "t": 511427 + }, + { + "targetX": 10, + "targetY": 29, + "t": 511444 + }, + { + "targetX": 5, + "targetY": 29, + "t": 511460 + }, + { + "targetX": 2, + "targetY": 28, + "t": 511478 + }, + { + "targetX": -2, + "targetY": 28, + "t": 511494 + }, + { + "targetX": -5, + "targetY": 28, + "t": 511511 + }, + { + "targetX": -7, + "targetY": 28, + "t": 511527 + }, + { + "targetX": -9, + "targetY": 28, + "t": 511544 + }, + { + "targetX": -11, + "targetY": 28, + "t": 511561 + }, + { + "targetX": -14, + "targetY": 28, + "t": 511578 + }, + { + "targetX": -18, + "targetY": 28, + "t": 511595 + }, + { + "targetX": -21, + "targetY": 28, + "t": 511611 + }, + { + "targetX": -25, + "targetY": 28, + "t": 511628 + }, + { + "targetX": -26, + "targetY": 28, + "t": 511644 + } + ], + "isFromTouch": false + }, + "terminationEvent": "cancel" + } + ], + "config": { + "deviceStyle": "screen4", + "roamingStyle": "bounds4", + "receiverStyle": "popup", + "safeZoneStyle": "safe-default" + } +} \ No newline at end of file From 42c43c48a2521a2b0187d75dc5aaab2dc1d1e55a Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Tue, 19 Jul 2022 15:31:03 +0700 Subject: [PATCH 14/29] feat(web): operational multitouch test --- .../browser/cases/recordedCoordSequences.js | 3 +- .../test/resources/json/basicMultitouch.json | 441 ++++++++++++++++++ .../src/inputSequenceSimulator.ts | 2 + 3 files changed, 445 insertions(+), 1 deletion(-) create mode 100644 common/web/gesture-recognizer/src/test/resources/json/basicMultitouch.json diff --git a/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js b/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js index e7b16d0568..922abd8516 100644 --- a/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js +++ b/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js @@ -57,7 +57,8 @@ describe("Layer one - DOM -> InputSequence", function() { 'hardBorderCancel', 'popupLongRoamingEnd', 'popupShimCancel', - 'popupSafePersistence' + 'popupSafePersistence', + 'basicMultitouch' ]; for(let recordingID of testRecordings) { diff --git a/common/web/gesture-recognizer/src/test/resources/json/basicMultitouch.json b/common/web/gesture-recognizer/src/test/resources/json/basicMultitouch.json new file mode 100644 index 0000000000..1f2f09472f --- /dev/null +++ b/common/web/gesture-recognizer/src/test/resources/json/basicMultitouch.json @@ -0,0 +1,441 @@ +{ + "set": [ + { + "sequence": { + "samples": [ + { + "targetX": 46, + "targetY": 66, + "t": 6679 + }, + { + "targetX": 46, + "targetY": 59, + "t": 6935.000000000001 + }, + { + "targetX": 46, + "targetY": 58, + "t": 6949 + }, + { + "targetX": 46, + "targetY": 57, + "t": 6983.000000000001 + }, + { + "targetX": 46, + "targetY": 56, + "t": 7016 + }, + { + "targetX": 45, + "targetY": 55, + "t": 7049 + }, + { + "targetX": 45, + "targetY": 54, + "t": 7083 + }, + { + "targetX": 44, + "targetY": 54, + "t": 7099 + }, + { + "targetX": 44, + "targetY": 53, + "t": 7115 + }, + { + "targetX": 43, + "targetY": 52, + "t": 7166 + }, + { + "targetX": 42, + "targetY": 51, + "t": 7199 + }, + { + "targetX": 42, + "targetY": 50, + "t": 7232 + }, + { + "targetX": 41, + "targetY": 49, + "t": 7249.000000000001 + }, + { + "targetX": 41, + "targetY": 48, + "t": 7265.000000000001 + }, + { + "targetX": 41, + "targetY": 47, + "t": 7282 + }, + { + "targetX": 41, + "targetY": 46, + "t": 7298 + }, + { + "targetX": 41, + "targetY": 45, + "t": 7332 + }, + { + "targetX": 41, + "targetY": 44, + "t": 7348 + }, + { + "targetX": 41, + "targetY": 43, + "t": 7399 + }, + { + "targetX": 40, + "targetY": 43, + "t": 7483.000000000001 + }, + { + "targetX": 40, + "targetY": 42, + "t": 7917 + }, + { + "targetX": 41, + "targetY": 42, + "t": 8406 + }, + { + "targetX": 42, + "targetY": 42, + "t": 8589 + }, + { + "targetX": 43, + "targetY": 42, + "t": 8765 + }, + { + "targetX": 42, + "targetY": 41, + "t": 9183 + }, + { + "targetX": 41, + "targetY": 40, + "t": 9231 + }, + { + "targetX": 40, + "targetY": 40, + "t": 9250 + }, + { + "targetX": 40, + "targetY": 39, + "t": 9265 + }, + { + "targetX": 38, + "targetY": 38, + "t": 9282 + }, + { + "targetX": 38, + "targetY": 37, + "t": 9299 + }, + { + "targetX": 37, + "targetY": 36, + "t": 9315 + }, + { + "targetX": 36, + "targetY": 35, + "t": 9331 + }, + { + "targetX": 35, + "targetY": 34, + "t": 9348 + }, + { + "targetX": 35, + "targetY": 33, + "t": 9365 + }, + { + "targetX": 34, + "targetY": 33, + "t": 9382 + }, + { + "targetX": 34, + "targetY": 32, + "t": 9414 + }, + { + "targetX": 33, + "targetY": 31, + "t": 9515 + }, + { + "targetX": 32, + "targetY": 30, + "t": 9548 + }, + { + "targetX": 32, + "targetY": 29, + "t": 9564 + }, + { + "targetX": 31, + "targetY": 29, + "t": 9581 + }, + { + "targetX": 31, + "targetY": 28, + "t": 9597 + }, + { + "targetX": 30, + "targetY": 27, + "t": 9615 + }, + { + "targetX": 30, + "targetY": 26, + "t": 9632 + }, + { + "targetX": 29, + "targetY": 25, + "t": 9648 + }, + { + "targetX": 29, + "targetY": 24, + "t": 9682 + }, + { + "targetX": 29, + "targetY": 23, + "t": 9732 + } + ], + "isFromTouch": true + }, + "terminationEvent": "end" + }, + { + "sequence": { + "samples": [ + { + "targetX": 172, + "targetY": 95, + "t": 7871 + }, + { + "targetX": 172, + "targetY": 85, + "t": 8151 + }, + { + "targetX": 172, + "targetY": 83, + "t": 8173 + }, + { + "targetX": 172, + "targetY": 80, + "t": 8183 + }, + { + "targetX": 172, + "targetY": 77, + "t": 8199 + }, + { + "targetX": 172, + "targetY": 74, + "t": 8215 + }, + { + "targetX": 172, + "targetY": 71, + "t": 8233 + }, + { + "targetX": 172, + "targetY": 68, + "t": 8249 + }, + { + "targetX": 172, + "targetY": 64, + "t": 8266 + }, + { + "targetX": 172, + "targetY": 61, + "t": 8283 + }, + { + "targetX": 172, + "targetY": 57, + "t": 8299 + }, + { + "targetX": 173, + "targetY": 54, + "t": 8315 + }, + { + "targetX": 173, + "targetY": 51, + "t": 8331 + }, + { + "targetX": 173, + "targetY": 48, + "t": 8348 + }, + { + "targetX": 173, + "targetY": 45, + "t": 8366 + }, + { + "targetX": 174, + "targetY": 42, + "t": 8383 + }, + { + "targetX": 174, + "targetY": 39, + "t": 8399 + }, + { + "targetX": 174, + "targetY": 36, + "t": 8415.000000000002 + }, + { + "targetX": 174, + "targetY": 33, + "t": 8431.000000000002 + }, + { + "targetX": 175, + "targetY": 30, + "t": 8449 + }, + { + "targetX": 175, + "targetY": 27, + "t": 8466 + }, + { + "targetX": 175, + "targetY": 24, + "t": 8483 + }, + { + "targetX": 175, + "targetY": 21, + "t": 8499 + }, + { + "targetX": 176, + "targetY": 18, + "t": 8515 + }, + { + "targetX": 176, + "targetY": 14, + "t": 8531 + }, + { + "targetX": 177, + "targetY": 10, + "t": 8548 + }, + { + "targetX": 177, + "targetY": 6, + "t": 8566 + }, + { + "targetX": 177, + "targetY": 3, + "t": 8582 + }, + { + "targetX": 178, + "targetY": 0, + "t": 8598 + }, + { + "targetX": 178, + "targetY": -3, + "t": 8615 + }, + { + "targetX": 179, + "targetY": -6, + "t": 8631 + }, + { + "targetX": 179, + "targetY": -9, + "t": 8649 + }, + { + "targetX": 180, + "targetY": -12, + "t": 8666 + }, + { + "targetX": 181, + "targetY": -15, + "t": 8682 + }, + { + "targetX": 182, + "targetY": -18, + "t": 8698 + }, + { + "targetX": 182, + "targetY": -22, + "t": 8715 + }, + { + "targetX": 183, + "targetY": -26, + "t": 8731 + } + ], + "isFromTouch": true + }, + "terminationEvent": "cancel" + } + ], + "config": { + "deviceStyle": "screen2", + "roamingStyle": "bounds1", + "receiverStyle": "full", + "safeZoneStyle": "safe-loose" + } +} \ No newline at end of file diff --git a/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts b/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts index 901b8e3469..d69a124d3c 100644 --- a/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts +++ b/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts @@ -181,6 +181,8 @@ namespace Testing { if(sequence.isFromTouch) { let otherTouches = [...sequenceTouches]; otherTouches.splice(selectedSequence, 1); + // Now that we've removed the entry for the current touchpoint, filter out any null entries. + otherTouches = otherTouches.filter((val) => !!val); sequenceTouches[selectedSequence] = this.replayTouchSample(sample, state, selectedSequence, otherTouches); if(appendEndEvent) { From d6be5f831a10b5d322a9e5cc6a764fd46a8a8a00 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Wed, 20 Jul 2022 10:00:14 +0700 Subject: [PATCH 15/29] fix(web): touchevent emulation in non-touch envs --- .../src/test/auto/browser/CI.conf.js | 22 ++---------- .../src/inputSequenceSimulator.ts | 36 ++++++++++++++++--- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/common/web/gesture-recognizer/src/test/auto/browser/CI.conf.js b/common/web/gesture-recognizer/src/test/auto/browser/CI.conf.js index d937a8406e..9438873b3a 100644 --- a/common/web/gesture-recognizer/src/test/auto/browser/CI.conf.js +++ b/common/web/gesture-recognizer/src/test/auto/browser/CI.conf.js @@ -43,7 +43,7 @@ module.exports = function(config) { var CURRENT_MAC_LAUNCHERS = { bs_firefox_mac: { browser: 'firefox', - browser_version: '62', + browser_version: '100', os: 'OS X', os_version: 'Mojave' }, @@ -61,29 +61,12 @@ module.exports = function(config) { } }; - // BrowserStack does not appear to properly support automated JavaScript testing at this time. - // See https://www.browserstack.com/list-of-browsers-and-platforms?product=js_testing for the reference. - var CURRENT_IOS_LAUNCHERS = {}; /*{ - bs_iphoneX: { - device: 'iPhone X', // Ideally, we'd use 'iPhone X', but BrowserStack's version is being problematic lately. - real_mobile: true, - os: 'ios', - os_version: '11.0' - }, - bs_ipad6: { - device: 'iPad 5th', - real_mobile: true, - os: 'ios', - os_version: '11.0' - } - };*/ - var CURRENT_WIN_LAUNCHERS = { bs_firefox_win: { os: 'Windows', os_version: '10', browser: 'firefox', - browser_version: '62.0' + browser_version: '100.0' }, bs_chrome_win: { os: 'Windows', @@ -109,7 +92,6 @@ module.exports = function(config) { * Final selection of the sets to be used for BrowserStack testing. */ var FINAL_LAUNCHER_DEFS = mergeLaunchers( CURRENT_ANDROID_LAUNCHERS, - CURRENT_IOS_LAUNCHERS, CURRENT_WIN_LAUNCHERS, CURRENT_MAC_LAUNCHERS); diff --git a/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts b/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts index d69a124d3c..b90afd538f 100644 --- a/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts +++ b/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts @@ -23,13 +23,39 @@ namespace Testing { private buildSyntheticTouchEvent(name: string, dict: TouchEventInit): TouchEvent { let config = this.controller.recognizer.config; - let fullDict = { + let baseDict = { srcElement: config.touchEventRoot, - view: window, - ...dict + view: window } as any; - return new Event('touchstart', fullDict) as TouchEvent; + if(dict.bubbles !== undefined) { + baseDict.bubbles = dict.bubbles; // is an event read-only property; can't assign it later! + delete dict.bubbles; + } + + // Produces a hacky-but-sufficient implementation of TouchList for our purposes + // in environments that avoid direct touch-support. + const arrToTouchList = (arr: Touch[]): TouchList => { + return { + //@ts-ignore + _arr: arr, // Obviously, this isn't a standard member of TouchList. + length: arr.length, + item: function(i: number) { return this._arr[i]; } + } + } + + //@ts-ignore + dict.touches = arrToTouchList(dict.touches); + //@ts-ignore + dict.changedTouches = arrToTouchList(dict.changedTouches); + + let event = new Event(name, baseDict) as TouchEvent; + + // Since touch-related properties aren't expected by some browsers' Event constructors, + // we must add them "in post" when testing in some non-touch environments. For those, + // the Event constructor will neglect to copy them from `baseDict`. + Object.assign(event, dict); + return event; } replayTouchSample(sample: JSONObject, state: string, identifier: number, otherTouches: Touch[]): Touch { @@ -59,8 +85,10 @@ namespace Testing { let buildEvent = (type: string, dict: TouchEventInit) => { if(window['TouchEvent'] !== undefined) { + // Nothing beats the real thing. return new TouchEvent(type, dict); } else { + // Effectively, an internal polyfill. But, if at ALL possible, use a real version instead! return this.buildSyntheticTouchEvent(type, dict); } } From 059adf53cc2dfcf25d8df93f04e551fec38026ee Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Wed, 20 Jul 2022 12:42:54 +0700 Subject: [PATCH 16/29] feat(web): unit test for initial touch in roaming zone --- .../src/test/auto/browser/cases/other.js | 40 +++++++++++++++++++ .../browser/cases/recordedCoordSequences.js | 1 - .../src/inputSequenceSimulator.ts | 8 +++- .../src/sequenceRecorder.ts | 4 ++ 4 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 common/web/gesture-recognizer/src/test/auto/browser/cases/other.js diff --git a/common/web/gesture-recognizer/src/test/auto/browser/cases/other.js b/common/web/gesture-recognizer/src/test/auto/browser/cases/other.js new file mode 100644 index 0000000000..f4e988e32c --- /dev/null +++ b/common/web/gesture-recognizer/src/test/auto/browser/cases/other.js @@ -0,0 +1,40 @@ +var assert = chai.assert; +var expect = chai.expect; + +describe("Layer one - DOM -> InputSequence", function() { + this.timeout(testconfig.timeouts.standard); + + before(function() { + fixture.setBase(''); + }); + + beforeEach(function(done) { + fixture.load('host-fixture.html'); + this.controller = new Testing.HostFixtureLayoutController(); + this.controller.connect().then(() => done()); + }); + + afterEach(function() { + this.controller.destroy(); + fixture.cleanup(); + fixture.cleanup(); + }); + + describe('other tests', function() { + it("starts in roaming zone are ignored", function() { + let playbackEngine = new Testing.InputSequenceSimulator(this.controller); + let recorder = new Testing.SequenceRecorder(this.controller); + let layout = new Testing.FixtureLayoutConfiguration("screen2", "bounds1", "full", "safe-loose"); + this.controller.layoutConfiguration = layout; + + playbackEngine.replayTouchSample(/*relative coord:*/ {targetX: 10, targetY: -5}, + /*state:*/ "start", + /*identifier:*/ 1, + /*otherTouches:*/ [], + /*targetElement:*/ this.controller.recognizer.config.maxRoamingBounds + ); + + assert.equal(recorder.count, 0, "Input starting in roaming area was not ignored!"); + }); + }); +}); \ No newline at end of file diff --git a/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js b/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js index 922abd8516..371b64ada3 100644 --- a/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js +++ b/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js @@ -23,7 +23,6 @@ describe("Layer one - DOM -> InputSequence", function() { describe('recorded input sequences', function() { // We rely on this function to have the same context as `it` - the test-definition function. let replayAndCompare = function(testObj) { - // Everything else can be extracted to a common test function... probably. let playbackEngine = new Testing.InputSequenceSimulator(this.controller); let result = playbackEngine.replay(testObj); diff --git a/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts b/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts index b90afd538f..bd2ece32a1 100644 --- a/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts +++ b/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts @@ -58,7 +58,11 @@ namespace Testing { return event; } - replayTouchSample(sample: JSONObject, state: string, identifier: number, otherTouches: Touch[]): Touch { + replayTouchSample(sample: JSONObject, + state: string, + identifier: number, + otherTouches: Touch[], + targetElement?: HTMLElement): Touch { let config = this.controller.recognizer.config; let event: TouchEvent; @@ -66,7 +70,7 @@ namespace Testing { let touch: Touch; let touchDict = {identifier: identifier, - target: config.targetRoot, + target: targetElement || config.targetRoot, ...mappedSample}; if(window['Touch'] !== undefined) { touch = new Touch(touchDict); diff --git a/common/web/gesture-recognizer/src/tools/unit-test-resources/src/sequenceRecorder.ts b/common/web/gesture-recognizer/src/tools/unit-test-resources/src/sequenceRecorder.ts index f1dd5d5952..c3a742bdcb 100644 --- a/common/web/gesture-recognizer/src/tools/unit-test-resources/src/sequenceRecorder.ts +++ b/common/web/gesture-recognizer/src/tools/unit-test-resources/src/sequenceRecorder.ts @@ -27,6 +27,10 @@ namespace Testing { this._attachRecognizerHooks(); } + public get count(): number { + return this.startOrder.length; + } + private _attachRecognizerHooks() { this.controller.recognizer.on(com.keyman.osk.GestureRecognizer.TRACKED_INPUT_UPDATE_EVENT_NAME, (wrappedSequence: WrappedInputSequence) => { const id = wrappedSequence.item.fullIdentifier; From b7bf82cf9b473379b9edc2550d987c275a10ff32 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Wed, 20 Jul 2022 13:27:35 +0700 Subject: [PATCH 17/29] feat(web): verifies unit-test synth events reach target --- .../src/mouseEventEngine.ts | 8 ++- .../src/test/auto/browser/base.conf.js | 2 + .../src/test/auto/browser/cases/canary.js | 65 ++++++++++++++++++- .../src/test/auto/browser/cases/other.js | 59 +++++++++++++++-- .../src/inputSequenceSimulator.ts | 9 ++- .../src/touchEventEngine.ts | 8 ++- 6 files changed, 135 insertions(+), 16 deletions(-) diff --git a/common/web/gesture-recognizer/src/mouseEventEngine.ts b/common/web/gesture-recognizer/src/mouseEventEngine.ts index cbbc8af4a8..b1e1ec377a 100644 --- a/common/web/gesture-recognizer/src/mouseEventEngine.ts +++ b/common/web/gesture-recognizer/src/mouseEventEngine.ts @@ -15,9 +15,11 @@ namespace com.keyman.osk { public constructor(config: Nonoptional) { super(config); - this._mouseStart = this.onMouseStart.bind(this); - this._mouseMove = this.onMouseMove.bind(this); - this._mouseEnd = this.onMouseEnd.bind(this); + // We use this approach, rather than .bind, because _this_ version allows hook + // insertion for unit tests via prototype manipulation. The .bind version doesn't. + this._mouseStart = (event: MouseEvent) => this.onMouseStart(event); + this._mouseMove = (event: MouseEvent) => this.onMouseMove(event); + this._mouseEnd = (event: MouseEvent) => this.onMouseEnd(event); // IDs should be unique. Unfortunately, we can't exactly control the identifier values for // the `touch` properties of TouchEvents... but they're usually non-negative, so this ought help. diff --git a/common/web/gesture-recognizer/src/test/auto/browser/base.conf.js b/common/web/gesture-recognizer/src/test/auto/browser/base.conf.js index 3cc27895c0..d6c172d498 100644 --- a/common/web/gesture-recognizer/src/test/auto/browser/base.conf.js +++ b/common/web/gesture-recognizer/src/test/auto/browser/base.conf.js @@ -41,6 +41,8 @@ module.exports = { 'build/tools/unit-test-resources.js', // The primary unit-test-resources script. {pattern: 'build/tools/unit-test-resources.js.map', watched: true, served: true, included: false}, + '../../../node_modules/sinon/pkg/sinon.js', + 'src/test/auto/browser/timeoutConfiguration.js', // Handles timeout configuration for local vs BrowserStack-based testing 'src/test/auto/browser/cases/**/*.js', // Where the tests actually reside. diff --git a/common/web/gesture-recognizer/src/test/auto/browser/cases/canary.js b/common/web/gesture-recognizer/src/test/auto/browser/cases/canary.js index d63c78de35..91ca0e4edd 100644 --- a/common/web/gesture-recognizer/src/test/auto/browser/cases/canary.js +++ b/common/web/gesture-recognizer/src/test/auto/browser/cases/canary.js @@ -42,7 +42,70 @@ describe("'Canary' checks", function() { }).finally(() => controller.destroy()); }) - after(function() { + afterEach(function() { fixture.cleanup(); }) + + describe('event simulation', function() { + beforeEach(function(done) { + fixture.load('host-fixture.html'); + this.controller = new Testing.HostFixtureLayoutController(); + this.controller.connect().then(() => done()); + }); + + afterEach(function() { + this.controller.destroy(); + fixture.cleanup(); + }); + + it("InputSequenceSimulator.replayTouchSample", function() { + let playbackEngine = new Testing.InputSequenceSimulator(this.controller); + let layout = new Testing.FixtureLayoutConfiguration("screen2", "bounds1", "full", "safe-loose"); + this.controller.layoutConfiguration = layout; + + let fireEvent = () => { + playbackEngine.replayTouchSample(/*relative coord:*/ {targetX: 10, targetY: 10}, + /*state:*/ "start", + /*identifier:*/ 1, + /*otherTouches:*/ [], + ); + } + + // Ensure that the expected handler is called. + let proto = com.keyman.osk.TouchEventEngine.prototype; + let trueHandler = proto.onTouchStart; + let fakeHandler = proto.onTouchStart = sinon.fake(); + fireEvent(); + try { + assert.isTrue(fakeHandler.called, "Unit test attempt failed: handler was not called successfully."); + } finally { + // Restore the true implementation. + proto.onTouchStart = trueHandler; + } + }); + + it("InputSequenceSimulator.replayMouseSample", function() { + let playbackEngine = new Testing.InputSequenceSimulator(this.controller); + let layout = new Testing.FixtureLayoutConfiguration("screen2", "bounds1", "full", "safe-loose"); + this.controller.layoutConfiguration = layout; + + let fireEvent = () => { + playbackEngine.replayMouseSample(/*relative coord:*/ {targetX: 15, targetY: 15}, + /*state:*/ "start" + ); + } + + // Ensure that the expected handler is called. + let proto = com.keyman.osk.MouseEventEngine.prototype; + let trueHandler = proto.onMouseStart; + let fakeHandler = proto.onMouseStart = sinon.fake(); + fireEvent(); + try { + assert.isTrue(fakeHandler.called, "Unit test attempt failed: handler was not called successfully."); + } finally { + // Restore the true implementation. + proto.onMouseStart = trueHandler; + } + }); + }); }); \ No newline at end of file diff --git a/common/web/gesture-recognizer/src/test/auto/browser/cases/other.js b/common/web/gesture-recognizer/src/test/auto/browser/cases/other.js index f4e988e32c..318473ff3e 100644 --- a/common/web/gesture-recognizer/src/test/auto/browser/cases/other.js +++ b/common/web/gesture-recognizer/src/test/auto/browser/cases/other.js @@ -17,7 +17,6 @@ describe("Layer one - DOM -> InputSequence", function() { afterEach(function() { this.controller.destroy(); fixture.cleanup(); - fixture.cleanup(); }); describe('other tests', function() { @@ -27,14 +26,60 @@ describe("Layer one - DOM -> InputSequence", function() { let layout = new Testing.FixtureLayoutConfiguration("screen2", "bounds1", "full", "safe-loose"); this.controller.layoutConfiguration = layout; - playbackEngine.replayTouchSample(/*relative coord:*/ {targetX: 10, targetY: -5}, - /*state:*/ "start", - /*identifier:*/ 1, - /*otherTouches:*/ [], - /*targetElement:*/ this.controller.recognizer.config.maxRoamingBounds - ); + let fireEvent = () => { + playbackEngine.replayTouchSample(/*relative coord:*/ {targetX: 10, targetY: -5}, + /*state:*/ "start", + /*identifier:*/ 1, + /*otherTouches:*/ [], + /*targetElement:*/ this.controller.recognizer.config.maxRoamingBounds + ); + } + // This test is invalidated if the handler itself isn't called. So... let's verify that! + // Not quite covered by the canary cases b/c of the distinct targetElement. + let proto = com.keyman.osk.TouchEventEngine.prototype; + let trueHandler = proto.onTouchStart; + let fakeHandler = proto.onTouchStart = sinon.fake(); + fireEvent(); + try { + assert.isTrue(fakeHandler.called, "Unit test attempt failed: handler was not called successfully."); + } finally { + // Restore the true implementation. + proto.onTouchStart = trueHandler; + } + + fireEvent(); assert.equal(recorder.count, 0, "Input starting in roaming area was not ignored!"); }); + + it("ignores target-external events", function() { + let playbackEngine = new Testing.InputSequenceSimulator(this.controller); + let recorder = new Testing.SequenceRecorder(this.controller); + let layout = new Testing.FixtureLayoutConfiguration("screen2", "bounds1", "full", "safe-loose"); + this.controller.layoutConfiguration = layout; + + let fireEvent = () => { + playbackEngine.replayMouseSample(/*relative coord:*/ {targetX: -5, targetY: 15}, + /*state:*/ "start", + /*targetElement:*/ document.body + ); + } + + // This test is invalidated if the handler itself isn't called. So... let's verify that! + // Not quite covered by the canary cases b/c of the distinct targetElement. + let proto = com.keyman.osk.MouseEventEngine.prototype; + let trueHandler = proto.onMouseStart; + let fakeHandler = proto.onMouseStart = sinon.fake(); + fireEvent(); + try { + assert.isTrue(fakeHandler.called, "Unit test attempt failed: handler was not called successfully."); + } finally { + // Restore the true implementation. + proto.onMouseStart = trueHandler; + } + + fireEvent(); + assert.equal(recorder.count, 0, "Input starting outside the main receiver element's hierarchy was not ignored!"); + }); }); }); \ No newline at end of file diff --git a/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts b/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts index bd2ece32a1..0b514bba92 100644 --- a/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts +++ b/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts @@ -114,7 +114,7 @@ namespace Testing { return touch; } - replayMouseSample(sample: JSONObject, state: string) { + replayMouseSample(sample: JSONObject, state: string, targetElement?: HTMLElement) { let config = this.controller.recognizer.config; let event: MouseEvent; @@ -136,7 +136,12 @@ namespace Testing { break; } - config.targetRoot.dispatchEvent(event); + targetElement = targetElement || config.targetRoot; + + // Implies that `targetElement` is the target element; .target cannot be set separately + // for mouse events, so we can't use config.mouseEventRoot. Don't worry, the + // listener will still receive the event b/c bubbling. + targetElement.dispatchEvent(event); } /** diff --git a/common/web/gesture-recognizer/src/touchEventEngine.ts b/common/web/gesture-recognizer/src/touchEventEngine.ts index a05b07ce1d..c68966e419 100644 --- a/common/web/gesture-recognizer/src/touchEventEngine.ts +++ b/common/web/gesture-recognizer/src/touchEventEngine.ts @@ -12,9 +12,11 @@ namespace com.keyman.osk { public constructor(config: Nonoptional) { super(config); - this._touchStart = this.onTouchStart.bind(this); - this._touchMove = this.onTouchMove.bind(this); - this._touchEnd = this.onTouchEnd.bind(this); + // We use this approach, rather than .bind, because _this_ version allows hook + // insertion for unit tests via prototype manipulation. The .bind version doesn't. + this._touchStart = (event: TouchEvent) => this.onTouchStart(event); + this._touchMove = (event: TouchEvent) => this.onTouchMove(event); + this._touchEnd = (event: TouchEvent) => this.onTouchEnd(event); } private get eventRoot(): HTMLElement { From e588343efe29da785db0ae1ffea70699283a351c Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Tue, 26 Jul 2022 12:24:46 +0700 Subject: [PATCH 18/29] change(web): converts to new object format, preps recorded json migration --- .../browser/cases/recordedCoordSequences.js | 29 +++++++++++--- .../src/inputSequenceSimulator.ts | 38 +++++++++++++------ 2 files changed, 51 insertions(+), 16 deletions(-) diff --git a/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js b/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js index 371b64ada3..9e4d37e4cf 100644 --- a/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js +++ b/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js @@ -26,7 +26,7 @@ describe("Layer one - DOM -> InputSequence", function() { let playbackEngine = new Testing.InputSequenceSimulator(this.controller); let result = playbackEngine.replay(testObj); - assert.equal(result.set.length, testObj.set.length); + assert.equal(result.inputs.length, testObj.set.length); // Removes the timestamp element; we know that this component won't match, as the playback engine // doesn't care about it. @@ -34,16 +34,35 @@ describe("Layer one - DOM -> InputSequence", function() { // Returns just the observed, cleaned samples for a sequence object. The recorded coordinates // should match perfectly. - let seqCleaner = (seq) => { return seq.sequence.samples.map(sampleCleaner) }; + let seqCleaner = (seq) => { + if(seq.sequence) { + // Old style + return seq.sequence.samples.map(sampleCleaner); + } else { + // New style + return seq.touchpoints[0].path.coords.map(sampleCleaner); + } + }; let cleanOriginalSet = testObj.set.map(seqCleaner); - let cleanResultSet = result .set.map(seqCleaner); + + // TEMP STATEMENT - logs the new JSON format for old-format JSON recordings. + // console.log(JSON.stringify(result, null, 2)); + let cleanResultSet = result .inputs.map(seqCleaner); expect(cleanResultSet).to.deep.equal(cleanOriginalSet); // The 'terminationEvent' property should match. Any sequence that was "canceled" should still // cancel; that's a pretty critical detail! - let terminationEventMapper = (seq) => seq.terminationEvent; - expect(result.set.map(terminationEventMapper)).to.deep.equal(testObj.set.map(terminationEventMapper)); + let terminationEventMapper = (seq) => { + if(seq.terminationEvent) { + // Old form + return seq.terminationEvent == 'cancel'; + } else { + // New form + return seq.touchpoints[0].path.wasCancelled; + } + } + expect(result.inputs.map(terminationEventMapper)).to.deep.equal(testObj.set.map(terminationEventMapper)); } // List all relevant fixtures in src/test/resources/json. diff --git a/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts b/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts index 0b514bba92..f0ff62e1f2 100644 --- a/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts +++ b/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts @@ -156,7 +156,23 @@ namespace Testing { * @returns A recording of the reproduced version of that sequence. */ replay(sequenceTestSpec: RecordedCoordSequenceSet): RecordedCoordSequenceSet { - const set = sequenceTestSpec.set; + // TEMP: ['set'] is for the old form of the recording spec. + let inputs = sequenceTestSpec.inputs; + if(inputs === undefined) { + inputs = sequenceTestSpec.inputs = sequenceTestSpec['set'].map((entry) => { + return { + touchpoints: [entry] + }; + }); + } + + // TEMP: while structures are in flux, a migration round + for(let entry of inputs) { + if(entry['sequence']) { + entry.touchpoints = [ entry['sequence'] ]; + } + } + const config = sequenceTestSpec.config; // We're going to record the test spec as we replay it, eventually returning the @@ -170,8 +186,8 @@ namespace Testing { * next sample to be 'replayed'. If the full sequence has already been * replayed, its corresponding entry will be set to Number.MAX_VALUE instead. */ - let sequenceProgress: number[] = new Array(set.length).fill(0); - let sequenceTouches: Touch[] = new Array(set.length).fill(null); + let sequenceProgress: number[] = new Array(inputs.length).fill(0); + let sequenceTouches: Touch[] = new Array(inputs.length).fill(null); while(sequenceProgress.find((number) => number != Number.MAX_VALUE) !== undefined) { // Determine the sequence that has the chronologically next-in-line sample @@ -184,26 +200,26 @@ namespace Testing { */ let selectedSequence = -1; - for(let index=0; index < set.length; index++) { - const sequence = set[index].sequence; + for(let index=0; index < inputs.length; index++) { + const touchpoint = new com.keyman.osk.TrackedPoint(index, inputs[index].touchpoints[0]); const indexInSequence = sequenceProgress[index]; if(indexInSequence == Number.MAX_VALUE) { continue; } - if(sequence.samples[indexInSequence].t < minTimestamp) { - minTimestamp = sequence.samples[indexInSequence].t; + if(touchpoint.path.coords[indexInSequence].t < minTimestamp) { + minTimestamp = touchpoint.path.coords[indexInSequence].t; selectedSequence = index; } } - const sequence = set[selectedSequence].sequence; + const touchpoint = new com.keyman.osk.TrackedPoint(selectedSequence, inputs[selectedSequence].touchpoints[0]); const indexInSequence = sequenceProgress[selectedSequence]; let state: string = "move"; let appendEndEvent = false; - if(indexInSequence + 1 >= sequence.samples.length) { + if(indexInSequence + 1 >= touchpoint.path.coords.length) { sequenceProgress[selectedSequence] = Number.MAX_VALUE; appendEndEvent = true; } else { @@ -214,8 +230,8 @@ namespace Testing { state = "start" } - const sample = sequence.samples[indexInSequence]; - if(sequence.isFromTouch) { + const sample = touchpoint.path.coords[indexInSequence]; + if(touchpoint.isFromTouch) { let otherTouches = [...sequenceTouches]; otherTouches.splice(selectedSequence, 1); // Now that we've removed the entry for the current touchpoint, filter out any null entries. From dc75dbf6e8f35d10aa4aa83fb8e546ab1bd5ce4f Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Wed, 27 Jul 2022 08:20:11 +0700 Subject: [PATCH 19/29] change(web): migration of test-recordings --- .../browser/cases/recordedCoordSequences.js | 29 +- .../test/resources/json/basicMultitouch.json | 860 ++++----- .../test/resources/json/canaryRecording.json | 306 ++-- .../resources/json/desktopRoamAndReturn.json | 556 +++--- .../resources/json/embeddedBorderCancel.json | 306 ++-- .../test/resources/json/hardBorderCancel.json | 36 +- .../json/mobileProximityApproach.json | 536 +++--- .../resources/json/mobileSafeZoneCancel.json | 56 +- .../resources/json/popupLongRoamingEnd.json | 1596 +++++++++-------- .../resources/json/popupSafePersistence.json | 1176 ++++++------ .../test/resources/json/popupShimCancel.json | 306 ++-- 11 files changed, 2914 insertions(+), 2849 deletions(-) diff --git a/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js b/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js index 9e4d37e4cf..9afda22bd4 100644 --- a/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js +++ b/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js @@ -26,7 +26,12 @@ describe("Layer one - DOM -> InputSequence", function() { let playbackEngine = new Testing.InputSequenceSimulator(this.controller); let result = playbackEngine.replay(testObj); - assert.equal(result.inputs.length, testObj.set.length); + // TEMP + if(testObj.set) { + assert.equal(result.inputs.length, testObj.set.length); + } else { + assert.equal(result.inputs.length, testObj.inputs.length); + } // Removes the timestamp element; we know that this component won't match, as the playback engine // doesn't care about it. @@ -43,10 +48,19 @@ describe("Layer one - DOM -> InputSequence", function() { return seq.touchpoints[0].path.coords.map(sampleCleaner); } }; - let cleanOriginalSet = testObj.set.map(seqCleaner); + + let cleanOriginalSet; + // TEMP + if(testObj.set) { + cleanOriginalSet = testObj.set.map(seqCleaner); + } else { + cleanOriginalSet = testObj.inputs.map(seqCleaner); + } // TEMP STATEMENT - logs the new JSON format for old-format JSON recordings. - // console.log(JSON.stringify(result, null, 2)); + if(testObj.set) { + console.log(JSON.stringify(result, null, 2)); + } let cleanResultSet = result .inputs.map(seqCleaner); expect(cleanResultSet).to.deep.equal(cleanOriginalSet); @@ -62,7 +76,13 @@ describe("Layer one - DOM -> InputSequence", function() { return seq.touchpoints[0].path.wasCancelled; } } - expect(result.inputs.map(terminationEventMapper)).to.deep.equal(testObj.set.map(terminationEventMapper)); + + // TEMP + if(testObj.set) { + expect(result.inputs.map(terminationEventMapper)).to.deep.equal(testObj.set.map(terminationEventMapper)); + } else { + expect(result.inputs.map(terminationEventMapper)).to.deep.equal(testObj.inputs.map(terminationEventMapper)); + } } // List all relevant fixtures in src/test/resources/json. @@ -84,6 +104,7 @@ describe("Layer one - DOM -> InputSequence", function() { let testObj = __json__[recordingID]; // 'describe' has a notably different `this` reference than `it`, `before`, etc. + console.log(`${recordingID}.json`); replayAndCompare.call(this, testObj); }); } diff --git a/common/web/gesture-recognizer/src/test/resources/json/basicMultitouch.json b/common/web/gesture-recognizer/src/test/resources/json/basicMultitouch.json index 1f2f09472f..ad9e84cc37 100644 --- a/common/web/gesture-recognizer/src/test/resources/json/basicMultitouch.json +++ b/common/web/gesture-recognizer/src/test/resources/json/basicMultitouch.json @@ -1,435 +1,443 @@ { - "set": [ + "inputs": [ { - "sequence": { - "samples": [ - { - "targetX": 46, - "targetY": 66, - "t": 6679 - }, - { - "targetX": 46, - "targetY": 59, - "t": 6935.000000000001 - }, - { - "targetX": 46, - "targetY": 58, - "t": 6949 - }, - { - "targetX": 46, - "targetY": 57, - "t": 6983.000000000001 - }, - { - "targetX": 46, - "targetY": 56, - "t": 7016 - }, - { - "targetX": 45, - "targetY": 55, - "t": 7049 - }, - { - "targetX": 45, - "targetY": 54, - "t": 7083 - }, - { - "targetX": 44, - "targetY": 54, - "t": 7099 - }, - { - "targetX": 44, - "targetY": 53, - "t": 7115 - }, - { - "targetX": 43, - "targetY": 52, - "t": 7166 - }, - { - "targetX": 42, - "targetY": 51, - "t": 7199 - }, - { - "targetX": 42, - "targetY": 50, - "t": 7232 - }, - { - "targetX": 41, - "targetY": 49, - "t": 7249.000000000001 - }, - { - "targetX": 41, - "targetY": 48, - "t": 7265.000000000001 - }, - { - "targetX": 41, - "targetY": 47, - "t": 7282 - }, - { - "targetX": 41, - "targetY": 46, - "t": 7298 - }, - { - "targetX": 41, - "targetY": 45, - "t": 7332 - }, - { - "targetX": 41, - "targetY": 44, - "t": 7348 - }, - { - "targetX": 41, - "targetY": 43, - "t": 7399 - }, - { - "targetX": 40, - "targetY": 43, - "t": 7483.000000000001 - }, - { - "targetX": 40, - "targetY": 42, - "t": 7917 - }, - { - "targetX": 41, - "targetY": 42, - "t": 8406 - }, - { - "targetX": 42, - "targetY": 42, - "t": 8589 - }, - { - "targetX": 43, - "targetY": 42, - "t": 8765 - }, - { - "targetX": 42, - "targetY": 41, - "t": 9183 - }, - { - "targetX": 41, - "targetY": 40, - "t": 9231 - }, - { - "targetX": 40, - "targetY": 40, - "t": 9250 - }, - { - "targetX": 40, - "targetY": 39, - "t": 9265 - }, - { - "targetX": 38, - "targetY": 38, - "t": 9282 - }, - { - "targetX": 38, - "targetY": 37, - "t": 9299 - }, - { - "targetX": 37, - "targetY": 36, - "t": 9315 - }, - { - "targetX": 36, - "targetY": 35, - "t": 9331 - }, - { - "targetX": 35, - "targetY": 34, - "t": 9348 - }, - { - "targetX": 35, - "targetY": 33, - "t": 9365 - }, - { - "targetX": 34, - "targetY": 33, - "t": 9382 - }, - { - "targetX": 34, - "targetY": 32, - "t": 9414 - }, - { - "targetX": 33, - "targetY": 31, - "t": 9515 - }, - { - "targetX": 32, - "targetY": 30, - "t": 9548 - }, - { - "targetX": 32, - "targetY": 29, - "t": 9564 - }, - { - "targetX": 31, - "targetY": 29, - "t": 9581 - }, - { - "targetX": 31, - "targetY": 28, - "t": 9597 - }, - { - "targetX": 30, - "targetY": 27, - "t": 9615 - }, - { - "targetX": 30, - "targetY": 26, - "t": 9632 - }, - { - "targetX": 29, - "targetY": 25, - "t": 9648 - }, - { - "targetX": 29, - "targetY": 24, - "t": 9682 - }, - { - "targetX": 29, - "targetY": 23, - "t": 9732 + "touchpoints": [ + { + "isFromTouch": true, + "path": { + "coords": [ + { + "targetX": 46, + "targetY": 66, + "t": 6679 + }, + { + "targetX": 46, + "targetY": 59, + "t": 6935.000000000001 + }, + { + "targetX": 46, + "targetY": 58, + "t": 6949 + }, + { + "targetX": 46, + "targetY": 57, + "t": 6983.000000000001 + }, + { + "targetX": 46, + "targetY": 56, + "t": 7016 + }, + { + "targetX": 45, + "targetY": 55, + "t": 7049 + }, + { + "targetX": 45, + "targetY": 54, + "t": 7083 + }, + { + "targetX": 44, + "targetY": 54, + "t": 7099 + }, + { + "targetX": 44, + "targetY": 53, + "t": 7115 + }, + { + "targetX": 43, + "targetY": 52, + "t": 7166 + }, + { + "targetX": 42, + "targetY": 51, + "t": 7199 + }, + { + "targetX": 42, + "targetY": 50, + "t": 7232 + }, + { + "targetX": 41, + "targetY": 49, + "t": 7249.000000000001 + }, + { + "targetX": 41, + "targetY": 48, + "t": 7265.000000000001 + }, + { + "targetX": 41, + "targetY": 47, + "t": 7282 + }, + { + "targetX": 41, + "targetY": 46, + "t": 7298 + }, + { + "targetX": 41, + "targetY": 45, + "t": 7332 + }, + { + "targetX": 41, + "targetY": 44, + "t": 7348 + }, + { + "targetX": 41, + "targetY": 43, + "t": 7399 + }, + { + "targetX": 40, + "targetY": 43, + "t": 7483.000000000001 + }, + { + "targetX": 40, + "targetY": 42, + "t": 7917 + }, + { + "targetX": 41, + "targetY": 42, + "t": 8406 + }, + { + "targetX": 42, + "targetY": 42, + "t": 8589 + }, + { + "targetX": 43, + "targetY": 42, + "t": 8765 + }, + { + "targetX": 42, + "targetY": 41, + "t": 9183 + }, + { + "targetX": 41, + "targetY": 40, + "t": 9231 + }, + { + "targetX": 40, + "targetY": 40, + "t": 9250 + }, + { + "targetX": 40, + "targetY": 39, + "t": 9265 + }, + { + "targetX": 38, + "targetY": 38, + "t": 9282 + }, + { + "targetX": 38, + "targetY": 37, + "t": 9299 + }, + { + "targetX": 37, + "targetY": 36, + "t": 9315 + }, + { + "targetX": 36, + "targetY": 35, + "t": 9331 + }, + { + "targetX": 35, + "targetY": 34, + "t": 9348 + }, + { + "targetX": 35, + "targetY": 33, + "t": 9365 + }, + { + "targetX": 34, + "targetY": 33, + "t": 9382 + }, + { + "targetX": 34, + "targetY": 32, + "t": 9414 + }, + { + "targetX": 33, + "targetY": 31, + "t": 9515 + }, + { + "targetX": 32, + "targetY": 30, + "t": 9548 + }, + { + "targetX": 32, + "targetY": 29, + "t": 9564 + }, + { + "targetX": 31, + "targetY": 29, + "t": 9581 + }, + { + "targetX": 31, + "targetY": 28, + "t": 9597 + }, + { + "targetX": 30, + "targetY": 27, + "t": 9615 + }, + { + "targetX": 30, + "targetY": 26, + "t": 9632 + }, + { + "targetX": 29, + "targetY": 25, + "t": 9648 + }, + { + "targetX": 29, + "targetY": 24, + "t": 9682 + }, + { + "targetX": 29, + "targetY": 23, + "t": 9732 + } + ], + "wasCancelled": false } - ], - "isFromTouch": true - }, - "terminationEvent": "end" + } + ] }, { - "sequence": { - "samples": [ - { - "targetX": 172, - "targetY": 95, - "t": 7871 - }, - { - "targetX": 172, - "targetY": 85, - "t": 8151 - }, - { - "targetX": 172, - "targetY": 83, - "t": 8173 - }, - { - "targetX": 172, - "targetY": 80, - "t": 8183 - }, - { - "targetX": 172, - "targetY": 77, - "t": 8199 - }, - { - "targetX": 172, - "targetY": 74, - "t": 8215 - }, - { - "targetX": 172, - "targetY": 71, - "t": 8233 - }, - { - "targetX": 172, - "targetY": 68, - "t": 8249 - }, - { - "targetX": 172, - "targetY": 64, - "t": 8266 - }, - { - "targetX": 172, - "targetY": 61, - "t": 8283 - }, - { - "targetX": 172, - "targetY": 57, - "t": 8299 - }, - { - "targetX": 173, - "targetY": 54, - "t": 8315 - }, - { - "targetX": 173, - "targetY": 51, - "t": 8331 - }, - { - "targetX": 173, - "targetY": 48, - "t": 8348 - }, - { - "targetX": 173, - "targetY": 45, - "t": 8366 - }, - { - "targetX": 174, - "targetY": 42, - "t": 8383 - }, - { - "targetX": 174, - "targetY": 39, - "t": 8399 - }, - { - "targetX": 174, - "targetY": 36, - "t": 8415.000000000002 - }, - { - "targetX": 174, - "targetY": 33, - "t": 8431.000000000002 - }, - { - "targetX": 175, - "targetY": 30, - "t": 8449 - }, - { - "targetX": 175, - "targetY": 27, - "t": 8466 - }, - { - "targetX": 175, - "targetY": 24, - "t": 8483 - }, - { - "targetX": 175, - "targetY": 21, - "t": 8499 - }, - { - "targetX": 176, - "targetY": 18, - "t": 8515 - }, - { - "targetX": 176, - "targetY": 14, - "t": 8531 - }, - { - "targetX": 177, - "targetY": 10, - "t": 8548 - }, - { - "targetX": 177, - "targetY": 6, - "t": 8566 - }, - { - "targetX": 177, - "targetY": 3, - "t": 8582 - }, - { - "targetX": 178, - "targetY": 0, - "t": 8598 - }, - { - "targetX": 178, - "targetY": -3, - "t": 8615 - }, - { - "targetX": 179, - "targetY": -6, - "t": 8631 - }, - { - "targetX": 179, - "targetY": -9, - "t": 8649 - }, - { - "targetX": 180, - "targetY": -12, - "t": 8666 - }, - { - "targetX": 181, - "targetY": -15, - "t": 8682 - }, - { - "targetX": 182, - "targetY": -18, - "t": 8698 - }, - { - "targetX": 182, - "targetY": -22, - "t": 8715 - }, - { - "targetX": 183, - "targetY": -26, - "t": 8731 + "touchpoints": [ + { + "isFromTouch": true, + "path": { + "coords": [ + { + "targetX": 172, + "targetY": 95, + "t": 7871 + }, + { + "targetX": 172, + "targetY": 85, + "t": 8151 + }, + { + "targetX": 172, + "targetY": 83, + "t": 8173 + }, + { + "targetX": 172, + "targetY": 80, + "t": 8183 + }, + { + "targetX": 172, + "targetY": 77, + "t": 8199 + }, + { + "targetX": 172, + "targetY": 74, + "t": 8215 + }, + { + "targetX": 172, + "targetY": 71, + "t": 8233 + }, + { + "targetX": 172, + "targetY": 68, + "t": 8249 + }, + { + "targetX": 172, + "targetY": 64, + "t": 8266 + }, + { + "targetX": 172, + "targetY": 61, + "t": 8283 + }, + { + "targetX": 172, + "targetY": 57, + "t": 8299 + }, + { + "targetX": 173, + "targetY": 54, + "t": 8315 + }, + { + "targetX": 173, + "targetY": 51, + "t": 8331 + }, + { + "targetX": 173, + "targetY": 48, + "t": 8348 + }, + { + "targetX": 173, + "targetY": 45, + "t": 8366 + }, + { + "targetX": 174, + "targetY": 42, + "t": 8383 + }, + { + "targetX": 174, + "targetY": 39, + "t": 8399 + }, + { + "targetX": 174, + "targetY": 36, + "t": 8415.000000000002 + }, + { + "targetX": 174, + "targetY": 33, + "t": 8431.000000000002 + }, + { + "targetX": 175, + "targetY": 30, + "t": 8449 + }, + { + "targetX": 175, + "targetY": 27, + "t": 8466 + }, + { + "targetX": 175, + "targetY": 24, + "t": 8483 + }, + { + "targetX": 175, + "targetY": 21, + "t": 8499 + }, + { + "targetX": 176, + "targetY": 18, + "t": 8515 + }, + { + "targetX": 176, + "targetY": 14, + "t": 8531 + }, + { + "targetX": 177, + "targetY": 10, + "t": 8548 + }, + { + "targetX": 177, + "targetY": 6, + "t": 8566 + }, + { + "targetX": 177, + "targetY": 3, + "t": 8582 + }, + { + "targetX": 178, + "targetY": 0, + "t": 8598 + }, + { + "targetX": 178, + "targetY": -3, + "t": 8615 + }, + { + "targetX": 179, + "targetY": -6, + "t": 8631 + }, + { + "targetX": 179, + "targetY": -9, + "t": 8649 + }, + { + "targetX": 180, + "targetY": -12, + "t": 8666 + }, + { + "targetX": 181, + "targetY": -15, + "t": 8682 + }, + { + "targetX": 182, + "targetY": -18, + "t": 8698 + }, + { + "targetX": 182, + "targetY": -22, + "t": 8715 + }, + { + "targetX": 183, + "targetY": -26, + "t": 8731 + } + ], + "wasCancelled": true } - ], - "isFromTouch": true - }, - "terminationEvent": "cancel" + } + ] } ], "config": { diff --git a/common/web/gesture-recognizer/src/test/resources/json/canaryRecording.json b/common/web/gesture-recognizer/src/test/resources/json/canaryRecording.json index 5eecbf1e20..8d0cfd4fb3 100644 --- a/common/web/gesture-recognizer/src/test/resources/json/canaryRecording.json +++ b/common/web/gesture-recognizer/src/test/resources/json/canaryRecording.json @@ -1,157 +1,161 @@ { - "set": [ + "inputs": [ { - "sequence": { - "samples": [ - { - "targetX": 231, - "targetY": 32, - "t": 388913 - }, - { - "targetX": 230, - "targetY": 32, - "t": 389101 - }, - { - "targetX": 229, - "targetY": 32, - "t": 389131 - }, - { - "targetX": 228, - "targetY": 32, - "t": 389164 - }, - { - "targetX": 228, - "targetY": 33, - "t": 389173 - }, - { - "targetX": 227, - "targetY": 33, - "t": 389206 - }, - { - "targetX": 226, - "targetY": 34, - "t": 389240 - }, - { - "targetX": 225, - "targetY": 34, - "t": 389275 - }, - { - "targetX": 225, - "targetY": 35, - "t": 389306 - }, - { - "targetX": 225, - "targetY": 36, - "t": 389390 - }, - { - "targetX": 225, - "targetY": 37, - "t": 389479 - }, - { - "targetX": 225, - "targetY": 38, - "t": 389609 - }, - { - "targetX": 226, - "targetY": 38, - "t": 389851 - }, - { - "targetX": 226, - "targetY": 39, - "t": 389856 - }, - { - "targetX": 227, - "targetY": 39, - "t": 389872 - }, - { - "targetX": 228, - "targetY": 39, - "t": 389906 - }, - { - "targetX": 228, - "targetY": 40, - "t": 389923 - }, - { - "targetX": 229, - "targetY": 40, - "t": 389957 - }, - { - "targetX": 230, - "targetY": 40, - "t": 389993 - }, - { - "targetX": 231, - "targetY": 40, - "t": 390025 - }, - { - "targetX": 232, - "targetY": 40, - "t": 390056 - }, - { - "targetX": 233, - "targetY": 39, - "t": 390098 - }, - { - "targetX": 234, - "targetY": 39, - "t": 390164 - }, - { - "targetX": 234, - "targetY": 38, - "t": 390213 - }, - { - "targetX": 235, - "targetY": 38, - "t": 390223 - }, - { - "targetX": 236, - "targetY": 38, - "t": 390257 - }, - { - "targetX": 236, - "targetY": 37, - "t": 390325 - }, - { - "targetX": 237, - "targetY": 37, - "t": 390654 - }, - { - "targetX": 237, - "targetY": 36, - "t": 390776 + "touchpoints": [ + { + "isFromTouch": false, + "path": { + "coords": [ + { + "targetX": 231, + "targetY": 32, + "t": 388913 + }, + { + "targetX": 230, + "targetY": 32, + "t": 389101 + }, + { + "targetX": 229, + "targetY": 32, + "t": 389131 + }, + { + "targetX": 228, + "targetY": 32, + "t": 389164 + }, + { + "targetX": 228, + "targetY": 33, + "t": 389173 + }, + { + "targetX": 227, + "targetY": 33, + "t": 389206 + }, + { + "targetX": 226, + "targetY": 34, + "t": 389240 + }, + { + "targetX": 225, + "targetY": 34, + "t": 389275 + }, + { + "targetX": 225, + "targetY": 35, + "t": 389306 + }, + { + "targetX": 225, + "targetY": 36, + "t": 389390 + }, + { + "targetX": 225, + "targetY": 37, + "t": 389479 + }, + { + "targetX": 225, + "targetY": 38, + "t": 389609 + }, + { + "targetX": 226, + "targetY": 38, + "t": 389851 + }, + { + "targetX": 226, + "targetY": 39, + "t": 389856 + }, + { + "targetX": 227, + "targetY": 39, + "t": 389872 + }, + { + "targetX": 228, + "targetY": 39, + "t": 389906 + }, + { + "targetX": 228, + "targetY": 40, + "t": 389923 + }, + { + "targetX": 229, + "targetY": 40, + "t": 389957 + }, + { + "targetX": 230, + "targetY": 40, + "t": 389993 + }, + { + "targetX": 231, + "targetY": 40, + "t": 390025 + }, + { + "targetX": 232, + "targetY": 40, + "t": 390056 + }, + { + "targetX": 233, + "targetY": 39, + "t": 390098 + }, + { + "targetX": 234, + "targetY": 39, + "t": 390164 + }, + { + "targetX": 234, + "targetY": 38, + "t": 390213 + }, + { + "targetX": 235, + "targetY": 38, + "t": 390223 + }, + { + "targetX": 236, + "targetY": 38, + "t": 390257 + }, + { + "targetX": 236, + "targetY": 37, + "t": 390325 + }, + { + "targetX": 237, + "targetY": 37, + "t": 390654 + }, + { + "targetX": 237, + "targetY": 36, + "t": 390776 + } + ], + "wasCancelled": false } - ], - "isFromTouch": false - }, - "terminationEvent": "end" + } + ] } ], "config": { diff --git a/common/web/gesture-recognizer/src/test/resources/json/desktopRoamAndReturn.json b/common/web/gesture-recognizer/src/test/resources/json/desktopRoamAndReturn.json index 336a13c1d1..3684c797e8 100644 --- a/common/web/gesture-recognizer/src/test/resources/json/desktopRoamAndReturn.json +++ b/common/web/gesture-recognizer/src/test/resources/json/desktopRoamAndReturn.json @@ -1,282 +1,286 @@ { - "set": [ + "inputs": [ { - "sequence": { - "samples": [ - { - "targetX": 119, - "targetY": 27, - "t": 670652.6999999881 - }, - { - "targetX": 119, - "targetY": 26, - "t": 670770.8999999762 - }, - { - "targetX": 119, - "targetY": 25, - "t": 670783.5 - }, - { - "targetX": 119, - "targetY": 24, - "t": 670792.5 - }, - { - "targetX": 119, - "targetY": 24, - "t": 670815.2999999523 - }, - { - "targetX": 119, - "targetY": 23, - "t": 670825.8999999762 - }, - { - "targetX": 119, - "targetY": 22, - "t": 670848.1999999881 - }, - { - "targetX": 119, - "targetY": 21, - "t": 670859.0999999642 - }, - { - "targetX": 119, - "targetY": 20, - "t": 670875.8999999762 - }, - { - "targetX": 119, - "targetY": 20, - "t": 670892.6999999881 - }, - { - "targetX": 119, - "targetY": 19, - "t": 670909.1999999881 - }, - { - "targetX": 119, - "targetY": 18, - "t": 670926.5 - }, - { - "targetX": 119, - "targetY": 16, - "t": 670942.5 - }, - { - "targetX": 118, - "targetY": 15, - "t": 670959.1999999881 - }, - { - "targetX": 118, - "targetY": 12, - "t": 670975.7999999523 - }, - { - "targetX": 118, - "targetY": 12, - "t": 670993.1999999881 - }, - { - "targetX": 118, - "targetY": 10, - "t": 671009.1999999881 - }, - { - "targetX": 118, - "targetY": 8, - "t": 671025.8999999762 - }, - { - "targetX": 118, - "targetY": 6, - "t": 671042.5999999642 - }, - { - "targetX": 118, - "targetY": 5, - "t": 671059.2999999523 - }, - { - "targetX": 118, - "targetY": 4, - "t": 671075.8999999762 - }, - { - "targetX": 118, - "targetY": 2, - "t": 671092.5999999642 - }, - { - "targetX": 118, - "targetY": 0, - "t": 671109.1999999881 - }, - { - "targetX": 118, - "targetY": 0, - "t": 671125.8999999762 - }, - { - "targetX": 118, - "targetY": -2, - "t": 671142.5999999642 - }, - { - "targetX": 118, - "targetY": -3, - "t": 671159.0999999642 - }, - { - "targetX": 118, - "targetY": -4, - "t": 671175.7999999523 - }, - { - "targetX": 118, - "targetY": -4, - "t": 671192.5 - }, - { - "targetX": 118, - "targetY": -5, - "t": 671244.3999999762 - }, - { - "targetX": 118, - "targetY": -6, - "t": 671260 - }, - { - "targetX": 119, - "targetY": -7, - "t": 671275.8999999762 - }, - { - "targetX": 120, - "targetY": -7, - "t": 671326.1999999881 - }, - { - "targetX": 120, - "targetY": -8, - "t": 671342.5 - }, - { - "targetX": 120, - "targetY": -8, - "t": 671359.8999999762 - }, - { - "targetX": 120, - "targetY": -8, - "t": 671376 - }, - { - "targetX": 121, - "targetY": -8, - "t": 671395.1999999881 - }, - { - "targetX": 121, - "targetY": -9, - "t": 671420.5999999642 - }, - { - "targetX": 122, - "targetY": -9, - "t": 671460 - }, - { - "targetX": 123, - "targetY": -9, - "t": 671475.8999999762 - }, - { - "targetX": 124, - "targetY": -9, - "t": 671525.8999999762 - }, - { - "targetX": 125, - "targetY": -8, - "t": 671542.5999999642 - }, - { - "targetX": 126, - "targetY": -8, - "t": 671559.2999999523 - }, - { - "targetX": 127, - "targetY": -6, - "t": 671575.7999999523 - }, - { - "targetX": 128, - "targetY": -4, - "t": 671592.5999999642 - }, - { - "targetX": 128, - "targetY": -3, - "t": 671609.2999999523 - }, - { - "targetX": 129, - "targetY": -1, - "t": 671625.8999999762 - }, - { - "targetX": 129, - "targetY": 0, - "t": 671642.5999999642 - }, - { - "targetX": 129, - "targetY": 0, - "t": 671659.1999999881 - }, - { - "targetX": 129, - "targetY": 1, - "t": 671675.8999999762 - }, - { - "targetX": 130, - "targetY": 1, - "t": 671692.8999999762 - }, - { - "targetX": 130, - "targetY": 3, - "t": 671709.2999999523 - }, - { - "targetX": 130, - "targetY": 4, - "t": 671734.8999999762 - }, - { - "targetX": 130, - "targetY": 4, - "t": 671749.5 - }, - { - "targetX": 130, - "targetY": 5, - "t": 671793 + "touchpoints": [ + { + "isFromTouch": false, + "path": { + "coords": [ + { + "targetX": 119, + "targetY": 27, + "t": 670652.6999999881 + }, + { + "targetX": 119, + "targetY": 26, + "t": 670770.8999999762 + }, + { + "targetX": 119, + "targetY": 25, + "t": 670783.5 + }, + { + "targetX": 119, + "targetY": 24, + "t": 670792.5 + }, + { + "targetX": 119, + "targetY": 24, + "t": 670815.2999999523 + }, + { + "targetX": 119, + "targetY": 23, + "t": 670825.8999999762 + }, + { + "targetX": 119, + "targetY": 22, + "t": 670848.1999999881 + }, + { + "targetX": 119, + "targetY": 21, + "t": 670859.0999999642 + }, + { + "targetX": 119, + "targetY": 20, + "t": 670875.8999999762 + }, + { + "targetX": 119, + "targetY": 20, + "t": 670892.6999999881 + }, + { + "targetX": 119, + "targetY": 19, + "t": 670909.1999999881 + }, + { + "targetX": 119, + "targetY": 18, + "t": 670926.5 + }, + { + "targetX": 119, + "targetY": 16, + "t": 670942.5 + }, + { + "targetX": 118, + "targetY": 15, + "t": 670959.1999999881 + }, + { + "targetX": 118, + "targetY": 12, + "t": 670975.7999999523 + }, + { + "targetX": 118, + "targetY": 12, + "t": 670993.1999999881 + }, + { + "targetX": 118, + "targetY": 10, + "t": 671009.1999999881 + }, + { + "targetX": 118, + "targetY": 8, + "t": 671025.8999999762 + }, + { + "targetX": 118, + "targetY": 6, + "t": 671042.5999999642 + }, + { + "targetX": 118, + "targetY": 5, + "t": 671059.2999999523 + }, + { + "targetX": 118, + "targetY": 4, + "t": 671075.8999999762 + }, + { + "targetX": 118, + "targetY": 2, + "t": 671092.5999999642 + }, + { + "targetX": 118, + "targetY": 0, + "t": 671109.1999999881 + }, + { + "targetX": 118, + "targetY": 0, + "t": 671125.8999999762 + }, + { + "targetX": 118, + "targetY": -2, + "t": 671142.5999999642 + }, + { + "targetX": 118, + "targetY": -3, + "t": 671159.0999999642 + }, + { + "targetX": 118, + "targetY": -4, + "t": 671175.7999999523 + }, + { + "targetX": 118, + "targetY": -4, + "t": 671192.5 + }, + { + "targetX": 118, + "targetY": -5, + "t": 671244.3999999762 + }, + { + "targetX": 118, + "targetY": -6, + "t": 671260 + }, + { + "targetX": 119, + "targetY": -7, + "t": 671275.8999999762 + }, + { + "targetX": 120, + "targetY": -7, + "t": 671326.1999999881 + }, + { + "targetX": 120, + "targetY": -8, + "t": 671342.5 + }, + { + "targetX": 120, + "targetY": -8, + "t": 671359.8999999762 + }, + { + "targetX": 120, + "targetY": -8, + "t": 671376 + }, + { + "targetX": 121, + "targetY": -8, + "t": 671395.1999999881 + }, + { + "targetX": 121, + "targetY": -9, + "t": 671420.5999999642 + }, + { + "targetX": 122, + "targetY": -9, + "t": 671460 + }, + { + "targetX": 123, + "targetY": -9, + "t": 671475.8999999762 + }, + { + "targetX": 124, + "targetY": -9, + "t": 671525.8999999762 + }, + { + "targetX": 125, + "targetY": -8, + "t": 671542.5999999642 + }, + { + "targetX": 126, + "targetY": -8, + "t": 671559.2999999523 + }, + { + "targetX": 127, + "targetY": -6, + "t": 671575.7999999523 + }, + { + "targetX": 128, + "targetY": -4, + "t": 671592.5999999642 + }, + { + "targetX": 128, + "targetY": -3, + "t": 671609.2999999523 + }, + { + "targetX": 129, + "targetY": -1, + "t": 671625.8999999762 + }, + { + "targetX": 129, + "targetY": 0, + "t": 671642.5999999642 + }, + { + "targetX": 129, + "targetY": 0, + "t": 671659.1999999881 + }, + { + "targetX": 129, + "targetY": 1, + "t": 671675.8999999762 + }, + { + "targetX": 130, + "targetY": 1, + "t": 671692.8999999762 + }, + { + "targetX": 130, + "targetY": 3, + "t": 671709.2999999523 + }, + { + "targetX": 130, + "targetY": 4, + "t": 671734.8999999762 + }, + { + "targetX": 130, + "targetY": 4, + "t": 671749.5 + }, + { + "targetX": 130, + "targetY": 5, + "t": 671793 + } + ], + "wasCancelled": false } - ], - "isFromTouch": false - }, - "terminationEvent": "end" + } + ] } ], "config": { diff --git a/common/web/gesture-recognizer/src/test/resources/json/embeddedBorderCancel.json b/common/web/gesture-recognizer/src/test/resources/json/embeddedBorderCancel.json index 4081a9ba90..2da4e5114e 100644 --- a/common/web/gesture-recognizer/src/test/resources/json/embeddedBorderCancel.json +++ b/common/web/gesture-recognizer/src/test/resources/json/embeddedBorderCancel.json @@ -1,157 +1,161 @@ { - "set": [ + "inputs": [ { - "sequence": { - "samples": [ - { - "targetX": 118, - "targetY": 79, - "t": 110727 - }, - { - "targetX": 118, - "targetY": 71, - "t": 110877 - }, - { - "targetX": 118, - "targetY": 68, - "t": 110909 - }, - { - "targetX": 118, - "targetY": 65, - "t": 110926 - }, - { - "targetX": 118, - "targetY": 62, - "t": 110943 - }, - { - "targetX": 118, - "targetY": 58, - "t": 110959 - }, - { - "targetX": 118, - "targetY": 55, - "t": 110976 - }, - { - "targetX": 118, - "targetY": 54, - "t": 110993 - }, - { - "targetX": 118, - "targetY": 52, - "t": 111009 - }, - { - "targetX": 118, - "targetY": 50, - "t": 111026 - }, - { - "targetX": 118, - "targetY": 45, - "t": 111043 - }, - { - "targetX": 118, - "targetY": 44, - "t": 111060 - }, - { - "targetX": 118, - "targetY": 42, - "t": 111076 - }, - { - "targetX": 118, - "targetY": 38, - "t": 111093 - }, - { - "targetX": 118, - "targetY": 34, - "t": 111109 - }, - { - "targetX": 118, - "targetY": 31, - "t": 111126 - }, - { - "targetX": 118, - "targetY": 28, - "t": 111143 - }, - { - "targetX": 118, - "targetY": 25, - "t": 111160 - }, - { - "targetX": 118, - "targetY": 22, - "t": 111176 - }, - { - "targetX": 118, - "targetY": 19, - "t": 111193 - }, - { - "targetX": 118, - "targetY": 17, - "t": 111209 - }, - { - "targetX": 118, - "targetY": 16, - "t": 111226 - }, - { - "targetX": 118, - "targetY": 15, - "t": 111243 - }, - { - "targetX": 118, - "targetY": 13, - "t": 111259 - }, - { - "targetX": 118, - "targetY": 10, - "t": 111276 - }, - { - "targetX": 118, - "targetY": 8, - "t": 111293 - }, - { - "targetX": 118, - "targetY": 6, - "t": 111309 - }, - { - "targetX": 118, - "targetY": 4, - "t": 111325 - }, - { - "targetX": 118, - "targetY": 2, - "t": 111343 + "touchpoints": [ + { + "isFromTouch": true, + "path": { + "coords": [ + { + "targetX": 118, + "targetY": 79, + "t": 110727 + }, + { + "targetX": 118, + "targetY": 71, + "t": 110877 + }, + { + "targetX": 118, + "targetY": 68, + "t": 110909 + }, + { + "targetX": 118, + "targetY": 65, + "t": 110926 + }, + { + "targetX": 118, + "targetY": 62, + "t": 110943 + }, + { + "targetX": 118, + "targetY": 58, + "t": 110959 + }, + { + "targetX": 118, + "targetY": 55, + "t": 110976 + }, + { + "targetX": 118, + "targetY": 54, + "t": 110993 + }, + { + "targetX": 118, + "targetY": 52, + "t": 111009 + }, + { + "targetX": 118, + "targetY": 50, + "t": 111026 + }, + { + "targetX": 118, + "targetY": 45, + "t": 111043 + }, + { + "targetX": 118, + "targetY": 44, + "t": 111060 + }, + { + "targetX": 118, + "targetY": 42, + "t": 111076 + }, + { + "targetX": 118, + "targetY": 38, + "t": 111093 + }, + { + "targetX": 118, + "targetY": 34, + "t": 111109 + }, + { + "targetX": 118, + "targetY": 31, + "t": 111126 + }, + { + "targetX": 118, + "targetY": 28, + "t": 111143 + }, + { + "targetX": 118, + "targetY": 25, + "t": 111160 + }, + { + "targetX": 118, + "targetY": 22, + "t": 111176 + }, + { + "targetX": 118, + "targetY": 19, + "t": 111193 + }, + { + "targetX": 118, + "targetY": 17, + "t": 111209 + }, + { + "targetX": 118, + "targetY": 16, + "t": 111226 + }, + { + "targetX": 118, + "targetY": 15, + "t": 111243 + }, + { + "targetX": 118, + "targetY": 13, + "t": 111259 + }, + { + "targetX": 118, + "targetY": 10, + "t": 111276 + }, + { + "targetX": 118, + "targetY": 8, + "t": 111293 + }, + { + "targetX": 118, + "targetY": 6, + "t": 111309 + }, + { + "targetX": 118, + "targetY": 4, + "t": 111325 + }, + { + "targetX": 118, + "targetY": 2, + "t": 111343 + } + ], + "wasCancelled": true } - ], - "isFromTouch": true - }, - "terminationEvent": "cancel" + } + ] } ], "config": { diff --git a/common/web/gesture-recognizer/src/test/resources/json/hardBorderCancel.json b/common/web/gesture-recognizer/src/test/resources/json/hardBorderCancel.json index f4178206e3..56c4caa88b 100644 --- a/common/web/gesture-recognizer/src/test/resources/json/hardBorderCancel.json +++ b/common/web/gesture-recognizer/src/test/resources/json/hardBorderCancel.json @@ -1,22 +1,26 @@ { - "set": [ + "inputs": [ { - "sequence": { - "samples": [ - { - "targetX": 231, - "targetY": 82, - "t": 131762 - }, - { - "targetX": 242, - "targetY": 81, - "t": 131976 + "touchpoints": [ + { + "isFromTouch": true, + "path": { + "coords": [ + { + "targetX": 231, + "targetY": 82, + "t": 131762 + }, + { + "targetX": 242, + "targetY": 81, + "t": 131976 + } + ], + "wasCancelled": true } - ], - "isFromTouch": true - }, - "terminationEvent": "cancel" + } + ] } ], "config": { diff --git a/common/web/gesture-recognizer/src/test/resources/json/mobileProximityApproach.json b/common/web/gesture-recognizer/src/test/resources/json/mobileProximityApproach.json index 1141e8e538..a3ca094a65 100644 --- a/common/web/gesture-recognizer/src/test/resources/json/mobileProximityApproach.json +++ b/common/web/gesture-recognizer/src/test/resources/json/mobileProximityApproach.json @@ -1,272 +1,276 @@ { - "set": [ + "inputs": [ { - "sequence": { - "samples": [ - { - "targetX": 115, - "targetY": 79, - "t": 14808 - }, - { - "targetX": 124, - "targetY": 76, - "t": 14945 - }, - { - "targetX": 128, - "targetY": 76, - "t": 14977 - }, - { - "targetX": 132, - "targetY": 75, - "t": 14995 - }, - { - "targetX": 136, - "targetY": 75, - "t": 15012 - }, - { - "targetX": 140, - "targetY": 75, - "t": 15028 - }, - { - "targetX": 146, - "targetY": 75, - "t": 15045 - }, - { - "targetX": 150, - "targetY": 75, - "t": 15061 - }, - { - "targetX": 154, - "targetY": 75, - "t": 15078 - }, - { - "targetX": 158, - "targetY": 75, - "t": 15095 - }, - { - "targetX": 160, - "targetY": 75, - "t": 15111 - }, - { - "targetX": 162, - "targetY": 75, - "t": 15128 - }, - { - "targetX": 164, - "targetY": 75, - "t": 15144 - }, - { - "targetX": 166, - "targetY": 75, - "t": 15162 - }, - { - "targetX": 167, - "targetY": 75, - "t": 15177 - }, - { - "targetX": 170, - "targetY": 75, - "t": 15195 - }, - { - "targetX": 172, - "targetY": 75, - "t": 15211 - }, - { - "targetX": 175, - "targetY": 75, - "t": 15227 - }, - { - "targetX": 178, - "targetY": 75, - "t": 15244 - }, - { - "targetX": 180, - "targetY": 75, - "t": 15261 - }, - { - "targetX": 183, - "targetY": 75, - "t": 15278 - }, - { - "targetX": 187, - "targetY": 75, - "t": 15294 - }, - { - "targetX": 190, - "targetY": 75, - "t": 15311 - }, - { - "targetX": 192, - "targetY": 75, - "t": 15328 - }, - { - "targetX": 195, - "targetY": 75, - "t": 15344 - }, - { - "targetX": 198, - "targetY": 75, - "t": 15361 - }, - { - "targetX": 198, - "targetY": 75, - "t": 15378 - }, - { - "targetX": 199, - "targetY": 75, - "t": 15394 - }, - { - "targetX": 203, - "targetY": 75, - "t": 15411 - }, - { - "targetX": 207, - "targetY": 75, - "t": 15427 - }, - { - "targetX": 210, - "targetY": 75, - "t": 15444 - }, - { - "targetX": 212, - "targetY": 75, - "t": 15461 - }, - { - "targetX": 214, - "targetY": 75, - "t": 15477 - }, - { - "targetX": 215, - "targetY": 75, - "t": 15494 - }, - { - "targetX": 216, - "targetY": 75, - "t": 15511 - }, - { - "targetX": 218, - "targetY": 75, - "t": 15544 - }, - { - "targetX": 219, - "targetY": 75, - "t": 15561 - }, - { - "targetX": 220, - "targetY": 75, - "t": 15578 - }, - { - "targetX": 222, - "targetY": 75, - "t": 15594 - }, - { - "targetX": 222, - "targetY": 75, - "t": 15611 - }, - { - "targetX": 223, - "targetY": 75, - "t": 15628 - }, - { - "targetX": 223, - "targetY": 74, - "t": 15744 - }, - { - "targetX": 223, - "targetY": 73, - "t": 15845 - }, - { - "targetX": 224, - "targetY": 73, - "t": 15861 - }, - { - "targetX": 226, - "targetY": 73, - "t": 15894 - }, - { - "targetX": 226, - "targetY": 72, - "t": 15911 - }, - { - "targetX": 228, - "targetY": 71, - "t": 15927 - }, - { - "targetX": 230, - "targetY": 71, - "t": 15961 - }, - { - "targetX": 230, - "targetY": 71, - "t": 15978 - }, - { - "targetX": 231, - "targetY": 71, - "t": 15995 - }, - { - "targetX": 232, - "targetY": 71, - "t": 16044 - }, - { - "targetX": 232, - "targetY": 70, - "t": 16078 + "touchpoints": [ + { + "isFromTouch": true, + "path": { + "coords": [ + { + "targetX": 115, + "targetY": 79, + "t": 14808 + }, + { + "targetX": 124, + "targetY": 76, + "t": 14945 + }, + { + "targetX": 128, + "targetY": 76, + "t": 14977 + }, + { + "targetX": 132, + "targetY": 75, + "t": 14995 + }, + { + "targetX": 136, + "targetY": 75, + "t": 15012 + }, + { + "targetX": 140, + "targetY": 75, + "t": 15028 + }, + { + "targetX": 146, + "targetY": 75, + "t": 15045 + }, + { + "targetX": 150, + "targetY": 75, + "t": 15061 + }, + { + "targetX": 154, + "targetY": 75, + "t": 15078 + }, + { + "targetX": 158, + "targetY": 75, + "t": 15095 + }, + { + "targetX": 160, + "targetY": 75, + "t": 15111 + }, + { + "targetX": 162, + "targetY": 75, + "t": 15128 + }, + { + "targetX": 164, + "targetY": 75, + "t": 15144 + }, + { + "targetX": 166, + "targetY": 75, + "t": 15162 + }, + { + "targetX": 167, + "targetY": 75, + "t": 15177 + }, + { + "targetX": 170, + "targetY": 75, + "t": 15195 + }, + { + "targetX": 172, + "targetY": 75, + "t": 15211 + }, + { + "targetX": 175, + "targetY": 75, + "t": 15227 + }, + { + "targetX": 178, + "targetY": 75, + "t": 15244 + }, + { + "targetX": 180, + "targetY": 75, + "t": 15261 + }, + { + "targetX": 183, + "targetY": 75, + "t": 15278 + }, + { + "targetX": 187, + "targetY": 75, + "t": 15294 + }, + { + "targetX": 190, + "targetY": 75, + "t": 15311 + }, + { + "targetX": 192, + "targetY": 75, + "t": 15328 + }, + { + "targetX": 195, + "targetY": 75, + "t": 15344 + }, + { + "targetX": 198, + "targetY": 75, + "t": 15361 + }, + { + "targetX": 198, + "targetY": 75, + "t": 15378 + }, + { + "targetX": 199, + "targetY": 75, + "t": 15394 + }, + { + "targetX": 203, + "targetY": 75, + "t": 15411 + }, + { + "targetX": 207, + "targetY": 75, + "t": 15427 + }, + { + "targetX": 210, + "targetY": 75, + "t": 15444 + }, + { + "targetX": 212, + "targetY": 75, + "t": 15461 + }, + { + "targetX": 214, + "targetY": 75, + "t": 15477 + }, + { + "targetX": 215, + "targetY": 75, + "t": 15494 + }, + { + "targetX": 216, + "targetY": 75, + "t": 15511 + }, + { + "targetX": 218, + "targetY": 75, + "t": 15544 + }, + { + "targetX": 219, + "targetY": 75, + "t": 15561 + }, + { + "targetX": 220, + "targetY": 75, + "t": 15578 + }, + { + "targetX": 222, + "targetY": 75, + "t": 15594 + }, + { + "targetX": 222, + "targetY": 75, + "t": 15611 + }, + { + "targetX": 223, + "targetY": 75, + "t": 15628 + }, + { + "targetX": 223, + "targetY": 74, + "t": 15744 + }, + { + "targetX": 223, + "targetY": 73, + "t": 15845 + }, + { + "targetX": 224, + "targetY": 73, + "t": 15861 + }, + { + "targetX": 226, + "targetY": 73, + "t": 15894 + }, + { + "targetX": 226, + "targetY": 72, + "t": 15911 + }, + { + "targetX": 228, + "targetY": 71, + "t": 15927 + }, + { + "targetX": 230, + "targetY": 71, + "t": 15961 + }, + { + "targetX": 230, + "targetY": 71, + "t": 15978 + }, + { + "targetX": 231, + "targetY": 71, + "t": 15995 + }, + { + "targetX": 232, + "targetY": 71, + "t": 16044 + }, + { + "targetX": 232, + "targetY": 70, + "t": 16078 + } + ], + "wasCancelled": false } - ], - "isFromTouch": true - }, - "terminationEvent": "end" + } + ] } ], "config": { diff --git a/common/web/gesture-recognizer/src/test/resources/json/mobileSafeZoneCancel.json b/common/web/gesture-recognizer/src/test/resources/json/mobileSafeZoneCancel.json index 508b28be0c..cba5a30a7b 100644 --- a/common/web/gesture-recognizer/src/test/resources/json/mobileSafeZoneCancel.json +++ b/common/web/gesture-recognizer/src/test/resources/json/mobileSafeZoneCancel.json @@ -1,32 +1,36 @@ { - "set": [ + "inputs": [ { - "sequence": { - "samples": [ - { - "targetX": 226, - "targetY": 38, - "t": 61290 - }, - { - "targetX": 234, - "targetY": 38, - "t": 61676 - }, - { - "targetX": 235, - "targetY": 37, - "t": 61729 - }, - { - "targetX": 237, - "targetY": 37, - "t": 61829 + "touchpoints": [ + { + "isFromTouch": true, + "path": { + "coords": [ + { + "targetX": 226, + "targetY": 38, + "t": 61290 + }, + { + "targetX": 234, + "targetY": 38, + "t": 61676 + }, + { + "targetX": 235, + "targetY": 37, + "t": 61729 + }, + { + "targetX": 237, + "targetY": 37, + "t": 61829 + } + ], + "wasCancelled": true } - ], - "isFromTouch": true - }, - "terminationEvent": "cancel" + } + ] } ], "config": { diff --git a/common/web/gesture-recognizer/src/test/resources/json/popupLongRoamingEnd.json b/common/web/gesture-recognizer/src/test/resources/json/popupLongRoamingEnd.json index 6b60ef61ad..cefbf00fb4 100644 --- a/common/web/gesture-recognizer/src/test/resources/json/popupLongRoamingEnd.json +++ b/common/web/gesture-recognizer/src/test/resources/json/popupLongRoamingEnd.json @@ -1,802 +1,806 @@ { - "set": [ + "inputs": [ { - "sequence": { - "samples": [ - { - "targetX": 83, - "targetY": 25, - "t": 512159 - }, - { - "targetX": 83, - "targetY": 33, - "t": 512303 - }, - { - "targetX": 83, - "targetY": 34, - "t": 512335 - }, - { - "targetX": 83, - "targetY": 37, - "t": 512351 - }, - { - "targetX": 82, - "targetY": 38, - "t": 512368 - }, - { - "targetX": 82, - "targetY": 40, - "t": 512385 - }, - { - "targetX": 82, - "targetY": 41, - "t": 512401 - }, - { - "targetX": 82, - "targetY": 44, - "t": 512418 - }, - { - "targetX": 82, - "targetY": 45, - "t": 512435 - }, - { - "targetX": 82, - "targetY": 46, - "t": 512451 - }, - { - "targetX": 80, - "targetY": 46, - "t": 512468 - }, - { - "targetX": 79, - "targetY": 46, - "t": 512484 - }, - { - "targetX": 79, - "targetY": 48, - "t": 512501 - }, - { - "targetX": 79, - "targetY": 48, - "t": 512518 - }, - { - "targetX": 79, - "targetY": 49, - "t": 512535 - }, - { - "targetX": 78, - "targetY": 49, - "t": 512551 - }, - { - "targetX": 78, - "targetY": 50, - "t": 512568 - }, - { - "targetX": 78, - "targetY": 52, - "t": 512584 - }, - { - "targetX": 75, - "targetY": 52, - "t": 512601 - }, - { - "targetX": 75, - "targetY": 53, - "t": 512618 - }, - { - "targetX": 74, - "targetY": 53, - "t": 512635 - }, - { - "targetX": 74, - "targetY": 54, - "t": 512651 - }, - { - "targetX": 71, - "targetY": 54, - "t": 512668 - }, - { - "targetX": 70, - "targetY": 54, - "t": 512685 - }, - { - "targetX": 70, - "targetY": 53, - "t": 512702 - }, - { - "targetX": 68, - "targetY": 53, - "t": 512718 - }, - { - "targetX": 66, - "targetY": 54, - "t": 512735 - }, - { - "targetX": 63, - "targetY": 53, - "t": 512751 - }, - { - "targetX": 62, - "targetY": 53, - "t": 512768 - }, - { - "targetX": 62, - "targetY": 53, - "t": 512785 - }, - { - "targetX": 59, - "targetY": 53, - "t": 512802 - }, - { - "targetX": 56, - "targetY": 53, - "t": 512818 - }, - { - "targetX": 54, - "targetY": 53, - "t": 512835 - }, - { - "targetX": 51, - "targetY": 53, - "t": 512851 - }, - { - "targetX": 48, - "targetY": 53, - "t": 512868 - }, - { - "targetX": 46, - "targetY": 53, - "t": 512885 - }, - { - "targetX": 42, - "targetY": 53, - "t": 512902 - }, - { - "targetX": 39, - "targetY": 53, - "t": 512918 - }, - { - "targetX": 38, - "targetY": 53, - "t": 512935 - }, - { - "targetX": 35, - "targetY": 53, - "t": 512951 - }, - { - "targetX": 32, - "targetY": 53, - "t": 512968 - }, - { - "targetX": 30, - "targetY": 53, - "t": 512984 - }, - { - "targetX": 30, - "targetY": 53, - "t": 513002 - }, - { - "targetX": 28, - "targetY": 53, - "t": 513018 - }, - { - "targetX": 27, - "targetY": 53, - "t": 513035 - }, - { - "targetX": 24, - "targetY": 53, - "t": 513051 - }, - { - "targetX": 23, - "targetY": 53, - "t": 513068 - }, - { - "targetX": 22, - "targetY": 53, - "t": 513084 - }, - { - "targetX": 20, - "targetY": 53, - "t": 513102 - }, - { - "targetX": 19, - "targetY": 53, - "t": 513118 - }, - { - "targetX": 16, - "targetY": 53, - "t": 513135 - }, - { - "targetX": 14, - "targetY": 53, - "t": 513152 - }, - { - "targetX": 12, - "targetY": 53, - "t": 513168 - }, - { - "targetX": 10, - "targetY": 53, - "t": 513184 - }, - { - "targetX": 10, - "targetY": 53, - "t": 513201 - }, - { - "targetX": 8, - "targetY": 53, - "t": 513218 - }, - { - "targetX": 7, - "targetY": 53, - "t": 513234 - }, - { - "targetX": 6, - "targetY": 53, - "t": 513251 - }, - { - "targetX": 6, - "targetY": 53, - "t": 513285 - }, - { - "targetX": 4, - "targetY": 53, - "t": 513319 - }, - { - "targetX": 3, - "targetY": 53, - "t": 513368 - }, - { - "targetX": 2, - "targetY": 52, - "t": 513385 - }, - { - "targetX": -1, - "targetY": 52, - "t": 513401 - }, - { - "targetX": -2, - "targetY": 50, - "t": 513418 - }, - { - "targetX": -5, - "targetY": 50, - "t": 513434 - }, - { - "targetX": -6, - "targetY": 49, - "t": 513452 - }, - { - "targetX": -6, - "targetY": 48, - "t": 513468 - }, - { - "targetX": -8, - "targetY": 48, - "t": 513485 - }, - { - "targetX": -10, - "targetY": 46, - "t": 513518 - }, - { - "targetX": -10, - "targetY": 45, - "t": 513535 - }, - { - "targetX": -12, - "targetY": 44, - "t": 513551 - }, - { - "targetX": -13, - "targetY": 44, - "t": 513568 - }, - { - "targetX": -13, - "targetY": 44, - "t": 513585 - }, - { - "targetX": -13, - "targetY": 42, - "t": 513618 - }, - { - "targetX": -13, - "targetY": 41, - "t": 513635 - }, - { - "targetX": -14, - "targetY": 40, - "t": 513652 - }, - { - "targetX": -14, - "targetY": 40, - "t": 513668 - }, - { - "targetX": -14, - "targetY": 38, - "t": 513685 - }, - { - "targetX": -14, - "targetY": 37, - "t": 513701 - }, - { - "targetX": -14, - "targetY": 36, - "t": 513719 - }, - { - "targetX": -14, - "targetY": 36, - "t": 513735 - }, - { - "targetX": -14, - "targetY": 34, - "t": 513752 - }, - { - "targetX": -14, - "targetY": 32, - "t": 513768 - }, - { - "targetX": -14, - "targetY": 29, - "t": 513802 - }, - { - "targetX": -14, - "targetY": 28, - "t": 513835 - }, - { - "targetX": -14, - "targetY": 26, - "t": 513851 - }, - { - "targetX": -14, - "targetY": 24, - "t": 513868 - }, - { - "targetX": -14, - "targetY": 22, - "t": 513885 - }, - { - "targetX": -14, - "targetY": 21, - "t": 513901 - }, - { - "targetX": -14, - "targetY": 20, - "t": 513919 - }, - { - "targetX": -14, - "targetY": 20, - "t": 513934 - }, - { - "targetX": -14, - "targetY": 18, - "t": 513952 - }, - { - "targetX": -14, - "targetY": 17, - "t": 513968 - }, - { - "targetX": -14, - "targetY": 16, - "t": 513985 - }, - { - "targetX": -14, - "targetY": 14, - "t": 514001 - }, - { - "targetX": -16, - "targetY": 13, - "t": 514018 - }, - { - "targetX": -17, - "targetY": 12, - "t": 514035 - }, - { - "targetX": -17, - "targetY": 10, - "t": 514051 - }, - { - "targetX": -17, - "targetY": 8, - "t": 514068 - }, - { - "targetX": -17, - "targetY": 8, - "t": 514085 - }, - { - "targetX": -17, - "targetY": 5, - "t": 514101 - }, - { - "targetX": -17, - "targetY": 4, - "t": 514118 - }, - { - "targetX": -17, - "targetY": 2, - "t": 514135 - }, - { - "targetX": -17, - "targetY": 0, - "t": 514151 - }, - { - "targetX": -17, - "targetY": 0, - "t": 514168 - }, - { - "targetX": -17, - "targetY": -2, - "t": 514184 - }, - { - "targetX": -17, - "targetY": -3, - "t": 514201 - }, - { - "targetX": -17, - "targetY": -4, - "t": 514219 - }, - { - "targetX": -17, - "targetY": -4, - "t": 514235 - }, - { - "targetX": -17, - "targetY": -6, - "t": 514251 - }, - { - "targetX": -17, - "targetY": -7, - "t": 514268 - }, - { - "targetX": -17, - "targetY": -8, - "t": 514284 - }, - { - "targetX": -17, - "targetY": -10, - "t": 514335 - }, - { - "targetX": -17, - "targetY": -11, - "t": 514351 - }, - { - "targetX": -17, - "targetY": -12, - "t": 514368 - }, - { - "targetX": -17, - "targetY": -12, - "t": 514485 - }, - { - "targetX": -16, - "targetY": -12, - "t": 514518 - }, - { - "targetX": -13, - "targetY": -14, - "t": 514535 - }, - { - "targetX": -13, - "targetY": -15, - "t": 514551 - }, - { - "targetX": -12, - "targetY": -15, - "t": 514602 - }, - { - "targetX": -10, - "targetY": -15, - "t": 514618 - }, - { - "targetX": -6, - "targetY": -15, - "t": 514635 - }, - { - "targetX": -5, - "targetY": -15, - "t": 514652 - }, - { - "targetX": -2, - "targetY": -15, - "t": 514701 - }, - { - "targetX": -1, - "targetY": -15, - "t": 514718 - }, - { - "targetX": 0, - "targetY": -15, - "t": 514735 - }, - { - "targetX": 2, - "targetY": -15, - "t": 514751 - }, - { - "targetX": 3, - "targetY": -15, - "t": 514768 - }, - { - "targetX": 6, - "targetY": -15, - "t": 514785 - }, - { - "targetX": 7, - "targetY": -15, - "t": 514801 - }, - { - "targetX": 10, - "targetY": -15, - "t": 514818 - }, - { - "targetX": 11, - "targetY": -15, - "t": 514834 - }, - { - "targetX": 14, - "targetY": -15, - "t": 514869 - }, - { - "targetX": 15, - "targetY": -15, - "t": 514885 - }, - { - "targetX": 16, - "targetY": -15, - "t": 514918 - }, - { - "targetX": 18, - "targetY": -15, - "t": 514934 - }, - { - "targetX": 19, - "targetY": -15, - "t": 514952 - }, - { - "targetX": 20, - "targetY": -15, - "t": 514968 - }, - { - "targetX": 23, - "targetY": -15, - "t": 515018 - }, - { - "targetX": 27, - "targetY": -15, - "t": 515034 - }, - { - "targetX": 31, - "targetY": -15, - "t": 515052 - }, - { - "targetX": 35, - "targetY": -14, - "t": 515068 - }, - { - "targetX": 36, - "targetY": -15, - "t": 515085 - }, - { - "targetX": 38, - "targetY": -15, - "t": 515101 - }, - { - "targetX": 39, - "targetY": -15, - "t": 515118 - }, - { - "targetX": 40, - "targetY": -15, - "t": 515134 - }, - { - "targetX": 42, - "targetY": -15, - "t": 515168 - }, - { - "targetX": 43, - "targetY": -15, - "t": 515185 - }, - { - "targetX": 44, - "targetY": -15, - "t": 515251 - }, - { - "targetX": 46, - "targetY": -15, - "t": 515268 - }, - { - "targetX": 46, - "targetY": -15, - "t": 515285 - }, - { - "targetX": 47, - "targetY": -15, - "t": 515301 - }, - { - "targetX": 48, - "targetY": -15, - "t": 515335 - }, - { - "targetX": 50, - "targetY": -15, - "t": 515352 - }, - { - "targetX": 50, - "targetY": -15, - "t": 515368 - }, - { - "targetX": 51, - "targetY": -15, - "t": 515385 - }, - { - "targetX": 52, - "targetY": -15, - "t": 515535 - }, - { - "targetX": 51, - "targetY": -15, - "t": 515552 + "touchpoints": [ + { + "isFromTouch": true, + "path": { + "coords": [ + { + "targetX": 83, + "targetY": 25, + "t": 512159 + }, + { + "targetX": 83, + "targetY": 33, + "t": 512303 + }, + { + "targetX": 83, + "targetY": 34, + "t": 512335 + }, + { + "targetX": 83, + "targetY": 37, + "t": 512351 + }, + { + "targetX": 82, + "targetY": 38, + "t": 512368 + }, + { + "targetX": 82, + "targetY": 40, + "t": 512385 + }, + { + "targetX": 82, + "targetY": 41, + "t": 512401 + }, + { + "targetX": 82, + "targetY": 44, + "t": 512418 + }, + { + "targetX": 82, + "targetY": 45, + "t": 512435 + }, + { + "targetX": 82, + "targetY": 46, + "t": 512451 + }, + { + "targetX": 80, + "targetY": 46, + "t": 512468 + }, + { + "targetX": 79, + "targetY": 46, + "t": 512484 + }, + { + "targetX": 79, + "targetY": 48, + "t": 512501 + }, + { + "targetX": 79, + "targetY": 48, + "t": 512518 + }, + { + "targetX": 79, + "targetY": 49, + "t": 512535 + }, + { + "targetX": 78, + "targetY": 49, + "t": 512551 + }, + { + "targetX": 78, + "targetY": 50, + "t": 512568 + }, + { + "targetX": 78, + "targetY": 52, + "t": 512584 + }, + { + "targetX": 75, + "targetY": 52, + "t": 512601 + }, + { + "targetX": 75, + "targetY": 53, + "t": 512618 + }, + { + "targetX": 74, + "targetY": 53, + "t": 512635 + }, + { + "targetX": 74, + "targetY": 54, + "t": 512651 + }, + { + "targetX": 71, + "targetY": 54, + "t": 512668 + }, + { + "targetX": 70, + "targetY": 54, + "t": 512685 + }, + { + "targetX": 70, + "targetY": 53, + "t": 512702 + }, + { + "targetX": 68, + "targetY": 53, + "t": 512718 + }, + { + "targetX": 66, + "targetY": 54, + "t": 512735 + }, + { + "targetX": 63, + "targetY": 53, + "t": 512751 + }, + { + "targetX": 62, + "targetY": 53, + "t": 512768 + }, + { + "targetX": 62, + "targetY": 53, + "t": 512785 + }, + { + "targetX": 59, + "targetY": 53, + "t": 512802 + }, + { + "targetX": 56, + "targetY": 53, + "t": 512818 + }, + { + "targetX": 54, + "targetY": 53, + "t": 512835 + }, + { + "targetX": 51, + "targetY": 53, + "t": 512851 + }, + { + "targetX": 48, + "targetY": 53, + "t": 512868 + }, + { + "targetX": 46, + "targetY": 53, + "t": 512885 + }, + { + "targetX": 42, + "targetY": 53, + "t": 512902 + }, + { + "targetX": 39, + "targetY": 53, + "t": 512918 + }, + { + "targetX": 38, + "targetY": 53, + "t": 512935 + }, + { + "targetX": 35, + "targetY": 53, + "t": 512951 + }, + { + "targetX": 32, + "targetY": 53, + "t": 512968 + }, + { + "targetX": 30, + "targetY": 53, + "t": 512984 + }, + { + "targetX": 30, + "targetY": 53, + "t": 513002 + }, + { + "targetX": 28, + "targetY": 53, + "t": 513018 + }, + { + "targetX": 27, + "targetY": 53, + "t": 513035 + }, + { + "targetX": 24, + "targetY": 53, + "t": 513051 + }, + { + "targetX": 23, + "targetY": 53, + "t": 513068 + }, + { + "targetX": 22, + "targetY": 53, + "t": 513084 + }, + { + "targetX": 20, + "targetY": 53, + "t": 513102 + }, + { + "targetX": 19, + "targetY": 53, + "t": 513118 + }, + { + "targetX": 16, + "targetY": 53, + "t": 513135 + }, + { + "targetX": 14, + "targetY": 53, + "t": 513152 + }, + { + "targetX": 12, + "targetY": 53, + "t": 513168 + }, + { + "targetX": 10, + "targetY": 53, + "t": 513184 + }, + { + "targetX": 10, + "targetY": 53, + "t": 513201 + }, + { + "targetX": 8, + "targetY": 53, + "t": 513218 + }, + { + "targetX": 7, + "targetY": 53, + "t": 513234 + }, + { + "targetX": 6, + "targetY": 53, + "t": 513251 + }, + { + "targetX": 6, + "targetY": 53, + "t": 513285 + }, + { + "targetX": 4, + "targetY": 53, + "t": 513319 + }, + { + "targetX": 3, + "targetY": 53, + "t": 513368 + }, + { + "targetX": 2, + "targetY": 52, + "t": 513385 + }, + { + "targetX": -1, + "targetY": 52, + "t": 513401 + }, + { + "targetX": -2, + "targetY": 50, + "t": 513418 + }, + { + "targetX": -5, + "targetY": 50, + "t": 513434 + }, + { + "targetX": -6, + "targetY": 49, + "t": 513452 + }, + { + "targetX": -6, + "targetY": 48, + "t": 513468 + }, + { + "targetX": -8, + "targetY": 48, + "t": 513485 + }, + { + "targetX": -10, + "targetY": 46, + "t": 513518 + }, + { + "targetX": -10, + "targetY": 45, + "t": 513535 + }, + { + "targetX": -12, + "targetY": 44, + "t": 513551 + }, + { + "targetX": -13, + "targetY": 44, + "t": 513568 + }, + { + "targetX": -13, + "targetY": 44, + "t": 513585 + }, + { + "targetX": -13, + "targetY": 42, + "t": 513618 + }, + { + "targetX": -13, + "targetY": 41, + "t": 513635 + }, + { + "targetX": -14, + "targetY": 40, + "t": 513652 + }, + { + "targetX": -14, + "targetY": 40, + "t": 513668 + }, + { + "targetX": -14, + "targetY": 38, + "t": 513685 + }, + { + "targetX": -14, + "targetY": 37, + "t": 513701 + }, + { + "targetX": -14, + "targetY": 36, + "t": 513719 + }, + { + "targetX": -14, + "targetY": 36, + "t": 513735 + }, + { + "targetX": -14, + "targetY": 34, + "t": 513752 + }, + { + "targetX": -14, + "targetY": 32, + "t": 513768 + }, + { + "targetX": -14, + "targetY": 29, + "t": 513802 + }, + { + "targetX": -14, + "targetY": 28, + "t": 513835 + }, + { + "targetX": -14, + "targetY": 26, + "t": 513851 + }, + { + "targetX": -14, + "targetY": 24, + "t": 513868 + }, + { + "targetX": -14, + "targetY": 22, + "t": 513885 + }, + { + "targetX": -14, + "targetY": 21, + "t": 513901 + }, + { + "targetX": -14, + "targetY": 20, + "t": 513919 + }, + { + "targetX": -14, + "targetY": 20, + "t": 513934 + }, + { + "targetX": -14, + "targetY": 18, + "t": 513952 + }, + { + "targetX": -14, + "targetY": 17, + "t": 513968 + }, + { + "targetX": -14, + "targetY": 16, + "t": 513985 + }, + { + "targetX": -14, + "targetY": 14, + "t": 514001 + }, + { + "targetX": -16, + "targetY": 13, + "t": 514018 + }, + { + "targetX": -17, + "targetY": 12, + "t": 514035 + }, + { + "targetX": -17, + "targetY": 10, + "t": 514051 + }, + { + "targetX": -17, + "targetY": 8, + "t": 514068 + }, + { + "targetX": -17, + "targetY": 8, + "t": 514085 + }, + { + "targetX": -17, + "targetY": 5, + "t": 514101 + }, + { + "targetX": -17, + "targetY": 4, + "t": 514118 + }, + { + "targetX": -17, + "targetY": 2, + "t": 514135 + }, + { + "targetX": -17, + "targetY": 0, + "t": 514151 + }, + { + "targetX": -17, + "targetY": 0, + "t": 514168 + }, + { + "targetX": -17, + "targetY": -2, + "t": 514184 + }, + { + "targetX": -17, + "targetY": -3, + "t": 514201 + }, + { + "targetX": -17, + "targetY": -4, + "t": 514219 + }, + { + "targetX": -17, + "targetY": -4, + "t": 514235 + }, + { + "targetX": -17, + "targetY": -6, + "t": 514251 + }, + { + "targetX": -17, + "targetY": -7, + "t": 514268 + }, + { + "targetX": -17, + "targetY": -8, + "t": 514284 + }, + { + "targetX": -17, + "targetY": -10, + "t": 514335 + }, + { + "targetX": -17, + "targetY": -11, + "t": 514351 + }, + { + "targetX": -17, + "targetY": -12, + "t": 514368 + }, + { + "targetX": -17, + "targetY": -12, + "t": 514485 + }, + { + "targetX": -16, + "targetY": -12, + "t": 514518 + }, + { + "targetX": -13, + "targetY": -14, + "t": 514535 + }, + { + "targetX": -13, + "targetY": -15, + "t": 514551 + }, + { + "targetX": -12, + "targetY": -15, + "t": 514602 + }, + { + "targetX": -10, + "targetY": -15, + "t": 514618 + }, + { + "targetX": -6, + "targetY": -15, + "t": 514635 + }, + { + "targetX": -5, + "targetY": -15, + "t": 514652 + }, + { + "targetX": -2, + "targetY": -15, + "t": 514701 + }, + { + "targetX": -1, + "targetY": -15, + "t": 514718 + }, + { + "targetX": 0, + "targetY": -15, + "t": 514735 + }, + { + "targetX": 2, + "targetY": -15, + "t": 514751 + }, + { + "targetX": 3, + "targetY": -15, + "t": 514768 + }, + { + "targetX": 6, + "targetY": -15, + "t": 514785 + }, + { + "targetX": 7, + "targetY": -15, + "t": 514801 + }, + { + "targetX": 10, + "targetY": -15, + "t": 514818 + }, + { + "targetX": 11, + "targetY": -15, + "t": 514834 + }, + { + "targetX": 14, + "targetY": -15, + "t": 514869 + }, + { + "targetX": 15, + "targetY": -15, + "t": 514885 + }, + { + "targetX": 16, + "targetY": -15, + "t": 514918 + }, + { + "targetX": 18, + "targetY": -15, + "t": 514934 + }, + { + "targetX": 19, + "targetY": -15, + "t": 514952 + }, + { + "targetX": 20, + "targetY": -15, + "t": 514968 + }, + { + "targetX": 23, + "targetY": -15, + "t": 515018 + }, + { + "targetX": 27, + "targetY": -15, + "t": 515034 + }, + { + "targetX": 31, + "targetY": -15, + "t": 515052 + }, + { + "targetX": 35, + "targetY": -14, + "t": 515068 + }, + { + "targetX": 36, + "targetY": -15, + "t": 515085 + }, + { + "targetX": 38, + "targetY": -15, + "t": 515101 + }, + { + "targetX": 39, + "targetY": -15, + "t": 515118 + }, + { + "targetX": 40, + "targetY": -15, + "t": 515134 + }, + { + "targetX": 42, + "targetY": -15, + "t": 515168 + }, + { + "targetX": 43, + "targetY": -15, + "t": 515185 + }, + { + "targetX": 44, + "targetY": -15, + "t": 515251 + }, + { + "targetX": 46, + "targetY": -15, + "t": 515268 + }, + { + "targetX": 46, + "targetY": -15, + "t": 515285 + }, + { + "targetX": 47, + "targetY": -15, + "t": 515301 + }, + { + "targetX": 48, + "targetY": -15, + "t": 515335 + }, + { + "targetX": 50, + "targetY": -15, + "t": 515352 + }, + { + "targetX": 50, + "targetY": -15, + "t": 515368 + }, + { + "targetX": 51, + "targetY": -15, + "t": 515385 + }, + { + "targetX": 52, + "targetY": -15, + "t": 515535 + }, + { + "targetX": 51, + "targetY": -15, + "t": 515552 + } + ], + "wasCancelled": false } - ], - "isFromTouch": true - }, - "terminationEvent": "end" + } + ] } ], "config": { diff --git a/common/web/gesture-recognizer/src/test/resources/json/popupSafePersistence.json b/common/web/gesture-recognizer/src/test/resources/json/popupSafePersistence.json index 04910afb3c..01c108c530 100644 --- a/common/web/gesture-recognizer/src/test/resources/json/popupSafePersistence.json +++ b/common/web/gesture-recognizer/src/test/resources/json/popupSafePersistence.json @@ -1,592 +1,596 @@ { - "set": [ + "inputs": [ { - "sequence": { - "samples": [ - { - "targetX": 133, - "targetY": 19, - "t": 601934 - }, - { - "targetX": 134, - "targetY": 19, - "t": 603068 - }, - { - "targetX": 135, - "targetY": 19, - "t": 603093 - }, - { - "targetX": 135, - "targetY": 19, - "t": 603109 - }, - { - "targetX": 136, - "targetY": 19, - "t": 603172 - }, - { - "targetX": 136, - "targetY": 18, - "t": 603193 - }, - { - "targetX": 137, - "targetY": 18, - "t": 603226 - }, - { - "targetX": 137, - "targetY": 18, - "t": 603278 - }, - { - "targetX": 138, - "targetY": 18, - "t": 603309 - }, - { - "targetX": 138, - "targetY": 17, - "t": 603326 - }, - { - "targetX": 139, - "targetY": 17, - "t": 603360 - }, - { - "targetX": 139, - "targetY": 16, - "t": 603376 - }, - { - "targetX": 140, - "targetY": 16, - "t": 603410 - }, - { - "targetX": 141, - "targetY": 16, - "t": 603426 - }, - { - "targetX": 141, - "targetY": 16, - "t": 603486 - }, - { - "targetX": 142, - "targetY": 16, - "t": 603509 - }, - { - "targetX": 143, - "targetY": 16, - "t": 603543 - }, - { - "targetX": 144, - "targetY": 16, - "t": 603584 - }, - { - "targetX": 145, - "targetY": 16, - "t": 603610 - }, - { - "targetX": 145, - "targetY": 16, - "t": 603646 - }, - { - "targetX": 146, - "targetY": 16, - "t": 603679 - }, - { - "targetX": 147, - "targetY": 16, - "t": 603692 - }, - { - "targetX": 148, - "targetY": 16, - "t": 603735 - }, - { - "targetX": 149, - "targetY": 16, - "t": 603759 - }, - { - "targetX": 149, - "targetY": 17, - "t": 603776 - }, - { - "targetX": 150, - "targetY": 17, - "t": 603809 - }, - { - "targetX": 151, - "targetY": 18, - "t": 603826 - }, - { - "targetX": 151, - "targetY": 19, - "t": 603873 - }, - { - "targetX": 152, - "targetY": 19, - "t": 603897 - }, - { - "targetX": 153, - "targetY": 19, - "t": 603954 - }, - { - "targetX": 153, - "targetY": 19, - "t": 603994 - }, - { - "targetX": 153, - "targetY": 20, - "t": 604076 - }, - { - "targetX": 153, - "targetY": 21, - "t": 604558 - }, - { - "targetX": 153, - "targetY": 22, - "t": 607143 - }, - { - "targetX": 153, - "targetY": 23, - "t": 607250 - }, - { - "targetX": 153, - "targetY": 23, - "t": 607276 - }, - { - "targetX": 153, - "targetY": 25, - "t": 607292 - }, - { - "targetX": 153, - "targetY": 26, - "t": 609118 - }, - { - "targetX": 153, - "targetY": 27, - "t": 609148 - }, - { - "targetX": 152, - "targetY": 27, - "t": 609176 - }, - { - "targetX": 152, - "targetY": 27, - "t": 609193 - }, - { - "targetX": 152, - "targetY": 28, - "t": 610651 - }, - { - "targetX": 152, - "targetY": 29, - "t": 610659 - }, - { - "targetX": 152, - "targetY": 30, - "t": 610676 - }, - { - "targetX": 152, - "targetY": 31, - "t": 610709 - }, - { - "targetX": 152, - "targetY": 31, - "t": 611036 - }, - { - "targetX": 152, - "targetY": 32, - "t": 611042 - }, - { - "targetX": 152, - "targetY": 33, - "t": 611059 - }, - { - "targetX": 152, - "targetY": 34, - "t": 611076 - }, - { - "targetX": 152, - "targetY": 35, - "t": 611092 - }, - { - "targetX": 152, - "targetY": 35, - "t": 611126 - }, - { - "targetX": 152, - "targetY": 36, - "t": 611142 - }, - { - "targetX": 152, - "targetY": 37, - "t": 611159 - }, - { - "targetX": 152, - "targetY": 38, - "t": 611192 - }, - { - "targetX": 151, - "targetY": 39, - "t": 611208 - }, - { - "targetX": 151, - "targetY": 40, - "t": 611226 - }, - { - "targetX": 151, - "targetY": 41, - "t": 611242 - }, - { - "targetX": 150, - "targetY": 43, - "t": 611275 - }, - { - "targetX": 150, - "targetY": 43, - "t": 611292 - }, - { - "targetX": 150, - "targetY": 45, - "t": 611309 - }, - { - "targetX": 149, - "targetY": 47, - "t": 611342 - }, - { - "targetX": 149, - "targetY": 47, - "t": 611359 - }, - { - "targetX": 149, - "targetY": 49, - "t": 611376 - }, - { - "targetX": 149, - "targetY": 51, - "t": 611409 - }, - { - "targetX": 149, - "targetY": 51, - "t": 611426 - }, - { - "targetX": 149, - "targetY": 52, - "t": 611442 - }, - { - "targetX": 149, - "targetY": 53, - "t": 611459 - }, - { - "targetX": 148, - "targetY": 55, - "t": 611475 - }, - { - "targetX": 148, - "targetY": 55, - "t": 611493 - }, - { - "targetX": 148, - "targetY": 56, - "t": 611509 - }, - { - "targetX": 147, - "targetY": 57, - "t": 611526 - }, - { - "targetX": 147, - "targetY": 59, - "t": 611543 - }, - { - "targetX": 146, - "targetY": 59, - "t": 611559 - }, - { - "targetX": 145, - "targetY": 60, - "t": 611576 - }, - { - "targetX": 145, - "targetY": 61, - "t": 611592 - }, - { - "targetX": 145, - "targetY": 63, - "t": 611609 - }, - { - "targetX": 144, - "targetY": 64, - "t": 611626 - }, - { - "targetX": 143, - "targetY": 65, - "t": 611642 - }, - { - "targetX": 141, - "targetY": 67, - "t": 611659 - }, - { - "targetX": 141, - "targetY": 68, - "t": 611676 - }, - { - "targetX": 139, - "targetY": 70, - "t": 611693 - }, - { - "targetX": 137, - "targetY": 71, - "t": 611709 - }, - { - "targetX": 136, - "targetY": 72, - "t": 611726 - }, - { - "targetX": 134, - "targetY": 74, - "t": 611743 - }, - { - "targetX": 133, - "targetY": 75, - "t": 611759 - }, - { - "targetX": 131, - "targetY": 76, - "t": 611776 - }, - { - "targetX": 130, - "targetY": 77, - "t": 611793 - }, - { - "targetX": 129, - "targetY": 78, - "t": 611809 - }, - { - "targetX": 127, - "targetY": 79, - "t": 611825 - }, - { - "targetX": 125, - "targetY": 79, - "t": 611842 - }, - { - "targetX": 123, - "targetY": 80, - "t": 611859 - }, - { - "targetX": 121, - "targetY": 81, - "t": 611876 - }, - { - "targetX": 121, - "targetY": 82, - "t": 611892 - }, - { - "targetX": 118, - "targetY": 82, - "t": 611909 - }, - { - "targetX": 117, - "targetY": 82, - "t": 611926 - }, - { - "targetX": 115, - "targetY": 82, - "t": 611942 - }, - { - "targetX": 113, - "targetY": 82, - "t": 611959 - }, - { - "targetX": 111, - "targetY": 82, - "t": 611975 - }, - { - "targetX": 109, - "targetY": 82, - "t": 611992 - }, - { - "targetX": 106, - "targetY": 81, - "t": 612009 - }, - { - "targetX": 102, - "targetY": 79, - "t": 612025 - }, - { - "targetX": 97, - "targetY": 78, - "t": 612042 - }, - { - "targetX": 92, - "targetY": 76, - "t": 612058 - }, - { - "targetX": 87, - "targetY": 75, - "t": 612076 - }, - { - "targetX": 83, - "targetY": 73, - "t": 612092 - }, - { - "targetX": 79, - "targetY": 72, - "t": 612109 - }, - { - "targetX": 75, - "targetY": 71, - "t": 612125 - }, - { - "targetX": 73, - "targetY": 71, - "t": 612142 - }, - { - "targetX": 71, - "targetY": 70, - "t": 612158 - }, - { - "targetX": 70, - "targetY": 70, - "t": 612175 - }, - { - "targetX": 69, - "targetY": 69, - "t": 612192 - }, - { - "targetX": 69, - "targetY": 68, - "t": 612244 - }, - { - "targetX": 68, - "targetY": 68, - "t": 612258 - }, - { - "targetX": 67, - "targetY": 68, - "t": 612276 - }, - { - "targetX": 66, - "targetY": 67, - "t": 612292 - }, - { - "targetX": 65, - "targetY": 67, - "t": 612309 + "touchpoints": [ + { + "isFromTouch": false, + "path": { + "coords": [ + { + "targetX": 133, + "targetY": 19, + "t": 601934 + }, + { + "targetX": 134, + "targetY": 19, + "t": 603068 + }, + { + "targetX": 135, + "targetY": 19, + "t": 603093 + }, + { + "targetX": 135, + "targetY": 19, + "t": 603109 + }, + { + "targetX": 136, + "targetY": 19, + "t": 603172 + }, + { + "targetX": 136, + "targetY": 18, + "t": 603193 + }, + { + "targetX": 137, + "targetY": 18, + "t": 603226 + }, + { + "targetX": 137, + "targetY": 18, + "t": 603278 + }, + { + "targetX": 138, + "targetY": 18, + "t": 603309 + }, + { + "targetX": 138, + "targetY": 17, + "t": 603326 + }, + { + "targetX": 139, + "targetY": 17, + "t": 603360 + }, + { + "targetX": 139, + "targetY": 16, + "t": 603376 + }, + { + "targetX": 140, + "targetY": 16, + "t": 603410 + }, + { + "targetX": 141, + "targetY": 16, + "t": 603426 + }, + { + "targetX": 141, + "targetY": 16, + "t": 603486 + }, + { + "targetX": 142, + "targetY": 16, + "t": 603509 + }, + { + "targetX": 143, + "targetY": 16, + "t": 603543 + }, + { + "targetX": 144, + "targetY": 16, + "t": 603584 + }, + { + "targetX": 145, + "targetY": 16, + "t": 603610 + }, + { + "targetX": 145, + "targetY": 16, + "t": 603646 + }, + { + "targetX": 146, + "targetY": 16, + "t": 603679 + }, + { + "targetX": 147, + "targetY": 16, + "t": 603692 + }, + { + "targetX": 148, + "targetY": 16, + "t": 603735 + }, + { + "targetX": 149, + "targetY": 16, + "t": 603759 + }, + { + "targetX": 149, + "targetY": 17, + "t": 603776 + }, + { + "targetX": 150, + "targetY": 17, + "t": 603809 + }, + { + "targetX": 151, + "targetY": 18, + "t": 603826 + }, + { + "targetX": 151, + "targetY": 19, + "t": 603873 + }, + { + "targetX": 152, + "targetY": 19, + "t": 603897 + }, + { + "targetX": 153, + "targetY": 19, + "t": 603954 + }, + { + "targetX": 153, + "targetY": 19, + "t": 603994 + }, + { + "targetX": 153, + "targetY": 20, + "t": 604076 + }, + { + "targetX": 153, + "targetY": 21, + "t": 604558 + }, + { + "targetX": 153, + "targetY": 22, + "t": 607143 + }, + { + "targetX": 153, + "targetY": 23, + "t": 607250 + }, + { + "targetX": 153, + "targetY": 23, + "t": 607276 + }, + { + "targetX": 153, + "targetY": 25, + "t": 607292 + }, + { + "targetX": 153, + "targetY": 26, + "t": 609118 + }, + { + "targetX": 153, + "targetY": 27, + "t": 609148 + }, + { + "targetX": 152, + "targetY": 27, + "t": 609176 + }, + { + "targetX": 152, + "targetY": 27, + "t": 609193 + }, + { + "targetX": 152, + "targetY": 28, + "t": 610651 + }, + { + "targetX": 152, + "targetY": 29, + "t": 610659 + }, + { + "targetX": 152, + "targetY": 30, + "t": 610676 + }, + { + "targetX": 152, + "targetY": 31, + "t": 610709 + }, + { + "targetX": 152, + "targetY": 31, + "t": 611036 + }, + { + "targetX": 152, + "targetY": 32, + "t": 611042 + }, + { + "targetX": 152, + "targetY": 33, + "t": 611059 + }, + { + "targetX": 152, + "targetY": 34, + "t": 611076 + }, + { + "targetX": 152, + "targetY": 35, + "t": 611092 + }, + { + "targetX": 152, + "targetY": 35, + "t": 611126 + }, + { + "targetX": 152, + "targetY": 36, + "t": 611142 + }, + { + "targetX": 152, + "targetY": 37, + "t": 611159 + }, + { + "targetX": 152, + "targetY": 38, + "t": 611192 + }, + { + "targetX": 151, + "targetY": 39, + "t": 611208 + }, + { + "targetX": 151, + "targetY": 40, + "t": 611226 + }, + { + "targetX": 151, + "targetY": 41, + "t": 611242 + }, + { + "targetX": 150, + "targetY": 43, + "t": 611275 + }, + { + "targetX": 150, + "targetY": 43, + "t": 611292 + }, + { + "targetX": 150, + "targetY": 45, + "t": 611309 + }, + { + "targetX": 149, + "targetY": 47, + "t": 611342 + }, + { + "targetX": 149, + "targetY": 47, + "t": 611359 + }, + { + "targetX": 149, + "targetY": 49, + "t": 611376 + }, + { + "targetX": 149, + "targetY": 51, + "t": 611409 + }, + { + "targetX": 149, + "targetY": 51, + "t": 611426 + }, + { + "targetX": 149, + "targetY": 52, + "t": 611442 + }, + { + "targetX": 149, + "targetY": 53, + "t": 611459 + }, + { + "targetX": 148, + "targetY": 55, + "t": 611475 + }, + { + "targetX": 148, + "targetY": 55, + "t": 611493 + }, + { + "targetX": 148, + "targetY": 56, + "t": 611509 + }, + { + "targetX": 147, + "targetY": 57, + "t": 611526 + }, + { + "targetX": 147, + "targetY": 59, + "t": 611543 + }, + { + "targetX": 146, + "targetY": 59, + "t": 611559 + }, + { + "targetX": 145, + "targetY": 60, + "t": 611576 + }, + { + "targetX": 145, + "targetY": 61, + "t": 611592 + }, + { + "targetX": 145, + "targetY": 63, + "t": 611609 + }, + { + "targetX": 144, + "targetY": 64, + "t": 611626 + }, + { + "targetX": 143, + "targetY": 65, + "t": 611642 + }, + { + "targetX": 141, + "targetY": 67, + "t": 611659 + }, + { + "targetX": 141, + "targetY": 68, + "t": 611676 + }, + { + "targetX": 139, + "targetY": 70, + "t": 611693 + }, + { + "targetX": 137, + "targetY": 71, + "t": 611709 + }, + { + "targetX": 136, + "targetY": 72, + "t": 611726 + }, + { + "targetX": 134, + "targetY": 74, + "t": 611743 + }, + { + "targetX": 133, + "targetY": 75, + "t": 611759 + }, + { + "targetX": 131, + "targetY": 76, + "t": 611776 + }, + { + "targetX": 130, + "targetY": 77, + "t": 611793 + }, + { + "targetX": 129, + "targetY": 78, + "t": 611809 + }, + { + "targetX": 127, + "targetY": 79, + "t": 611825 + }, + { + "targetX": 125, + "targetY": 79, + "t": 611842 + }, + { + "targetX": 123, + "targetY": 80, + "t": 611859 + }, + { + "targetX": 121, + "targetY": 81, + "t": 611876 + }, + { + "targetX": 121, + "targetY": 82, + "t": 611892 + }, + { + "targetX": 118, + "targetY": 82, + "t": 611909 + }, + { + "targetX": 117, + "targetY": 82, + "t": 611926 + }, + { + "targetX": 115, + "targetY": 82, + "t": 611942 + }, + { + "targetX": 113, + "targetY": 82, + "t": 611959 + }, + { + "targetX": 111, + "targetY": 82, + "t": 611975 + }, + { + "targetX": 109, + "targetY": 82, + "t": 611992 + }, + { + "targetX": 106, + "targetY": 81, + "t": 612009 + }, + { + "targetX": 102, + "targetY": 79, + "t": 612025 + }, + { + "targetX": 97, + "targetY": 78, + "t": 612042 + }, + { + "targetX": 92, + "targetY": 76, + "t": 612058 + }, + { + "targetX": 87, + "targetY": 75, + "t": 612076 + }, + { + "targetX": 83, + "targetY": 73, + "t": 612092 + }, + { + "targetX": 79, + "targetY": 72, + "t": 612109 + }, + { + "targetX": 75, + "targetY": 71, + "t": 612125 + }, + { + "targetX": 73, + "targetY": 71, + "t": 612142 + }, + { + "targetX": 71, + "targetY": 70, + "t": 612158 + }, + { + "targetX": 70, + "targetY": 70, + "t": 612175 + }, + { + "targetX": 69, + "targetY": 69, + "t": 612192 + }, + { + "targetX": 69, + "targetY": 68, + "t": 612244 + }, + { + "targetX": 68, + "targetY": 68, + "t": 612258 + }, + { + "targetX": 67, + "targetY": 68, + "t": 612276 + }, + { + "targetX": 66, + "targetY": 67, + "t": 612292 + }, + { + "targetX": 65, + "targetY": 67, + "t": 612309 + } + ], + "wasCancelled": false } - ], - "isFromTouch": false - }, - "terminationEvent": "end" + } + ] } ], "config": { diff --git a/common/web/gesture-recognizer/src/test/resources/json/popupShimCancel.json b/common/web/gesture-recognizer/src/test/resources/json/popupShimCancel.json index 764f91e9d2..8e90d8c142 100644 --- a/common/web/gesture-recognizer/src/test/resources/json/popupShimCancel.json +++ b/common/web/gesture-recognizer/src/test/resources/json/popupShimCancel.json @@ -1,157 +1,161 @@ { - "set": [ + "inputs": [ { - "sequence": { - "samples": [ - { - "targetX": 68, - "targetY": 22, - "t": 511067 - }, - { - "targetX": 67, - "targetY": 22, - "t": 511180 - }, - { - "targetX": 65, - "targetY": 24, - "t": 511211 - }, - { - "targetX": 63, - "targetY": 25, - "t": 511228 - }, - { - "targetX": 62, - "targetY": 25, - "t": 511244 - }, - { - "targetX": 58, - "targetY": 26, - "t": 511261 - }, - { - "targetX": 55, - "targetY": 26, - "t": 511277 - }, - { - "targetX": 51, - "targetY": 27, - "t": 511294 - }, - { - "targetX": 46, - "targetY": 28, - "t": 511310 - }, - { - "targetX": 42, - "targetY": 29, - "t": 511327 - }, - { - "targetX": 38, - "targetY": 29, - "t": 511345 - }, - { - "targetX": 34, - "targetY": 29, - "t": 511361 - }, - { - "targetX": 30, - "targetY": 29, - "t": 511377 - }, - { - "targetX": 25, - "targetY": 29, - "t": 511394 - }, - { - "targetX": 20, - "targetY": 29, - "t": 511411 - }, - { - "targetX": 15, - "targetY": 29, - "t": 511427 - }, - { - "targetX": 10, - "targetY": 29, - "t": 511444 - }, - { - "targetX": 5, - "targetY": 29, - "t": 511460 - }, - { - "targetX": 2, - "targetY": 28, - "t": 511478 - }, - { - "targetX": -2, - "targetY": 28, - "t": 511494 - }, - { - "targetX": -5, - "targetY": 28, - "t": 511511 - }, - { - "targetX": -7, - "targetY": 28, - "t": 511527 - }, - { - "targetX": -9, - "targetY": 28, - "t": 511544 - }, - { - "targetX": -11, - "targetY": 28, - "t": 511561 - }, - { - "targetX": -14, - "targetY": 28, - "t": 511578 - }, - { - "targetX": -18, - "targetY": 28, - "t": 511595 - }, - { - "targetX": -21, - "targetY": 28, - "t": 511611 - }, - { - "targetX": -25, - "targetY": 28, - "t": 511628 - }, - { - "targetX": -26, - "targetY": 28, - "t": 511644 + "touchpoints": [ + { + "isFromTouch": false, + "path": { + "coords": [ + { + "targetX": 68, + "targetY": 22, + "t": 511067 + }, + { + "targetX": 67, + "targetY": 22, + "t": 511180 + }, + { + "targetX": 65, + "targetY": 24, + "t": 511211 + }, + { + "targetX": 63, + "targetY": 25, + "t": 511228 + }, + { + "targetX": 62, + "targetY": 25, + "t": 511244 + }, + { + "targetX": 58, + "targetY": 26, + "t": 511261 + }, + { + "targetX": 55, + "targetY": 26, + "t": 511277 + }, + { + "targetX": 51, + "targetY": 27, + "t": 511294 + }, + { + "targetX": 46, + "targetY": 28, + "t": 511310 + }, + { + "targetX": 42, + "targetY": 29, + "t": 511327 + }, + { + "targetX": 38, + "targetY": 29, + "t": 511345 + }, + { + "targetX": 34, + "targetY": 29, + "t": 511361 + }, + { + "targetX": 30, + "targetY": 29, + "t": 511377 + }, + { + "targetX": 25, + "targetY": 29, + "t": 511394 + }, + { + "targetX": 20, + "targetY": 29, + "t": 511411 + }, + { + "targetX": 15, + "targetY": 29, + "t": 511427 + }, + { + "targetX": 10, + "targetY": 29, + "t": 511444 + }, + { + "targetX": 5, + "targetY": 29, + "t": 511460 + }, + { + "targetX": 2, + "targetY": 28, + "t": 511478 + }, + { + "targetX": -2, + "targetY": 28, + "t": 511494 + }, + { + "targetX": -5, + "targetY": 28, + "t": 511511 + }, + { + "targetX": -7, + "targetY": 28, + "t": 511527 + }, + { + "targetX": -9, + "targetY": 28, + "t": 511544 + }, + { + "targetX": -11, + "targetY": 28, + "t": 511561 + }, + { + "targetX": -14, + "targetY": 28, + "t": 511578 + }, + { + "targetX": -18, + "targetY": 28, + "t": 511595 + }, + { + "targetX": -21, + "targetY": 28, + "t": 511611 + }, + { + "targetX": -25, + "targetY": 28, + "t": 511628 + }, + { + "targetX": -26, + "targetY": 28, + "t": 511644 + } + ], + "wasCancelled": true } - ], - "isFromTouch": false - }, - "terminationEvent": "cancel" + } + ] } ], "config": { From 2fc61e10f441961c9323e0373a1b528513829452 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Wed, 27 Jul 2022 08:21:59 +0700 Subject: [PATCH 20/29] chore(web): removes recording-migration gerry-rig --- .../browser/cases/recordedCoordSequences.js | 46 +++---------------- 1 file changed, 6 insertions(+), 40 deletions(-) diff --git a/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js b/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js index 9afda22bd4..d4d9354916 100644 --- a/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js +++ b/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js @@ -26,12 +26,7 @@ describe("Layer one - DOM -> InputSequence", function() { let playbackEngine = new Testing.InputSequenceSimulator(this.controller); let result = playbackEngine.replay(testObj); - // TEMP - if(testObj.set) { - assert.equal(result.inputs.length, testObj.set.length); - } else { - assert.equal(result.inputs.length, testObj.inputs.length); - } + assert.equal(result.inputs.length, testObj.inputs.length); // Removes the timestamp element; we know that this component won't match, as the playback engine // doesn't care about it. @@ -39,28 +34,11 @@ describe("Layer one - DOM -> InputSequence", function() { // Returns just the observed, cleaned samples for a sequence object. The recorded coordinates // should match perfectly. - let seqCleaner = (seq) => { - if(seq.sequence) { - // Old style - return seq.sequence.samples.map(sampleCleaner); - } else { - // New style - return seq.touchpoints[0].path.coords.map(sampleCleaner); - } + let seqCleaner = (input) => { + return input.touchpoints[0].path.coords.map(sampleCleaner); }; - let cleanOriginalSet; - // TEMP - if(testObj.set) { - cleanOriginalSet = testObj.set.map(seqCleaner); - } else { - cleanOriginalSet = testObj.inputs.map(seqCleaner); - } - - // TEMP STATEMENT - logs the new JSON format for old-format JSON recordings. - if(testObj.set) { - console.log(JSON.stringify(result, null, 2)); - } + let cleanOriginalSet = testObj.inputs.map(seqCleaner); let cleanResultSet = result .inputs.map(seqCleaner); expect(cleanResultSet).to.deep.equal(cleanOriginalSet); @@ -68,21 +46,10 @@ describe("Layer one - DOM -> InputSequence", function() { // The 'terminationEvent' property should match. Any sequence that was "canceled" should still // cancel; that's a pretty critical detail! let terminationEventMapper = (seq) => { - if(seq.terminationEvent) { - // Old form - return seq.terminationEvent == 'cancel'; - } else { - // New form - return seq.touchpoints[0].path.wasCancelled; - } + return seq.touchpoints[0].path.wasCancelled; } - // TEMP - if(testObj.set) { - expect(result.inputs.map(terminationEventMapper)).to.deep.equal(testObj.set.map(terminationEventMapper)); - } else { - expect(result.inputs.map(terminationEventMapper)).to.deep.equal(testObj.inputs.map(terminationEventMapper)); - } + expect(result.inputs.map(terminationEventMapper)).to.deep.equal(testObj.inputs.map(terminationEventMapper)); } // List all relevant fixtures in src/test/resources/json. @@ -104,7 +71,6 @@ describe("Layer one - DOM -> InputSequence", function() { let testObj = __json__[recordingID]; // 'describe' has a notably different `this` reference than `it`, `before`, etc. - console.log(`${recordingID}.json`); replayAndCompare.call(this, testObj); }); } From 7fa7d581b37c033a11518418ff97c42b6d207ace Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Fri, 29 Jul 2022 12:43:24 +0700 Subject: [PATCH 21/29] chore(web): build & test script updates --- common/web/gesture-recognizer/build.sh | 9 +- .../web/gesture-recognizer/src/test/test.sh | 103 ++++++------------ 2 files changed, 42 insertions(+), 70 deletions(-) diff --git a/common/web/gesture-recognizer/build.sh b/common/web/gesture-recognizer/build.sh index 41f94271a0..f41a266751 100755 --- a/common/web/gesture-recognizer/build.sh +++ b/common/web/gesture-recognizer/build.sh @@ -25,7 +25,8 @@ builder_describe "Builds the gesture-recognition model for Web-based on-screen k "build" \ "test" \ ":module" \ - ":tools tools for testing & developing test resources for this module" + ":tools tools for testing & developing test resources for this module" \ + "--ci sets the --ci option for child scripts (i.e, the `test` action)" builder_parse "$@" @@ -60,6 +61,10 @@ if builder_has_action build :tools; then fi if builder_has_action test :module; then - npm test + if builder_has_option --ci; then + npm test -- --ci + else + npm test + fi builder_report success test :module fi \ No newline at end of file diff --git a/common/web/gesture-recognizer/src/test/test.sh b/common/web/gesture-recognizer/src/test/test.sh index 1dbb600cd9..9db8acc079 100755 --- a/common/web/gesture-recognizer/src/test/test.sh +++ b/common/web/gesture-recognizer/src/test/test.sh @@ -12,25 +12,35 @@ THIS_SCRIPT="$(greadlink -f "${BASH_SOURCE[0]}" 2>/dev/null || readlink -f "${BA . "$(dirname "$THIS_SCRIPT")/../../../../../resources/build/build-utils.sh" ## END STANDARD BUILD SCRIPT INCLUDE +. $KEYMAN_ROOT/resources/shellHelperFunctions.sh + # This script runs from its own folder cd "$(dirname $THIS_SCRIPT)" -display_usage ( ) { - echo "Usage: $0 [configure] [clean] [build] [test]" - echo " [--verbose|-v]" - echo " $0 -h|--help" - echo - echo " headless runs all headless unit tests (CI or local)" - echo " browser runs all browser-based unit tests in CI mode" - echo " all runs all unit test sets in CI mode" - echo " ci runs selected unit tests via BrowserStack with CI-oriented reporting" - echo " local runs selected unit test sets locally with local-oriented reporting" - echo " browser-debug runs all browser-based unit tests in persistent local mode." -} +################################ Main script ################################ -test-headless ( ) { - echo "Headless unit tests for this module have not yet been defined." -} +builder_describe "Runs all tests for the gesture-recognizer module" \ + "test+" \ + ":headless Runs headless user tests" \ + ":browser Runs browser-based user tests" \ + "--ci Uses CI-based test configurations & emits CI-friendly test reports" \ + "--debug Activates developer-friendly debug mode for unit tests where applicable" + +builder_parse "$@" + +# TODO: build if out-of-date if test is specified +# TODO: configure if npm has not been run, and build is specified + +# START - Script parameter configuration +REPORT_STYLE="local" # Default setting. + +if builder_has_option --ci; then + REPORT_STYLE="ci" + + echo "Replacing user-friendly test reports & configurations with CI-friendly versions." +fi + +# END - Script parameter configuration test-browser ( ) { KARMA_FLAGS= @@ -46,61 +56,18 @@ test-browser ( ) { npm run karma -- start src/test/auto/browser/$KARMA_CONFIG "$KARMA_FLAGS" } -################################ Main script ################################ - -# Yes, this is a test script, not a build script. But why not use the new -# style anyway? - -builder_init "headless browser all ci local browser-debug" "$@" - -# TODO: build if out-of-date if test is specified -# TODO: configure if npm has not been run, and build is specified - -# START - Script parameter configuration - -if builder_has_action build >/dev/null; then - builder_chosen_actions+=("all"); +if builder_has_action test :headless; then + echo "Headless unit tests for this module have not yet been defined." + builder_report success test :headless fi -if builder_has_action all >/dev/null; then - builder_chosen_actions+=("headless"); - builder_chosen_actions+=("browser"); -fi - -if builder_has_action all-local >/dev/null; then - builder_chosen_actions+=("headless"); - builder_chosen_actions+=("browser"); -fi - -if builder_has_action local >/dev/null; then - REPORT_STYLE="local" -elif builder_has_action ci >/dev/null; then - REPORT_STYLE="ci" -else - # Attempt a TC-auto-detect. - echo "Auto-detecting optimal testing style..." - if [ ! -z "${TEAMCITY_VERSION-}" ]; then - echo "TeamCity detected: running in CI mode." - builder_chosen_actions+=("ci"); - REPORT_STYLE="ci" +if builder_has_action test :browser; then + if builder_has_option --debug; then + echo "Running browser-based unit tests in debug-mode configuration..." + echo "${COLOR_YELLOW}You must manually terminate this mode (CTRL-C) for the script to exit.${COLOR_RESET}" + test-browser debug else - echo "TeamCity not detected: running in local mode." - builder_chosen_actions+=("local"); - REPORT_STYLE="local" + test-browser fi -fi - -# END - Script parameter configuration - -if builder_has_action headless; then - test-headless - builder_report headless success -fi - -if builder_has_action browser-debug; then - test-browser debug - builder_report browser-debug success -elif builder_has_action browser; then - test-browser - builder_report browser success + builder_report success test :browser fi \ No newline at end of file From f21f85d9e74ce2ab4cbbe77527f4f74daa01f15a Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Fri, 29 Jul 2022 12:45:39 +0700 Subject: [PATCH 22/29] chore(web): addresses nit from PR review --- common/web/gesture-recognizer/src/test/auto/browser/CI.conf.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/common/web/gesture-recognizer/src/test/auto/browser/CI.conf.js b/common/web/gesture-recognizer/src/test/auto/browser/CI.conf.js index 9438873b3a..1e1cdd14cd 100644 --- a/common/web/gesture-recognizer/src/test/auto/browser/CI.conf.js +++ b/common/web/gesture-recognizer/src/test/auto/browser/CI.conf.js @@ -86,8 +86,6 @@ module.exports = function(config) { } } - // Sadly, legacy IE isn't very testable with Mocha. One of its dependencies requires a feature that is IE11+. - /* * Final selection of the sets to be used for BrowserStack testing. */ From 57630b5574e4ccbd1e2e83eb5071e95e0d8cd9d2 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Mon, 1 Aug 2022 07:53:37 +0700 Subject: [PATCH 23/29] chore(web): minor test-script tweaks post demo --- common/web/gesture-recognizer/src/test/test.sh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/common/web/gesture-recognizer/src/test/test.sh b/common/web/gesture-recognizer/src/test/test.sh index 9db8acc079..d923098928 100755 --- a/common/web/gesture-recognizer/src/test/test.sh +++ b/common/web/gesture-recognizer/src/test/test.sh @@ -21,10 +21,10 @@ cd "$(dirname $THIS_SCRIPT)" builder_describe "Runs all tests for the gesture-recognizer module" \ "test+" \ - ":headless Runs headless user tests" \ - ":browser Runs browser-based user tests" \ - "--ci Uses CI-based test configurations & emits CI-friendly test reports" \ - "--debug Activates developer-friendly debug mode for unit tests where applicable" + ":headless Runs headless user tests" \ + ":browser Runs browser-based user tests" \ + "--ci Uses CI-based test configurations & emits CI-friendly test reports" \ + "--debug,-d Activates developer-friendly debug mode for unit tests where applicable" builder_parse "$@" @@ -64,7 +64,9 @@ fi if builder_has_action test :browser; then if builder_has_option --debug; then echo "Running browser-based unit tests in debug-mode configuration..." + echo echo "${COLOR_YELLOW}You must manually terminate this mode (CTRL-C) for the script to exit.${COLOR_RESET}" + sleep 2 test-browser debug else test-browser From 48d9f5b4b596dec8f0a447efdffb18d39ccc8a75 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Wed, 3 Aug 2022 08:28:05 +0700 Subject: [PATCH 24/29] change(web): feature branch use of centralized CI config, timeout-prep module --- .../src/test/auto/browser/CI.conf.js | 143 +----------------- .../src/test/auto/browser/base.conf.js | 2 +- .../test/auto/browser/timeoutConfiguration.js | 21 --- 3 files changed, 4 insertions(+), 162 deletions(-) delete mode 100644 common/web/gesture-recognizer/src/test/auto/browser/timeoutConfiguration.js diff --git a/common/web/gesture-recognizer/src/test/auto/browser/CI.conf.js b/common/web/gesture-recognizer/src/test/auto/browser/CI.conf.js index 1e1cdd14cd..dcb282763e 100644 --- a/common/web/gesture-recognizer/src/test/auto/browser/CI.conf.js +++ b/common/web/gesture-recognizer/src/test/auto/browser/CI.conf.js @@ -1,141 +1,4 @@ -module.exports = function(config) { - var base = require("./base.conf.js"); +var BASE_CONFIG = require("./base.conf.js"); +var ci_config_adapter = require("../../../../../../../common/test/resources/karma-browserstack-config.js"); - var timeouts = base.client.args[0]; - var browserStackModifier = 10; - - for(key in timeouts) { - if(typeof timeouts[key] == 'number') { - timeouts[key] = timeouts[key] * browserStackModifier; - } - } - - /* - * Definition of utility functions for managing our browser lists. - */ - var mergeLaunchers = function() { - var mergedDefs = {}; - var i; - for(i=0; i < arguments.length; i++) { - for(var name in arguments[i]) { - mergedDefs[name] = arguments[i][name]; - - // Necessary for Karma to process it properly. - mergedDefs[name].base = 'BrowserStack'; - } - } - - return mergedDefs; - } - - var toBrowserList = function(mergedSet) { - var list = []; - for(var name in mergedSet) { - list.push(name); - } - - return list; - } - - /* - * Definition of browser sets possibly relevant for testing. - */ - var CURRENT_MAC_LAUNCHERS = { - bs_firefox_mac: { - browser: 'firefox', - browser_version: '100', - os: 'OS X', - os_version: 'Mojave' - }, - bs_safari_mac_m: { - browser: 'safari', - browser_version: '13', - os: 'OS X', - os_version: 'Catalina' - }, - bs_chrome_mac: { - browser: 'chrome', - browser_version: '70.0', - os: 'OS X', - os_version: 'Mojave' - } - }; - - var CURRENT_WIN_LAUNCHERS = { - bs_firefox_win: { - os: 'Windows', - os_version: '10', - browser: 'firefox', - browser_version: '100.0' - }, - bs_chrome_win: { - os: 'Windows', - os_version: '10', - browser: 'chrome', - browser_version: '70.0' - } - } - - var CURRENT_ANDROID_LAUNCHERS = { - bs_chrome_android: { - os: 'android', - os_version: '7.1', - browser: 'chrome', - real_mobile: true, - device: 'Samsung Galaxy Note 8' - } - } - - /* - * Final selection of the sets to be used for BrowserStack testing. - */ - var FINAL_LAUNCHER_DEFS = mergeLaunchers( CURRENT_ANDROID_LAUNCHERS, - CURRENT_WIN_LAUNCHERS, - CURRENT_MAC_LAUNCHERS); - - var FINAL_BROWSER_LIST = toBrowserList(FINAL_LAUNCHER_DEFS); - - /* - * Final definition of our BrowserStack testing Karma configuration. - */ - - var specifics = { - // BrowserStack configuration options - browserStack: { - video: true, - browserDisconnectTimeout: 6e4, // 1 minute (60s => 60,000ms) - retryLimit: 3, // 0 is ignored. - startTunnel: true, - }, - - // Attempts to avoid generating a 'fail' exit code if one of our selected browsers on BrowserStack goes poof. - failOnEmptyTestSuite: false, - - captureTimeout: 1.2e5, // in milliseconds - browserNoActivityTimeout: 6e4, - browserDisconnectTimeout: 6e4, - browserDisconnectTolerance: 3, - - // Avoids generating a 'fail' exit code if one of our selected browsers on BrowserStack goes poof. - failOnEmptyTestSuite: false, - - // test results reporter to use - // possible values: 'dots', 'progress' - // available reporters: https://npmjs.org/browse/keyword/karma-reporter - reporters: ['teamcity', 'BrowserStack'], - - // level of logging - // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG - logLevel: config.LOG_DEBUG, - - // Concurrency level - // For CI, it really helps to keep a nice, clean set of output logs. - concurrency: 5, - - customLaunchers: FINAL_LAUNCHER_DEFS, - - browsers: FINAL_BROWSER_LIST - }; - - config.set(Object.assign(specifics, base)); -} +module.exports = ci_config_adapter(BASE_CONFIG, "partial"); \ No newline at end of file diff --git a/common/web/gesture-recognizer/src/test/auto/browser/base.conf.js b/common/web/gesture-recognizer/src/test/auto/browser/base.conf.js index d6c172d498..b1c9bf1d6f 100644 --- a/common/web/gesture-recognizer/src/test/auto/browser/base.conf.js +++ b/common/web/gesture-recognizer/src/test/auto/browser/base.conf.js @@ -43,7 +43,7 @@ module.exports = { '../../../node_modules/sinon/pkg/sinon.js', - 'src/test/auto/browser/timeoutConfiguration.js', // Handles timeout configuration for local vs BrowserStack-based testing + '../../../common/test/resources/timeout-adapter.js', // Handles timeout configuration for local vs BrowserStack-based testing 'src/test/auto/browser/cases/**/*.js', // Where the tests actually reside. diff --git a/common/web/gesture-recognizer/src/test/auto/browser/timeoutConfiguration.js b/common/web/gesture-recognizer/src/test/auto/browser/timeoutConfiguration.js deleted file mode 100644 index 75629fd8ec..0000000000 --- a/common/web/gesture-recognizer/src/test/auto/browser/timeoutConfiguration.js +++ /dev/null @@ -1,21 +0,0 @@ -// Preprocessing of the Karma configuration's client.args parameter. - -com = com || {}; -com.keyman = com.keyman || {}; -com.keyman.karma = com.keyman.karma || {}; - -(function() { - var kmwconfig = window['testconfig'] = {}; - - var configArgs = window['__karma__'].config.args; // Where Karma gives us our custom args. - for(var i = 0; i < configArgs.length; configArgs++) { - switch(configArgs[i].type) { - case 'timeouts': - var timeouts = JSON.parse(JSON.stringify(configArgs[i])); - delete timeouts.type; - - kmwconfig['timeouts'] = timeouts; - break; - } - } -})(); From 23be4f2bac1351eba77de95f869b724886f940da Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Wed, 3 Aug 2022 09:14:20 +0700 Subject: [PATCH 25/29] chore(web): missed a test.sh tweak post-merges --- web/unit_tests/test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/unit_tests/test.sh b/web/unit_tests/test.sh index ac0173b2fd..a831b3c09d 100755 --- a/web/unit_tests/test.sh +++ b/web/unit_tests/test.sh @@ -136,7 +136,7 @@ popd # For now, we'll also link in the gesture-recognizer unit tests here. echo_heading "Running gesture-recognizer test suite" pushd "$KEYMAN_ROOT/common/web/gesture-recognizer" -./build.sh tools test +./build.sh test popd # Once done, now we run the integrated (KeymanWeb) tests. From 0b6c0079bc7752ba98b1259f6fcf27d0e34b7766 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Wed, 3 Aug 2022 09:38:33 +0700 Subject: [PATCH 26/29] fix(web): forgot to pass --ci flag --- web/unit_tests/test.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/web/unit_tests/test.sh b/web/unit_tests/test.sh index a831b3c09d..c10ea639a8 100755 --- a/web/unit_tests/test.sh +++ b/web/unit_tests/test.sh @@ -70,6 +70,7 @@ get_OS get_browser_set_for_OS CONFIG=manual.conf.js # TODO - get/make OS-specific version +SH_FLAGS= DEBUG=false FLAGS= HEADLESS_FLAGS=-skip-package-install @@ -81,6 +82,7 @@ while [[ $# -gt 0 ]] ; do -CI) CONFIG=CI.conf.js HEADLESS_FLAGS="$HEADLESS_FLAGS -CI" + SH_FLAGS="--ci" ;; -log-level) shift @@ -136,7 +138,7 @@ popd # For now, we'll also link in the gesture-recognizer unit tests here. echo_heading "Running gesture-recognizer test suite" pushd "$KEYMAN_ROOT/common/web/gesture-recognizer" -./build.sh test +npm run test -- $SH_FLAGS popd # Once done, now we run the integrated (KeymanWeb) tests. From 8268cf5f35be8363c0b53f042ba4324314c24431 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Wed, 3 Aug 2022 10:33:24 +0700 Subject: [PATCH 27/29] fix(web): missing setup on 'partial' test set --- common/test/resources/karma-browserstack-config.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/common/test/resources/karma-browserstack-config.js b/common/test/resources/karma-browserstack-config.js index a6b3c95662..b65b46d104 100644 --- a/common/test/resources/karma-browserstack-config.js +++ b/common/test/resources/karma-browserstack-config.js @@ -107,7 +107,10 @@ module.exports = function(baseConfigParams /* the project's base configuration FINAL_LAUNCHER_DEFS = mergeLaunchers( PRIMARY_LAUNCHERS, SECONDARY_LAUNCHERS); } else { - FINAL_LAUNCHER_DEFS = PRIMARY_LAUNCHERS; + // The function also does some necessary bookkeeping that makes for + // cleaner definitions at the file's top, so we call it even if we + // aren't merging lists. + FINAL_LAUNCHER_DEFS = mergeLaunchers(PRIMARY_LAUNCHERS); } var FINAL_BROWSER_LIST = toBrowserList(FINAL_LAUNCHER_DEFS); From 2dbca5286a09329ef53725af9657e6509b223d56 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Wed, 3 Aug 2022 14:00:20 +0700 Subject: [PATCH 28/29] chore(web): updates SinonJS (old one lacks runAllAsync) --- common/predictive-text/package.json | 2 +- common/web/gesture-recognizer/package.json | 3 +- .../src/inputSequenceSimulator.ts | 2 +- common/web/lm-worker/package.json | 2 +- package-lock.json | 290 ++++++++---------- 5 files changed, 130 insertions(+), 169 deletions(-) diff --git a/common/predictive-text/package.json b/common/predictive-text/package.json index 81a92a4fa9..d7c12767ad 100644 --- a/common/predictive-text/package.json +++ b/common/predictive-text/package.json @@ -39,7 +39,7 @@ "karma-teamcity-reporter": "^1.1.0", "mocha": "^8.4.0", "mocha-teamcity-reporter": "^4.0.0", - "sinon": "^7.1.1", + "sinon": "^14.0.0", "ts-node": "^9.1.1", "typescript": "^4.5.4" }, diff --git a/common/web/gesture-recognizer/package.json b/common/web/gesture-recognizer/package.json index 290398abee..e00cbd7dc3 100644 --- a/common/web/gesture-recognizer/package.json +++ b/common/web/gesture-recognizer/package.json @@ -4,6 +4,7 @@ "devDependencies": { "@keymanapp/resources-gosh": "*", "@types/node": "^11.9.4", + "@types/sinon": "^10.0.13", "chai": "^4.3.4", "karma": "^6.3.4", "karma-browserstack-launcher": "^1.6.0", @@ -20,7 +21,7 @@ "karma-teamcity-reporter": "^1.1.0", "mocha": "^8.4.0", "mocha-teamcity-reporter": "^4.0.0", - "sinon": "^7.1.1", + "sinon": "^14.0.0", "ts-node": "^9.1.1", "typescript": "^4.5.4" }, diff --git a/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts b/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts index f0ff62e1f2..1fcbdaa50a 100644 --- a/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts +++ b/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts @@ -227,7 +227,7 @@ namespace Testing { } if(indexInSequence == 0) { - state = "start" + state = "start"; } const sample = touchpoint.path.coords[indexInSequence]; diff --git a/common/web/lm-worker/package.json b/common/web/lm-worker/package.json index f0ebe95036..753948495a 100644 --- a/common/web/lm-worker/package.json +++ b/common/web/lm-worker/package.json @@ -31,7 +31,7 @@ "karma-teamcity-reporter": "^1.1.0", "mocha": "^8.4.0", "mocha-teamcity-reporter": "^4.0.0", - "sinon": "^7.1.1", + "sinon": "^14.0.0", "ts-node": "^9.1.1", "typescript": "^4.5.4" }, diff --git a/package-lock.json b/package-lock.json index 8975479db6..0149000800 100644 --- a/package-lock.json +++ b/package-lock.json @@ -199,7 +199,7 @@ "karma-teamcity-reporter": "^1.1.0", "mocha": "^8.4.0", "mocha-teamcity-reporter": "^4.0.0", - "sinon": "^7.1.1", + "sinon": "^14.0.0", "ts-node": "^9.1.1", "typescript": "^4.5.4" } @@ -221,18 +221,30 @@ }, "common/web/gesture-recognizer": { "name": "@keymanapp/gesture-recognizer", - "license": "MIT", "dependencies": { - "@keymanapp/keyman-version": "*", - "@keymanapp/web-utils": "*", "eventemitter3": "^4.0.0" }, "devDependencies": { "@keymanapp/resources-gosh": "*", "@types/node": "^11.9.4", + "@types/sinon": "^10.0.13", "chai": "^4.3.4", + "karma": "^6.3.4", + "karma-browserstack-launcher": "^1.6.0", + "karma-chai": "^0.1.0", + "karma-chrome-launcher": "^2.2.0", + "karma-firefox-launcher": "^1.1.0", + "karma-fixture": "^0.2.6", + "karma-html2js-preprocessor": "^1.1.0", + "karma-ie-launcher": "^1.0.0", + "karma-json-fixtures-preprocessor": "0.0.6", + "karma-mocha": "^2.0.1", + "karma-mocha-reporter": "^2.2.5", + "karma-safari-launcher": "^1.0.0", + "karma-teamcity-reporter": "^1.1.0", "mocha": "^8.4.0", "mocha-teamcity-reporter": "^4.0.0", + "sinon": "^14.0.0", "ts-node": "^9.1.1", "typescript": "^4.5.4" } @@ -341,7 +353,7 @@ "karma-teamcity-reporter": "^1.1.0", "mocha": "^8.4.0", "mocha-teamcity-reporter": "^4.0.0", - "sinon": "^7.1.1", + "sinon": "^14.0.0", "ts-node": "^9.1.1", "typescript": "^4.5.4" } @@ -1496,31 +1508,30 @@ "type-detect": "4.0.8" } }, - "node_modules/@sinonjs/formatio": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.2.tgz", - "integrity": "sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ==", + "node_modules/@sinonjs/fake-timers": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", + "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", "dev": true, "dependencies": { - "@sinonjs/commons": "^1", - "@sinonjs/samsam": "^3.1.0" + "@sinonjs/commons": "^1.7.0" } }, "node_modules/@sinonjs/samsam": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz", - "integrity": "sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.1.1.tgz", + "integrity": "sha512-cZ7rKJTLiE7u7Wi/v9Hc2fs3Ucc3jrWeMgPHbbTCeVAB2S0wOBbYlkJVeNSL04i7fdhT8wIbDq1zhC/PXTD2SA==", "dev": true, "dependencies": { - "@sinonjs/commons": "^1.3.0", - "array-from": "^2.1.1", - "lodash": "^4.17.15" + "@sinonjs/commons": "^1.6.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" } }, "node_modules/@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", "dev": true }, "node_modules/@socket.io/base64-arraybuffer": { @@ -1724,6 +1735,21 @@ "@types/node": "*" } }, + "node_modules/@types/sinon": { + "version": "10.0.13", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.13.tgz", + "integrity": "sha512-UVjDqJblVNQYvVNUsj0PuYYw0ELRmgt1Nt5Vk0pT5f16ROGfcKJY8o1HVuMOJOpD727RrGB9EGvoaTQE5tgxZQ==", + "dev": true, + "dependencies": { + "@types/sinonjs__fake-timers": "*" + } + }, + "node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz", + "integrity": "sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA==", + "dev": true + }, "node_modules/@types/ws": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", @@ -1916,12 +1942,6 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, - "node_modules/array-from": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", - "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", - "dev": true - }, "node_modules/asn1": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", @@ -4393,12 +4413,6 @@ } } }, - "node_modules/lolex": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-4.2.0.tgz", - "integrity": "sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg==", - "dev": true - }, "node_modules/lowercase-keys": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", @@ -4952,33 +4966,24 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, "node_modules/nise": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.3.tgz", - "integrity": "sha512-Ymbac/94xeIrMf59REBPOv0thr+CJVFMhrlAkW/gjCIE58BGQdCj0x7KRCb3yz+Ga2Rz3E9XXSvUyyxqqhjQAQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.1.tgz", + "integrity": "sha512-yr5kW2THW1AkxVmCnKEh4nbYkJdB3I7LUkiUgOvEkOp414mc2UMaHMA7pjq1nYowhdoJZGwEKGaQVbxfpWj10A==", "dev": true, "dependencies": { - "@sinonjs/formatio": "^3.2.1", + "@sinonjs/commons": "^1.8.3", + "@sinonjs/fake-timers": ">=5", "@sinonjs/text-encoding": "^0.7.1", "just-extend": "^4.0.2", - "lolex": "^5.0.1", "path-to-regexp": "^1.7.0" } }, "node_modules/nise/node_modules/isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", "dev": true }, - "node_modules/nise/node_modules/lolex": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", - "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.7.0" - } - }, "node_modules/nise/node_modules/path-to-regexp": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", @@ -5842,48 +5847,21 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/sinon": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.5.0.tgz", - "integrity": "sha512-AoD0oJWerp0/rY9czP/D6hDTTUYGpObhZjMpd7Cl/A6+j0xBE+ayL/ldfggkBXUs0IkvIiM1ljM8+WkOc5k78Q==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-14.0.0.tgz", + "integrity": "sha512-ugA6BFmE+WrJdh0owRZHToLd32Uw3Lxq6E6LtNRU+xTVBefx632h03Q7apXWRsRdZAJ41LB8aUfn2+O4jsDNMw==", "dev": true, "dependencies": { - "@sinonjs/commons": "^1.4.0", - "@sinonjs/formatio": "^3.2.1", - "@sinonjs/samsam": "^3.3.3", - "diff": "^3.5.0", - "lolex": "^4.2.0", - "nise": "^1.5.2", - "supports-color": "^5.5.0" - } - }, - "node_modules/sinon/node_modules/diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/sinon/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/sinon/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" + "@sinonjs/commons": "^1.8.3", + "@sinonjs/fake-timers": "^9.1.2", + "@sinonjs/samsam": "^6.1.1", + "diff": "^5.0.0", + "nise": "^5.1.1", + "supports-color": "^7.2.0" }, - "engines": { - "node": ">=4" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" } }, "node_modules/socket.io": { @@ -7587,14 +7565,27 @@ "@keymanapp/gesture-recognizer": { "version": "file:common/web/gesture-recognizer", "requires": { - "@keymanapp/keyman-version": "*", "@keymanapp/resources-gosh": "*", - "@keymanapp/web-utils": "*", "@types/node": "^11.9.4", + "@types/sinon": "^10.0.13", "chai": "^4.3.4", "eventemitter3": "^4.0.0", + "karma": "^6.3.4", + "karma-browserstack-launcher": "^1.6.0", + "karma-chai": "^0.1.0", + "karma-chrome-launcher": "^2.2.0", + "karma-firefox-launcher": "^1.1.0", + "karma-fixture": "^0.2.6", + "karma-html2js-preprocessor": "^1.1.0", + "karma-ie-launcher": "^1.0.0", + "karma-json-fixtures-preprocessor": "0.0.6", + "karma-mocha": "^2.0.1", + "karma-mocha-reporter": "^2.2.5", + "karma-safari-launcher": "^1.0.0", + "karma-teamcity-reporter": "^1.1.0", "mocha": "^8.4.0", "mocha-teamcity-reporter": "^4.0.0", + "sinon": "^14.0.0", "ts-node": "^9.1.1", "typescript": "^4.5.4" } @@ -7752,7 +7743,7 @@ "karma-teamcity-reporter": "^1.1.0", "mocha": "^8.4.0", "mocha-teamcity-reporter": "^4.0.0", - "sinon": "^7.1.1", + "sinon": "^14.0.0", "string.prototype.codepointat": "^0.2.1", "ts-node": "^9.1.1", "typescript": "^4.5.4" @@ -7800,7 +7791,7 @@ "karma-teamcity-reporter": "^1.1.0", "mocha": "^8.4.0", "mocha-teamcity-reporter": "^4.0.0", - "sinon": "^7.1.1", + "sinon": "^14.0.0", "string.prototype.codepointat": "^0.2.1", "string.prototype.startswith": "^0.2.0", "ts-node": "^9.1.1", @@ -8321,31 +8312,30 @@ "type-detect": "4.0.8" } }, - "@sinonjs/formatio": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.2.tgz", - "integrity": "sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ==", + "@sinonjs/fake-timers": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", + "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", "dev": true, "requires": { - "@sinonjs/commons": "^1", - "@sinonjs/samsam": "^3.1.0" + "@sinonjs/commons": "^1.7.0" } }, "@sinonjs/samsam": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz", - "integrity": "sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.1.1.tgz", + "integrity": "sha512-cZ7rKJTLiE7u7Wi/v9Hc2fs3Ucc3jrWeMgPHbbTCeVAB2S0wOBbYlkJVeNSL04i7fdhT8wIbDq1zhC/PXTD2SA==", "dev": true, "requires": { - "@sinonjs/commons": "^1.3.0", - "array-from": "^2.1.1", - "lodash": "^4.17.15" + "@sinonjs/commons": "^1.6.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" } }, "@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", "dev": true }, "@socket.io/base64-arraybuffer": { @@ -8543,6 +8533,21 @@ "@types/node": "*" } }, + "@types/sinon": { + "version": "10.0.13", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.13.tgz", + "integrity": "sha512-UVjDqJblVNQYvVNUsj0PuYYw0ELRmgt1Nt5Vk0pT5f16ROGfcKJY8o1HVuMOJOpD727RrGB9EGvoaTQE5tgxZQ==", + "dev": true, + "requires": { + "@types/sinonjs__fake-timers": "*" + } + }, + "@types/sinonjs__fake-timers": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz", + "integrity": "sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA==", + "dev": true + }, "@types/ws": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", @@ -8697,12 +8702,6 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, - "array-from": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", - "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", - "dev": true - }, "asn1": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", @@ -10719,12 +10718,6 @@ } } }, - "lolex": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-4.2.0.tgz", - "integrity": "sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg==", - "dev": true - }, "lowercase-keys": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", @@ -11139,33 +11132,24 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, "nise": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.3.tgz", - "integrity": "sha512-Ymbac/94xeIrMf59REBPOv0thr+CJVFMhrlAkW/gjCIE58BGQdCj0x7KRCb3yz+Ga2Rz3E9XXSvUyyxqqhjQAQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.1.tgz", + "integrity": "sha512-yr5kW2THW1AkxVmCnKEh4nbYkJdB3I7LUkiUgOvEkOp414mc2UMaHMA7pjq1nYowhdoJZGwEKGaQVbxfpWj10A==", "dev": true, "requires": { - "@sinonjs/formatio": "^3.2.1", + "@sinonjs/commons": "^1.8.3", + "@sinonjs/fake-timers": ">=5", "@sinonjs/text-encoding": "^0.7.1", "just-extend": "^4.0.2", - "lolex": "^5.0.1", "path-to-regexp": "^1.7.0" }, "dependencies": { "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", "dev": true }, - "lolex": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", - "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, "path-to-regexp": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", @@ -11822,41 +11806,17 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "sinon": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.5.0.tgz", - "integrity": "sha512-AoD0oJWerp0/rY9czP/D6hDTTUYGpObhZjMpd7Cl/A6+j0xBE+ayL/ldfggkBXUs0IkvIiM1ljM8+WkOc5k78Q==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-14.0.0.tgz", + "integrity": "sha512-ugA6BFmE+WrJdh0owRZHToLd32Uw3Lxq6E6LtNRU+xTVBefx632h03Q7apXWRsRdZAJ41LB8aUfn2+O4jsDNMw==", "dev": true, "requires": { - "@sinonjs/commons": "^1.4.0", - "@sinonjs/formatio": "^3.2.1", - "@sinonjs/samsam": "^3.3.3", - "diff": "^3.5.0", - "lolex": "^4.2.0", - "nise": "^1.5.2", - "supports-color": "^5.5.0" - }, - "dependencies": { - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "@sinonjs/commons": "^1.8.3", + "@sinonjs/fake-timers": "^9.1.2", + "@sinonjs/samsam": "^6.1.1", + "diff": "^5.0.0", + "nise": "^5.1.1", + "supports-color": "^7.2.0" } }, "socket.io": { From 3acc97e50e6e7f9e7b773e38b5ad4a623a4b8cf4 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Wed, 3 Aug 2022 15:09:17 +0700 Subject: [PATCH 29/29] feat(web): timestamp simulation, testing --- .../browser/cases/recordedCoordSequences.js | 76 ++++++++---- .../src/inputSequenceSimulator.ts | 112 +++++++++++++----- 2 files changed, 138 insertions(+), 50 deletions(-) diff --git a/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js b/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js index d4d9354916..6b964ff419 100644 --- a/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js +++ b/common/web/gesture-recognizer/src/test/auto/browser/cases/recordedCoordSequences.js @@ -21,35 +21,68 @@ describe("Layer one - DOM -> InputSequence", function() { }); describe('recorded input sequences', function() { + beforeEach(function() { + this.clock = sinon.useFakeTimers(); + }); + + afterEach(function() { + this.clock.restore(); // restores the default implementations. + }); + // We rely on this function to have the same context as `it` - the test-definition function. let replayAndCompare = function(testObj) { + let resultPromise; + let playbackEngine = new Testing.InputSequenceSimulator(this.controller); - let result = playbackEngine.replay(testObj); + resultPromise = playbackEngine.replayAsync(testObj); - assert.equal(result.inputs.length, testObj.inputs.length); + // replayAsync sets up timeouts against the `clock` object. + // This will run through the simulated timeout queue asynchronously. + this.clock.runAllAsync(); - // Removes the timestamp element; we know that this component won't match, as the playback engine - // doesn't care about it. - let sampleCleaner = (sample) => { return {targetX: sample.targetX, targetY: sample.targetY} }; + // resultPromise resolves on the final timeout in the queue. + return resultPromise.then((result) => { + assert.equal(result.inputs.length, testObj.inputs.length); - // Returns just the observed, cleaned samples for a sequence object. The recorded coordinates - // should match perfectly. - let seqCleaner = (input) => { - return input.touchpoints[0].path.coords.map(sampleCleaner); - }; + // Removes the timestamp element; we know that this component may not match perfectly. + let sampleCleaner = (sample) => { return {targetX: sample.targetX, targetY: sample.targetY} }; - let cleanOriginalSet = testObj.inputs.map(seqCleaner); - let cleanResultSet = result .inputs.map(seqCleaner); + // Returns just the observed, cleaned samples for a sequence object. The recorded coordinates + // should match perfectly. + let seqCleaner = (input) => { + return input.touchpoints[0].path.coords.map(sampleCleaner); + }; - expect(cleanResultSet).to.deep.equal(cleanOriginalSet); + let cleanOriginalSet = testObj.inputs.map(seqCleaner); + let cleanResultSet = result .inputs.map(seqCleaner); - // The 'terminationEvent' property should match. Any sequence that was "canceled" should still - // cancel; that's a pretty critical detail! - let terminationEventMapper = (seq) => { - return seq.touchpoints[0].path.wasCancelled; - } + expect(cleanResultSet).to.deep.equal(cleanOriginalSet); - expect(result.inputs.map(terminationEventMapper)).to.deep.equal(testObj.inputs.map(terminationEventMapper)); + // Now to compare just the timestamp elements. We'll tolerate a difference of up to 1. + // Note: if using the `replaySync` function instead, disable this section! + // (Through to the nested for-loop `assert.closeTo`) + let sampleTimeExtractor = (sample) => sample.t; + let inputTimeExtractor = (input) => { + return input.touchpoints[0].path.coords.map(sampleTimeExtractor); + } + + let originalTimeSet = testObj.inputs.map(inputTimeExtractor); + let resultTimeSet = result .inputs.map(inputTimeExtractor); + + for(let i = 0; i < originalTimeSet.length; i++) { + for(let j = 0; j < originalTimeSet[i].length; j++) { + assert.closeTo(resultTimeSet[i][j], originalTimeSet[i][j], 1); + } + } + + // The 'terminationEvent' property should match. Any sequence that was "canceled" should still + // cancel; that's a pretty critical detail! + let terminationEventMapper = (seq) => { + return seq.touchpoints[0].path.wasCancelled; + } + + expect(result.inputs.map(terminationEventMapper)).to.deep.equal(testObj.inputs.map(terminationEventMapper)); + }); } // List all relevant fixtures in src/test/resources/json. @@ -70,8 +103,9 @@ describe("Layer one - DOM -> InputSequence", function() { it(`${recordingID}.json`, function() { let testObj = __json__[recordingID]; - // 'describe' has a notably different `this` reference than `it`, `before`, etc. - replayAndCompare.call(this, testObj); + // 'describe' has a notably different `this` reference than `it`, `before`, etc, + // hence the `.call` construction. + return replayAndCompare.call(this, testObj); }); } }); diff --git a/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts b/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts index 1fcbdaa50a..8f0a0e8294 100644 --- a/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts +++ b/common/web/gesture-recognizer/src/tools/unit-test-resources/src/inputSequenceSimulator.ts @@ -149,36 +149,16 @@ namespace Testing { * reproduces their corresponding events and runs them through this instance's linked * gesture-recognizer instance. * - * It will re-use the original fixture-layout configuration, **but** it will ignore - * all timestamp information, as that is nigh-impossible to perfectly reproduce without - * busy-waiting. + * It will re-use the original fixture-layout configuration. * @param sequenceTestSpec A previously-recorded sequence of sampled input coordinates. - * @returns A recording of the reproduced version of that sequence. + * @param replayExecutor A middleman function to use when simulating the actual events. + * @returns The final sample to be simulated. */ - replay(sequenceTestSpec: RecordedCoordSequenceSet): RecordedCoordSequenceSet { - // TEMP: ['set'] is for the old form of the recording spec. + private replayCore(sequenceTestSpec: RecordedCoordSequenceSet, + replayExecutor: (func: () => void, sample?: com.keyman.osk.InputSample) => void): com.keyman.osk.InputSample { let inputs = sequenceTestSpec.inputs; - if(inputs === undefined) { - inputs = sequenceTestSpec.inputs = sequenceTestSpec['set'].map((entry) => { - return { - touchpoints: [entry] - }; - }); - } - - // TEMP: while structures are in flux, a migration round - for(let entry of inputs) { - if(entry['sequence']) { - entry.touchpoints = [ entry['sequence'] ]; - } - } - const config = sequenceTestSpec.config; - // We're going to record the test spec as we replay it, eventually returning the - // final result. - const recorder = new Testing.SequenceRecorder(this.controller); - this.controller.layoutConfiguration = new FixtureLayoutConfiguration(config); /** @@ -189,6 +169,8 @@ namespace Testing { let sequenceProgress: number[] = new Array(inputs.length).fill(0); let sequenceTouches: Touch[] = new Array(inputs.length).fill(null); + let lastSample = null; + while(sequenceProgress.find((number) => number != Number.MAX_VALUE) !== undefined) { // Determine the sequence that has the chronologically next-in-line sample // to reproduce. @@ -201,6 +183,7 @@ namespace Testing { let selectedSequence = -1; for(let index=0; index < inputs.length; index++) { + // TODO: does not iterate over all touchpoints. Not that we can have more than one at present... const touchpoint = new com.keyman.osk.TrackedPoint(index, inputs[index].touchpoints[0]); const indexInSequence = sequenceProgress[index]; @@ -237,19 +220,51 @@ namespace Testing { // Now that we've removed the entry for the current touchpoint, filter out any null entries. otherTouches = otherTouches.filter((val) => !!val); - sequenceTouches[selectedSequence] = this.replayTouchSample(sample, state, selectedSequence, otherTouches); + replayExecutor(() => { + sequenceTouches[selectedSequence] = this.replayTouchSample(sample, state, selectedSequence, otherTouches); + }, sample); if(appendEndEvent) { - this.replayTouchSample(sample, "end", selectedSequence, otherTouches); + // Synchronous in sync mode, but set with a timeout in async mode. + replayExecutor(() => { + this.replayTouchSample(sample, "end", selectedSequence, otherTouches); + }, sample); sequenceTouches[selectedSequence] = null; } } else { - this.replayMouseSample(sample, state); + replayExecutor(() => { + this.replayMouseSample(sample, state); + }, sample); if(appendEndEvent) { - this.replayMouseSample(sample, "end"); + replayExecutor(() => { + this.replayMouseSample(sample, "end"); + }, sample); } } + + lastSample = sample; } + return lastSample; + } + + /** + * Given a previously-recorded sequence of sampled input coordinates, this function + * reproduces their corresponding events and runs them through this instance's linked + * gesture-recognizer instance. The original timestamps are ignored; only the relative + * order of events will be enforced. + * + * It will re-use the original fixture-layout configuration. + * @param sequenceTestSpec A previously-recorded sequence of sampled input coordinates. + * @returns A recording of the reproduced version of that sequence. + */ + replaySync(sequenceTestSpec: RecordedCoordSequenceSet): RecordedCoordSequenceSet { + // We're going to record the test spec as we replay it, eventually returning the + // final result. + const recorder = new Testing.SequenceRecorder(this.controller); + + // We're operating synchronously, so just simulate the events on the spot. + this.replayCore(sequenceTestSpec, (func) => { func() }); + // We technically don't have access to 'samples' for the actual recorded object. // Obtaining a 'deep copy' (though methodless) works around this nicely and // matches the original `sequenceTestSpec`'s format to boot. @@ -257,5 +272,44 @@ namespace Testing { recorder.clear(); return recording; } + + + /** + * Given a previously-recorded sequence of sampled input coordinates, this function + * reproduces their corresponding events and runs them through this instance's linked + * gesture-recognizer instance. The original timestamps will be used; if a timer-stubbing + * package (like SinonJS) is used, they should be reproduced to ~1ms precision. + * + * (Experimentally, the actual value tends to be Math.floor() - not Math.round().) + * + * It will re-use the original fixture-layout configuration. + * @param sequenceTestSpec A previously-recorded sequence of sampled input coordinates. + * @returns A Promise for a recording of the reproduced version of that sequence. + */ + replayAsync(sequenceTestSpec: RecordedCoordSequenceSet): Promise { + // We're going to record the test spec as we replay it, eventually returning the + // final result. + const recorder = new Testing.SequenceRecorder(this.controller); + + // We're operating asynchronously, so we wrap each simulated event in a timeout. + // NOTE: this will be much less precise without stubbed timeout systems like the one + // included within SinonJS. + const finalSample = this.replayCore(sequenceTestSpec, (func, sample) => { + window.setTimeout(() => { + func(); + }, sample.t); + }); + + return new Promise((resolve) => { + window.setTimeout(() => { + // We technically don't have access to 'samples' for the actual recorded object. + // Obtaining a 'deep copy' (though methodless) works around this nicely and + // matches the original `sequenceTestSpec`'s format to boot. + let recording = JSON.parse(recorder.recordingsToJSON()) as RecordedCoordSequenceSet; + recorder.clear(); + resolve(recording); + }, finalSample.t + 30); + }); + } } } \ No newline at end of file