diff --git a/web/source/kmwdevice.ts b/web/source/kmwdevice.ts index 209587c65e..fce6196117 100644 --- a/web/source/kmwdevice.ts +++ b/web/source/kmwdevice.ts @@ -77,7 +77,10 @@ namespace com.keyman { // Starting with 13.1, "Macintosh" can reflect iPads (by default) or iPhones // (by user setting); a new "Request Desktop Website" setting for Safari will // change the user agent string to match a desktop Mac. - let regex = /Intel Mac OS X (10(?:_\d+)+)/i; + // + // Firefox uses '.' between version components, while Chrome and Safari use + // '_' instead. So, we have to check for both. Yay. + let regex = /Intel Mac OS X (10(?:[_\.]\d+)+)/i; let results = regex.exec(agent); // Match result: a version string with components separated by underscores. diff --git a/web/unit_tests/CI.conf.js b/web/unit_tests/CI.conf.js index a5e5fb803b..386142fd0e 100644 --- a/web/unit_tests/CI.conf.js +++ b/web/unit_tests/CI.conf.js @@ -43,14 +43,12 @@ module.exports = function(config) { * Definition of browser sets possibly relevant for testing. */ var CURRENT_MAC_LAUNCHERS = { - // Disabled due to a long period BrowserStack failing to actually run the tests on - // the browser/platform combo. - // bs_firefox_mac: { - // browser: 'firefox', - // browser_version: '62', - // os: 'OS X', - // os_version: 'Mojave' - // }, + bs_firefox_mac: { + browser: 'firefox', + browser_version: '62', + os: 'OS X', + os_version: 'Mojave' + }, bs_safari_mac_m: { browser: 'safari', browser_version: '13', @@ -162,7 +160,7 @@ module.exports = function(config) { // level of logging // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG - logLevel: config.LOG_INFO, + logLevel: config.LOG_DEBUG, // Concurrency level // For CI, it really helps to keep a nice, clean set of output logs. diff --git a/web/unit_tests/base.conf.js b/web/unit_tests/base.conf.js index 17070b9f68..408d5064fb 100644 --- a/web/unit_tests/base.conf.js +++ b/web/unit_tests/base.conf.js @@ -7,6 +7,9 @@ module.exports = { // base path that will be used to resolve all patterns (eg. files, exclude) basePath: '..', + // Safeguards against disconnecting after starting tests. + browserNoActivityTimeout: 60000, + client: { /* `client.args` here is passed to the test runner page as `__karma__.config.args`. * @@ -37,6 +40,7 @@ module.exports = { 'unit_tests/recorder_InputEvents.js', // The object definitions used to generate/replicate key events for engine tests. // Includes KMW's Device class, which is used by test_utils below. 'unit_tests/dev_resources.js', // Defines com.keyman.dom objects separate from KMW for unit testing. + 'unit_tests/test_init_check.js', // Ensures that tests will initialize properly 'unit_tests/test_utils.js', // A basic utility script useful for constructing tests 'unit_tests/cases/**/*.js', // Where the tests actually reside. 'unit_tests/json/**/*.json', // Where pre-loaded JSON resides. diff --git a/web/unit_tests/cases/attachmentAPI.js b/web/unit_tests/cases/attachmentAPI.js index a61b40e690..087cdf9265 100644 --- a/web/unit_tests/cases/attachmentAPI.js +++ b/web/unit_tests/cases/attachmentAPI.js @@ -4,6 +4,7 @@ describe('Attachment API', function() { this.timeout(kmwconfig.timeouts.standard); before(function(done) { + assert.isFalse(com.keyman.karma.DEVICE_DETECT_FAILURE, "Cannot run due to device detection failure."); fixture.setBase('unit_tests/fixtures'); this.timeout(kmwconfig.timeouts.scriptLoad * 3); diff --git a/web/unit_tests/cases/basics.js b/web/unit_tests/cases/basics.js index 7987a68351..269f2c9b20 100644 --- a/web/unit_tests/cases/basics.js +++ b/web/unit_tests/cases/basics.js @@ -3,6 +3,11 @@ var assert = chai.assert; describe('Basic KeymanWeb', function() { this.timeout(kmwconfig.timeouts.standard); + before(function() { + // These tests require use of KMW's device-detection functionality. + assert.isFalse(com.keyman.karma.DEVICE_DETECT_FAILURE, "Cannot run due to device detection failure."); + }) + beforeEach(function(done) { this.timeout(kmwconfig.timeouts.scriptLoad); diff --git a/web/unit_tests/cases/element_interfaces.js b/web/unit_tests/cases/element_interfaces.js index e1ed36b69c..3a2321126e 100644 --- a/web/unit_tests/cases/element_interfaces.js +++ b/web/unit_tests/cases/element_interfaces.js @@ -1177,6 +1177,11 @@ describe('Element Input/Output Interfacing', function() { * TODO: Design and implement some 'complex', cross-Node selection tests. */ describe('Wrapper: Content-Editable Elements (using DIVs)', function() { + before(function() { + // These tests require use of KMW's device-detection functionality. + assert.isFalse(com.keyman.karma.DEVICE_DETECT_FAILURE, "Cannot run due to device detection failure."); + }) + describe('Caret Handling', function() { describe('hasSelection', function() { it('correctly recognizes Selection ownership', function () { @@ -1264,6 +1269,11 @@ describe('Element Input/Output Interfacing', function() { // We'll need a larger timeout. this.timeout(kmwconfig.timeouts.scriptLoad); + before(function() { + // These tests require use of KMW's device-detection functionality. + assert.isFalse(com.keyman.karma.DEVICE_DETECT_FAILURE, "Cannot run due to device detection failure."); + }) + beforeEach(function(done) { // Per-test creation of reg. pair and dummy elements, since IFrames are async. // Relies on the main-level's renewal of the overall fixture to be processed first. diff --git a/web/unit_tests/cases/touch_aliases.js b/web/unit_tests/cases/touch_aliases.js index 1af6139f32..a559a21394 100644 --- a/web/unit_tests/cases/touch_aliases.js +++ b/web/unit_tests/cases/touch_aliases.js @@ -93,6 +93,11 @@ describe('TouchAliasElement', function() { }); describe("With ['base']", function() { + before(function() { + // These tests require use of KMW's device-detection functionality. + assert.isFalse(com.keyman.karma.DEVICE_DETECT_FAILURE, "Cannot run due to device detection failure."); + }) + it('correctly aliases upon construction', function(done) { var input = document.getElementById(DynamicElements.addInput()); input.value = "apples"; diff --git a/web/unit_tests/test.sh b/web/unit_tests/test.sh index 3a6ab9a1c5..133dccf8b6 100755 --- a/web/unit_tests/test.sh +++ b/web/unit_tests/test.sh @@ -4,7 +4,7 @@ # It's rigged to be callable by NPM to facilitate testing during development when in other folders. display_usage ( ) { - echo "test.sh [-CI | -debug | -? | -h | -help] [-reporter ]" + echo "test.sh [-CI | -debug | -? | -h | -help] | [-log-level ] | [-reporter ]" echo echo " -CI to run unit tests in CI mode on BrowserStack." echo " This script requires your credentials to be set in environment variables - see " @@ -13,6 +13,9 @@ display_usage ( ) { echo " -debug to establish a Karma server that facilitates unit test debugging" echo " Not compatible with -CI." echo "" + echo " -log-level to set the logging level used by Karma." + echo " Valid options: 'debug', 'info', 'warn', 'error', 'disable'" + echo "" echo " -reporter sets the test engine to utilize the specified ." echo " Valid options: BrowserStack, teamcity, dots, progress, mocha" echo "" @@ -65,6 +68,10 @@ while [[ $# -gt 0 ]] ; do -CI) CONFIG=CI.conf.js ;; + -log-level) + shift + FLAGS="--log-level=$1 $FLAGS" + ;; -debug) # Disables the default 'run once, then done' configuration needed for CI. DEBUG=true @@ -103,7 +110,7 @@ cd $BASE_PATH/../source ./build_dev_resources.sh npm --no-color run modernizr -- -c unit_tests/modernizr.config.json -d unit_tests/modernizr.js -npm --no-color run karma -- start --log-level=debug $FLAGS $BROWSERS unit_tests/$CONFIG +npm --no-color run karma -- start $FLAGS $BROWSERS unit_tests/$CONFIG CODE=$? diff --git a/web/unit_tests/test_init_check.js b/web/unit_tests/test_init_check.js new file mode 100644 index 0000000000..6b1619a950 --- /dev/null +++ b/web/unit_tests/test_init_check.js @@ -0,0 +1,28 @@ +// A simple "does device detection work properly" unit test. +// We'll let things break after this reports, since this will likely signal a LOT of other failures. +var assert = chai.assert; + +/* Note - we still have to prevent errors setting up test resources; + * Karma will fail to report errors for affected browsers otherwise. + * + * This simply gives us a test report with the error that can be shown + * if we disable other tests when this fails. + */ +describe('Test Initialization', function() { + // We can't use kmwconfig timeouts yet (they rely on test success), so we simply give + // this should-be-simple test a nice, large block of time to run. + this.timeout(15000); + + it("Detects device without JS errors", function() { + var device; + try { + device = new com.keyman.Device(); + device.detect(); + + console.log("Detected platform: " + device.browser + " on " + device.OS + " with form factor " + device.formFactor); + } catch (err) { + console.error("Error during device detection: " + err.toString()); + assert.fail(err); + } + }); +}); \ No newline at end of file diff --git a/web/unit_tests/test_utils.js b/web/unit_tests/test_utils.js index bdb50d0267..6830727aea 100644 --- a/web/unit_tests/test_utils.js +++ b/web/unit_tests/test_utils.js @@ -1,9 +1,23 @@ // KeymanWeb test suite - processing of the Karma configuration's client.args parameter. +com = com || {}; +com.keyman = com.keyman || {}; +com.keyman.karma = com.keyman.karma || {}; +com.keyman.karma.DEVICE_DETECT_FAILURE = false; + (function() { - var device = new com.keyman.Device(); - device.detect(); - var mobile = (device.formFactor != 'desktop'); + // Default value in case of device-detection failure. + var mobile = false; + + try { + var device = new com.keyman.Device(); + device.detect(); + + mobile = (device.formFactor != 'desktop'); + } catch (err) { + // Sets a warning flag that unit-test files can use to disable themselves. + com.keyman.karma.DEVICE_DETECT_FAILURE = true; + } var kmwconfig = window['kmwconfig'] = {mobile: mobile};