diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b72195b5..302f2a10 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 10a99ce1..24ce9736 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 54380ebd..00000000 --- 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 bfe16a54..d11faa99 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,21 +46,13 @@ "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", "pofile": "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 a7012271..34ab038f 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 87a6aa51..9adbd457 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 692cc23b..936db2a1 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 15323eb2..c50216c8 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 b565b907..18170941 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 5844ce17..3c6bc5f9 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 d2e27ed2..798eab19 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 2c8720c7..17fc568e 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 25e412ba..6f70b052 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 11a02f2f..8553beba 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 378ebd58..739dde89 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 063a5f2c..f0f784e3 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 ccc01247..bbd4bca9 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 () { @@ -622,7 +626,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 a1cb4547..9678f2d3 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 e40813ca..cbafe6f9 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 2a7bbeaa..4b641cdb 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 b47fbcaf..ab48435a 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 ebdfd969..d3747199 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 29c8caa4..ce669211 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 eb724095..8a48f6a7 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 62bcbfa5..3437bcb9 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 9151a060..4ed087cb 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 15729102..960f719d 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 00000000..8c312d67 --- /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