diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b72195b52..302f2a104 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,13 +10,13 @@ jobs: - ubuntu-latest - windows-latest browser: - - ChromeHeadless - - FirefoxHeadless + - chrome + - firefox include: - os: macos-latest - browser: Safari + browser: safari - os: windows-latest - browser: EdgeHeadless + browser: edge fail-fast: false runs-on: ${{ matrix.os }} steps: diff --git a/eslint.config.mjs b/eslint.config.mjs index 13b1a32a4..f6050b14b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -78,8 +78,6 @@ export default [ globals: { ...globals.node, ...globals.mocha, - sinon: false, - expect: false, } }, rules: { diff --git a/karma.conf.js b/karma.conf.js deleted file mode 100644 index 54380ebd2..000000000 --- a/karma.conf.js +++ /dev/null @@ -1,92 +0,0 @@ -// Karma configuration - -// The Safari launcher is broken, so construct our own -function SafariBrowser(id, baseBrowserDecorator, args) { - baseBrowserDecorator(this); - - this._start = function(url) { - this._execCommand('/usr/bin/open', ['-W', '-n', '-a', 'Safari', url]); - } -} - -SafariBrowser.prototype = { - name: 'Safari' -} - -module.exports = (config) => { - let browsers = []; - - if (process.env.TEST_BROWSER_NAME) { - browsers = process.env.TEST_BROWSER_NAME.split(','); - } - - const my_conf = { - - // base path that will be used to resolve all patterns (eg. files, exclude) - basePath: '', - - // frameworks to use - // available frameworks: https://npmjs.org/browse/keyword/karma-adapter - frameworks: ['mocha'], - - // list of files / patterns to load in the browser - files: [ - // node modules - { pattern: 'node_modules/chai/**', included: false }, - { pattern: 'node_modules/sinon/**', included: false }, - { pattern: 'node_modules/sinon-chai/**', included: false }, - // modules to test - { pattern: 'app/localization.js', included: false, type: 'module' }, - { pattern: 'app/webutil.js', included: false, type: 'module' }, - { pattern: 'core/**/*.js', included: false, type: 'module' }, - { pattern: 'vendor/pako/**/*.js', included: false, type: 'module' }, - // tests - { pattern: 'tests/test.*.js', type: 'module' }, - // test support files - { pattern: 'tests/fake.*.js', included: false, type: 'module' }, - { pattern: 'tests/assertions.js', type: 'module' }, - ], - - client: { - mocha: { - // replace Karma debug page with mocha display - 'reporter': 'html', - 'ui': 'bdd' - } - }, - - // list of files to exclude - exclude: [ - ], - - plugins: [ - 'karma-*', - '@chiragrupani/karma-chromium-edge-launcher', - { 'launcher:Safari': [ 'type', SafariBrowser ] }, - ], - - // start these browsers - // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher - browsers: browsers, - - // test results reporter to use - // possible values: 'dots', 'progress' - // available reporters: https://npmjs.org/browse/keyword/karma-reporter - reporters: ['mocha'], - - - // level of logging - // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG - logLevel: config.LOG_INFO, - - - // enable / disable watching file and executing tests whenever any file changes - autoWatch: false, - - // Continuous Integration mode - // if true, Karma captures browsers, runs the tests and exits - singleRun: true, - }; - - config.set(my_conf); -}; diff --git a/package.json b/package.json index e28850a8d..111155afa 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ ], "scripts": { "lint": "eslint app core po/po2js po/xgettext-html tests utils", - "test": "karma start karma.conf.js", + "test": "web-test-runner", "prepublish": "node ./utils/convert.js --clean" }, "repository": { @@ -46,22 +46,14 @@ "fs-extra": "latest", "globals": "latest", "jsdom": "latest", - "karma": "latest", - "karma-mocha": "latest", - "karma-chrome-launcher": "latest", - "@chiragrupani/karma-chromium-edge-launcher": "latest", - "karma-firefox-launcher": "latest", - "karma-ie-launcher": "latest", - "karma-mocha-reporter": "latest", - "karma-safari-launcher": "latest", - "karma-script-launcher": "latest", "mocha": "latest", "node-getopt": "latest", "po2json": "latest", "sinon": "latest", - "sinon-chai": "latest" + "sinon-chai": "latest", + "@web/test-runner": "latest", + "@web/test-runner-webdriver": "latest" }, - "dependencies": {}, "keywords": [ "vnc", "rfb", diff --git a/tests/assertions.js b/tests/assertions.js index a70122717..34ab038f0 100644 --- a/tests/assertions.js +++ b/tests/assertions.js @@ -1,10 +1,6 @@ -import * as chai from '../node_modules/chai/chai.js'; -import sinon from '../node_modules/sinon/pkg/sinon-esm.js'; -import sinonChai from '../node_modules/sinon-chai/lib/sinon-chai.js'; +import * as chai from 'chai'; +import sinonChai from 'sinon-chai'; -window.expect = chai.expect; - -window.sinon = sinon; chai.use(sinonChai); // noVNC specific assertions diff --git a/tests/test.base64.js b/tests/test.base64.js index e5644dcdb..b29a80d86 100644 --- a/tests/test.base64.js +++ b/tests/test.base64.js @@ -1,3 +1,5 @@ +import { expect } from 'chai'; + import Base64 from '../core/base64.js'; describe('Base64 Tools', function () { diff --git a/tests/test.browser.js b/tests/test.browser.js index 692cc23b2..936db2a1a 100644 --- a/tests/test.browser.js +++ b/tests/test.browser.js @@ -1,3 +1,5 @@ +import { expect } from 'chai'; + import { isMac, isWindows, isIOS, isAndroid, isChromeOS, isSafari, isFirefox, isChrome, isChromium, isOpera, isEdge, isGecko, isWebKit, isBlink } from '../core/util/browser.js'; diff --git a/tests/test.copyrect.js b/tests/test.copyrect.js index 60c395287..81e425c62 100644 --- a/tests/test.copyrect.js +++ b/tests/test.copyrect.js @@ -1,3 +1,6 @@ +import './assertions.js'; +import { expect } from 'chai'; + import Websock from '../core/websock.js'; import Display from '../core/display.js'; diff --git a/tests/test.deflator.js b/tests/test.deflator.js index b565b9075..181709414 100644 --- a/tests/test.deflator.js +++ b/tests/test.deflator.js @@ -1,3 +1,6 @@ +import './assertions.js'; +import { expect } from 'chai'; + import { inflateInit, inflate } from "../vendor/pako/lib/zlib/inflate.js"; import ZStream from "../vendor/pako/lib/zlib/zstream.js"; import Deflator from "../core/deflator.js"; diff --git a/tests/test.display.js b/tests/test.display.js index d2c51793b..37990e8ee 100644 --- a/tests/test.display.js +++ b/tests/test.display.js @@ -1,3 +1,7 @@ +import './assertions.js'; +import { expect } from 'chai'; +import sinon from 'sinon'; + import Base64 from '../core/base64.js'; import Display from '../core/display.js'; diff --git a/tests/test.gesturehandler.js b/tests/test.gesturehandler.js index d2e27ed2a..798eab19e 100644 --- a/tests/test.gesturehandler.js +++ b/tests/test.gesturehandler.js @@ -1,3 +1,7 @@ +import './assertions.js'; +import { expect } from 'chai'; +import sinon from 'sinon'; + import EventTargetMixin from '../core/util/eventtarget.js'; import GestureHandler from '../core/input/gesturehandler.js'; diff --git a/tests/test.helper.js b/tests/test.helper.js index 2c8720c77..17fc568e2 100644 --- a/tests/test.helper.js +++ b/tests/test.helper.js @@ -1,3 +1,5 @@ +import { expect } from 'chai'; + import keysyms from '../core/input/keysymdef.js'; import * as KeyboardUtil from "../core/input/util.js"; diff --git a/tests/test.hextile.js b/tests/test.hextile.js index f788fd4dc..70607bef4 100644 --- a/tests/test.hextile.js +++ b/tests/test.hextile.js @@ -1,3 +1,6 @@ +import './assertions.js'; +import { expect } from 'chai'; + import Websock from '../core/websock.js'; import Display from '../core/display.js'; diff --git a/tests/test.inflator.js b/tests/test.inflator.js index 11a02f2f4..8553bebaf 100644 --- a/tests/test.inflator.js +++ b/tests/test.inflator.js @@ -1,3 +1,6 @@ +import './assertions.js'; +import { expect } from 'chai'; + import { deflateInit, deflate, Z_FULL_FLUSH } from "../vendor/pako/lib/zlib/deflate.js"; import ZStream from "../vendor/pako/lib/zlib/zstream.js"; import Inflator from "../core/inflator.js"; diff --git a/tests/test.int.js b/tests/test.int.js index 378ebd589..739dde895 100644 --- a/tests/test.int.js +++ b/tests/test.int.js @@ -1,3 +1,5 @@ +import { expect } from 'chai'; + import { toUnsigned32bit, toSigned32bit } from '../core/util/int.js'; describe('Integer casting', function () { diff --git a/tests/test.jpeg.js b/tests/test.jpeg.js index 5cc153f90..692de4488 100644 --- a/tests/test.jpeg.js +++ b/tests/test.jpeg.js @@ -1,3 +1,6 @@ +import './assertions.js'; +import { expect } from 'chai'; + import Websock from '../core/websock.js'; import Display from '../core/display.js'; diff --git a/tests/test.keyboard.js b/tests/test.keyboard.js index 11c8b6eb7..0fdacd0c8 100644 --- a/tests/test.keyboard.js +++ b/tests/test.keyboard.js @@ -1,3 +1,7 @@ +import './assertions.js'; +import { expect } from 'chai'; +import sinon from 'sinon'; + import Keyboard from '../core/input/keyboard.js'; describe('Key Event Handling', function () { @@ -591,7 +595,7 @@ describe('Key Event Handling', function () { expect(kbd.onkeyevent).to.have.been.calledWith(0xffe1, 'ShiftLeft', true); kbd.onkeyevent.resetHistory(); - kbd._handleKeyDown(keyevent('keydown', {code: 'ShiftRight', key: 'Shift', location: 2})); + kbd._handleKeyDown(keyevent('keydown', {code: 'ShiftRight', key: 'ShiftFoo', location: 2})); expect(kbd.onkeyevent).to.have.been.calledOnce; expect(kbd.onkeyevent).to.have.been.calledWith(0xffe2, 'ShiftRight', true); kbd.onkeyevent.resetHistory(); diff --git a/tests/test.localization.js b/tests/test.localization.js index a1cb45474..9678f2d34 100644 --- a/tests/test.localization.js +++ b/tests/test.localization.js @@ -1,3 +1,7 @@ +import './assertions.js'; +import { expect } from 'chai'; +import sinon from 'sinon'; + import _, { Localizer, l10n } from '../app/localization.js'; describe('Localization', function () { diff --git a/tests/test.raw.js b/tests/test.raw.js index 19b2377f7..aff9384f1 100644 --- a/tests/test.raw.js +++ b/tests/test.raw.js @@ -1,3 +1,6 @@ +import './assertions.js'; +import { expect } from 'chai'; + import Websock from '../core/websock.js'; import Display from '../core/display.js'; diff --git a/tests/test.rfb.js b/tests/test.rfb.js index 2be3bfbfc..71129dcd4 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -1,8 +1,13 @@ +import './assertions.js'; +import { expect } from 'chai'; +import sinon from 'sinon'; + import RFB from '../core/rfb.js'; import Websock from '../core/websock.js'; import ZStream from "../vendor/pako/lib/zlib/zstream.js"; import { deflateInit, deflate, Z_DEFAULT_COMPRESSION } from "../vendor/pako/lib/zlib/deflate.js"; import { encodings } from '../core/encodings.js'; +import { initLogging } from '../core/util/logging.js'; import { toUnsigned32bit } from '../core/util/int.js'; import { encodeUTF8 } from '../core/util/strings.js'; import KeyTable from '../core/input/keysym.js'; @@ -111,6 +116,9 @@ describe('Remote Frame Buffer Protocol Client', function () { // Avoiding printing the entire Websock buffer on errors Websock.prototype.inspect = function () { return "[object Websock]"; }; + + // Avoid spamming test output + initLogging('none'); }); after(function () { diff --git a/tests/test.rre.js b/tests/test.rre.js index 7b5f73d0e..d16a26e1e 100644 --- a/tests/test.rre.js +++ b/tests/test.rre.js @@ -1,3 +1,6 @@ +import './assertions.js'; +import { expect } from 'chai'; + import Websock from '../core/websock.js'; import Display from '../core/display.js'; diff --git a/tests/test.tight.js b/tests/test.tight.js index 3d6b555da..2162dd0f6 100644 --- a/tests/test.tight.js +++ b/tests/test.tight.js @@ -1,3 +1,6 @@ +import './assertions.js'; +import { expect } from 'chai'; + import Websock from '../core/websock.js'; import Display from '../core/display.js'; diff --git a/tests/test.tightpng.js b/tests/test.tightpng.js index e7edc8fa6..cac586d62 100644 --- a/tests/test.tightpng.js +++ b/tests/test.tightpng.js @@ -1,3 +1,6 @@ +import './assertions.js'; +import { expect } from 'chai'; + import Websock from '../core/websock.js'; import Display from '../core/display.js'; diff --git a/tests/test.util.js b/tests/test.util.js index eb7240951..8a48f6a71 100644 --- a/tests/test.util.js +++ b/tests/test.util.js @@ -1,4 +1,8 @@ /* eslint-disable no-console */ +import './assertions.js'; +import { expect } from 'chai'; +import sinon from 'sinon'; + import * as Log from '../core/util/logging.js'; import { encodeUTF8, decodeUTF8 } from '../core/util/strings.js'; @@ -7,11 +11,11 @@ describe('Utils', function () { describe('logging functions', function () { beforeEach(function () { - sinon.spy(console, 'log'); - sinon.spy(console, 'debug'); - sinon.spy(console, 'warn'); - sinon.spy(console, 'error'); - sinon.spy(console, 'info'); + sinon.stub(console, 'log'); + sinon.stub(console, 'debug'); + sinon.stub(console, 'warn'); + sinon.stub(console, 'error'); + sinon.stub(console, 'info'); }); afterEach(function () { diff --git a/tests/test.websock.js b/tests/test.websock.js index 53145b360..f13404902 100644 --- a/tests/test.websock.js +++ b/tests/test.websock.js @@ -1,3 +1,7 @@ +import './assertions.js'; +import { expect } from 'chai'; +import sinon from 'sinon'; + import Websock from '../core/websock.js'; import FakeWebSocket from './fake.websocket.js'; diff --git a/tests/test.webutil.js b/tests/test.webutil.js index 9151a0603..4ed087cb6 100644 --- a/tests/test.webutil.js +++ b/tests/test.webutil.js @@ -1,6 +1,11 @@ /* jshint expr: true */ +import './assertions.js'; +import { expect } from 'chai'; +import sinon from 'sinon'; + import * as WebUtil from '../app/webutil.js'; +import { initLogging } from '../core/util/logging.js'; describe('WebUtil', function () { "use strict"; @@ -63,6 +68,9 @@ describe('WebUtil', function () { before(function () { chrome = window.chrome; window.chrome = null; + + // Avoid spamming test output + initLogging('none'); }); after(function () { window.chrome = chrome; diff --git a/tests/test.zrle.js b/tests/test.zrle.js index f7c6089d5..a2203b101 100644 --- a/tests/test.zrle.js +++ b/tests/test.zrle.js @@ -1,3 +1,6 @@ +import './assertions.js'; +import { expect } from 'chai'; + import Websock from '../core/websock.js'; import Display from '../core/display.js'; diff --git a/web-test-runner.config.mjs b/web-test-runner.config.mjs new file mode 100644 index 000000000..8c312d67d --- /dev/null +++ b/web-test-runner.config.mjs @@ -0,0 +1,78 @@ +import { defaultReporter } from '@web/test-runner'; +import { summaryReporter } from '@web/test-runner'; +import { webdriverLauncher } from '@web/test-runner-webdriver'; + +let browsers; +let launchers; + +if (process.env.TEST_BROWSER_NAME) { + browsers = process.env.TEST_BROWSER_NAME.split(','); +} else { + browsers = ['chrome', 'firefox']; + if (process.platform === 'win32') { + browsers.push('edge'); + } + if (process.platform === 'darwin') { + browsers.push('safari'); + } +} + +launchers = []; + +for (let browser of browsers) { + switch (browser) { + case 'chrome': + launchers.push(webdriverLauncher({ + capabilities: { + browserName: 'chrome', + 'goog:chromeOptions': { + args: ['headless', 'disable-gpu'] + }, + }, + })); + break; + case 'firefox': + launchers.push(webdriverLauncher({ + capabilities: { + browserName: 'firefox', + 'moz:firefoxOptions': { + args: ['-headless'] + } + }, + })); + break; + case 'edge': + launchers.push(webdriverLauncher({ + capabilities: { + browserName: 'edge', + 'ms:edgeOptions': { + args: ['--headless'] + } + }, + })); + break; + case 'safari': + launchers.push(webdriverLauncher({ + capabilities: { + browserName: 'safari', + }, + })); + break; + default: + throw new Error('Unknown browser: ' + browser); + } +} + +export default { + nodeResolve: true, + files: [ + 'tests/test.*.js', + ], + browsers: launchers, + reporters: [ + defaultReporter(), + summaryReporter(), + ], + // We have small test files, so let's kill hangs quickly + testsFinishTimeout: 10000, +}; \ No newline at end of file